Last Comment Bug 481815 - Provide a Windows service to update applications without asking Administrator password
: Provide a Windows service to update applications without asking Administrator...
Status: RESOLVED FIXED
dev-doc-needed for nsIWindowsRegKey.idl
: dev-doc-complete
Product: Toolkit
Classification: Components
Component: Application Update (show other bugs)
: unspecified
: x86 Windows Vista
: P1 enhancement with 13 votes (vote)
: mozilla12
Assigned To: Brian R. Bondy [:bbondy]
:
Mentors:
https://wiki.mozilla.org/Windows_Serv...
: 615141 (view as bug list)
Depends on: 716477 716492 509158 663055 696777 698837 699700 701069 704578 704591 706573 706716 707020 707333 707666 708123 708153 708697 708854 709183 709598 711140 711660 711682 711692 711726 711834 715465 715489 715746 715749 715876 715910 716126 716237 716473 716480 716915 719947 720016 720230 721182 725876 733108 745426 750731 753775 772841 775458 CVE-2013-1706
Blocks: 710436 711044 711393 715803 bgupdates 561146 627591 697543 707037 711054 711475 711505 711792 758326
  Show dependency treegraph
 
Reported: 2009-03-05 22:53 PST by René
Modified: 2014-09-04 10:38 PDT (History)
81 users (show)
dveditz: sec‑review+
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---
fixed


Attachments
Intermediate work in progress patch (69.58 KB, patch)
2011-09-19 20:02 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 1 of 6: Base Windows service code (works but still in progress) (66.50 KB, patch)
2011-09-27 20:18 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: feedback+
Details | Diff | Review
Patch 2 of 6: Service main logic for work items to run updater.exe (works but still in progress) (14.27 KB, patch)
2011-09-27 20:20 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 3 of 6: /toolkit/xre code launching service work items (works but still in progress) (9.00 KB, patch)
2011-09-27 20:21 PDT, Brian R. Bondy [:bbondy]
bugspam.Callek: feedback-
Details | Diff | Review
Patch 4 of 6: Proposed removing UAC elevation prompt from within js (works but still in progress) (5.72 KB, patch)
2011-09-27 20:22 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 5 of 6: Build procss (4.00 KB, patch)
2011-09-27 20:29 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 6 of 6: Service installer patch (works but still in progress, first time updates work todo) (2.10 KB, patch)
2011-09-27 20:33 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Intermediate backup of service related work (127.27 KB, patch)
2011-10-13 10:31 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Ideas for optional install process (178.62 KB, image/png)
2011-10-13 15:49 PDT, Brian R. Bondy [:bbondy]
no flags Details
A proposed new option for whether or not the product should use the silent update service. Only shows up if service is installed. (64.77 KB, image/png)
2011-10-14 17:52 PDT, Brian R. Bondy [:bbondy]
faaborg: ui‑review-
Details
Patch for service certificate check on updater.exe and related logic (29.32 KB, patch)
2011-10-14 18:59 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Screenshot: New installer step inside Firefox installer (36.23 KB, image/png)
2011-10-17 20:59 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: feedback+
Details
Screenshot: Program files directory showing output files of Maintenance Service Installer (75.46 KB, image/png)
2011-10-17 21:00 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: feedback+
Details
Screenshot: Showing up separately inside Add/Remve programs (icon pending) (86.35 KB, image/png)
2011-10-17 21:01 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: feedback+
Details
New installer process pretty much completed minus some code cleanup (30.95 KB, patch)
2011-10-17 21:20 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
New installer process pretty much completed minus some code cleanup. v2 (32.39 KB, patch)
2011-10-18 06:04 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
A proposed new option for whether or not the product should use the silent update service. Only shows up if service is installed. v2 (50.19 KB, image/png)
2011-10-18 07:18 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: feedback-
Details
Intermediate backup of service related work (241.77 KB, patch)
2011-10-19 13:47 PDT, Brian R. Bondy [:bbondy]
ian.melven: feedback+
Details | Diff | Review
Service checkbox; Preferences page option; v3. (51.38 KB, image/png)
2011-10-19 16:50 PDT, Brian R. Bondy [:bbondy]
faaborg: ui‑review+
robert.strong.bugs: feedback+
Details
Showing up separately inside Add/Remve programs. v2. (124.14 KB, image/png)
2011-10-19 17:36 PDT, Brian R. Bondy [:bbondy]
faaborg: ui‑review+
robert.strong.bugs: feedback+
Details
All changes in one for easy applying (245.24 KB, patch)
2011-10-21 09:21 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 1 - Base service code (60.09 KB, patch)
2011-10-21 09:22 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 2 - Main service logic (17.40 KB, patch)
2011-10-21 09:24 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 3 - Code for installing the service (16.30 KB, patch)
2011-10-21 09:25 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 4 - Certificate check code inside service (31.87 KB, patch)
2011-10-21 09:26 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 5 - XUL Runtime Environment code (13.57 KB, patch)
2011-10-21 09:28 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 6 - Firefox preferences update page changes (4.80 KB, patch)
2011-10-21 09:29 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 7 - RAII base helpers (8.26 KB, patch)
2011-10-21 09:33 PDT, Brian R. Bondy [:bbondy]
jmathies: review+
Details | Diff | Review
Patch 8 - NSPR wide string logging support on Windows (4.00 KB, patch)
2011-10-21 09:34 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes (13.94 KB, patch)
2011-10-21 09:36 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 10 - Shared installer code and service upgrade (3.72 KB, patch)
2011-10-21 09:37 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 11 - New maintenance service installer (9.23 KB, patch)
2011-10-21 09:38 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 12 - Simple SC plugin inclusion (43.57 KB, patch)
2011-10-21 09:40 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 13 - Moving the update check after init (3.98 KB, patch)
2011-10-21 09:41 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
Patch 14 - Build process (15.08 KB, patch)
2011-10-21 09:42 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Screenshot: Services checkbox moved under show update history button (51.13 KB, image/png)
2011-10-23 08:41 PDT, Brian R. Bondy [:bbondy]
netzen: ui‑review+
Details
Patch 6 - Firefox preferences update page changes. v2. (4.81 KB, patch)
2011-10-23 08:43 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 2 - Main service logic. v2 (18.46 KB, patch)
2011-10-23 18:48 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 15 - Added support for Limited user account upgrades and improved handling of tokens (15.35 KB, patch)
2011-10-24 10:36 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v2. (17.98 KB, patch)
2011-10-24 10:57 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Screenshot of affected pages for installer alignment changes (82.04 KB, image/png)
2011-10-24 11:02 PDT, Brian R. Bondy [:bbondy]
faaborg: ui‑review+
robert.strong.bugs: feedback+
Details
Patch 5 - XUL Runtime Environment code. v2. (14.03 KB, patch)
2011-10-25 21:19 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. (11.84 KB, patch)
2011-10-26 08:03 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 15 - UAC helper functions. (8.68 KB, patch)
2011-10-26 08:05 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
Patch 2 - Main service logic. v3. (22.29 KB, patch)
2011-10-26 08:08 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 1 - Base service code. v2. (60.88 KB, patch)
2011-10-26 08:55 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 3 - Code for installing the service. v2. (18.53 KB, patch)
2011-10-26 11:31 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
(PUSHED) Patch 8 - NSPR wide string logging support on Windows. v2. (3.97 KB, patch)
2011-10-26 14:11 PDT, Brian R. Bondy [:bbondy]
ted: review+
Details | Diff | Review
Patch 4 - Certificate check code inside service. v2. (33.47 KB, patch)
2011-10-26 20:43 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 2 - Main service logic. v3' (22.29 KB, patch)
2011-10-26 20:45 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v1' (11.83 KB, patch)
2011-10-26 20:47 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 5 - XUL Runtime Environment code. v3. (14.67 KB, patch)
2011-10-27 08:08 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 15 - UAC helper functions. v2. (10.07 KB, patch)
2011-10-27 17:47 PDT, Brian R. Bondy [:bbondy]
netzen: review+
Details | Diff | Review
Patch 1 - Base service code. v3. (60.87 KB, patch)
2011-10-27 18:40 PDT, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
Patch 2 - Main service logic. v4 (22.27 KB, patch)
2011-10-27 18:43 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 3 - Code for installing the service. v3. (18.52 KB, patch)
2011-10-27 18:46 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 5 - XUL Runtime Environment code. v4. (14.67 KB, patch)
2011-10-27 18:54 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v2 (11.78 KB, patch)
2011-10-27 19:00 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 15 - UAC helper functions. v2'. (9.18 KB, patch)
2011-10-28 09:23 PDT, Brian R. Bondy [:bbondy]
netzen: review+
Details | Diff | Review
Patch 12 - Simple SC plugin inclusion. v2. (27.66 KB, patch)
2011-10-28 18:29 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v3. (15.25 KB, patch)
2011-10-28 18:32 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v2. (9.40 KB, patch)
2011-10-28 18:36 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 14 - Build process. v2. (10.32 KB, patch)
2011-10-28 18:41 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 14 - Build process. v3. (10.39 KB, patch)
2011-10-29 11:16 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v3'. (15.39 KB, patch)
2011-10-29 11:24 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v3. (9.83 KB, patch)
2011-10-29 11:27 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 10 - Shared installer code and service upgrade. v2. (4.76 KB, patch)
2011-10-29 11:30 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v4. (15.39 KB, patch)
2011-10-29 17:06 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v3. (15.57 KB, patch)
2011-10-29 21:24 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 17 - New update.status for when service fails to force no service update (9.73 KB, patch)
2011-10-29 21:26 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 18 - Write out update.status error code in maintenance service on failure (4.03 KB, patch)
2011-10-29 21:29 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 19 - Create the updatedir from the installer and set permissions on it to full access (1.49 KB, patch)
2011-10-29 21:43 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 20 - Path hashing for unique registry location (7.78 KB, patch)
2011-10-30 20:19 PDT, Brian R. Bondy [:bbondy]
jmathies: review+
Details | Diff | Review
Patch 12 - Simple SC plugin inclusion. v3. (26.30 KB, patch)
2011-10-30 20:21 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 18 - Write out update.status error code in maintenance service on failure. v2. (5.49 KB, patch)
2011-10-30 20:22 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 4 - Certificate check code inside service. v3. (33.72 KB, patch)
2011-10-30 20:23 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v4. (9.23 KB, patch)
2011-10-30 20:25 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v5. (18.28 KB, patch)
2011-10-30 20:27 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 19 - Create the updatedir from the installer and set permissions on it to full access. v1'. (1.49 KB, patch)
2011-10-30 20:28 PDT, Brian R. Bondy [:bbondy]
jmathies: review+
Details | Diff | Review
Patch 12 - NSIS plugin for working with services. v4. (26.16 KB, patch)
2011-10-31 13:51 PDT, Brian R. Bondy [:bbondy]
jmathies: review+
Details | Diff | Review
Patch 7 - RAII base helpers. v2. (7.33 KB, patch)
2011-11-01 12:13 PDT, Brian R. Bondy [:bbondy]
jmathies: review+
benjamin: superreview+
Details | Diff | Review
Patch 3 - Code for installing the service. v3'. (18.52 KB, patch)
2011-11-01 12:27 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 18 - Write out update.status error code in maintenance service on failure. v2'. (10.37 KB, patch)
2011-11-01 12:36 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 5 - XUL Runtime Environment code. v4'. (14.67 KB, patch)
2011-11-01 12:40 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 4 - Certificate check code inside service. v3'. (33.79 KB, patch)
2011-11-01 12:53 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v5. (9.26 KB, patch)
2011-11-01 20:14 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 10 - Shared installer code and service upgrade. v3. (4.39 KB, patch)
2011-11-02 09:04 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 21 - New service self update code (5.32 KB, patch)
2011-11-02 09:08 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v5'. (9.31 KB, patch)
2011-11-02 13:01 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 22 - Always set pending-no-service when using service to handle unhandled failures (5.29 KB, patch)
2011-11-02 16:47 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 20 - Path hashing for unique registry location. v2. (7.77 KB, patch)
2011-11-02 18:42 PDT, Brian R. Bondy [:bbondy]
netzen: review+
Details | Diff | Review
Patch 11 - New maintenance service installer. v6. (9.42 KB, patch)
2011-11-03 06:43 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 2 - Main service logic. v4'. (22.32 KB, patch)
2011-11-03 07:40 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v4. (16.20 KB, patch)
2011-11-03 10:20 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 18 - Write out update.status error code in maintenance service on failure. v2''. (10.45 KB, patch)
2011-11-03 10:22 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 8 - NSPR wide string logging v 2.2 (same as v2 but based on wchar_t instead of WCHAR) (3.98 KB, patch)
2011-11-03 11:45 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 11 - New maintenance service installer. v7. (9.12 KB, patch)
2011-11-04 12:16 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v5. (16.59 KB, patch)
2011-11-04 18:59 PDT, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
(PUSHED) Patch 23 - Strings for localization (2.85 KB, patch)
2011-11-07 13:19 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
emorley: checkin+
Details | Diff | Review
Patch 7 - RAII base helpers. v3. (3.95 KB, patch)
2011-11-07 15:35 PST, Brian R. Bondy [:bbondy]
netzen: review+
benjamin: superreview+
Details | Diff | Review
Patch 2 - Main service logic. v4''. (22.32 KB, patch)
2011-11-07 15:41 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Patch 3 - Code for installing the service. v3''. (18.52 KB, patch)
2011-11-07 15:45 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 4 - Certificate check code inside service. v3''. (33.79 KB, patch)
2011-11-07 15:46 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 5 - XUL Runtime Environment code. v4''. (14.64 KB, patch)
2011-11-07 15:48 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 14 - Build process. v3'. (9.79 KB, patch)
2011-11-07 15:50 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 9 - Firefox installer/uninstaller changes. v5'. (16.84 KB, patch)
2011-11-08 06:31 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 14 - Build process. v3''. (9.81 KB, patch)
2011-11-08 06:33 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 3.5 temp - Review comments (6.17 KB, patch)
2011-11-09 20:22 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 24 - Fixed javadoc throughout (27.66 KB, patch)
2011-11-10 08:54 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 2 - Main service logic. v5. (22.14 KB, patch)
2011-11-10 09:33 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 16 - Support for launching callback app as unelevated. v5'. (16.59 KB, patch)
2011-11-10 09:35 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 18 - Write out update.status error code in maintenance service on failure. v2'''. (10.43 KB, patch)
2011-11-10 09:38 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 21 - New service self update code. v1'. (5.21 KB, patch)
2011-11-10 09:41 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 25 - Global scope operator fix (24.05 KB, patch)
2011-11-10 10:51 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
Patch 3 - Code for installing the service. v4. (18.17 KB, patch)
2011-11-10 12:06 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Patch 24 - Fixed javadoc throughout. v1'. (26.57 KB, patch)
2011-11-10 12:08 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v1. (252.58 KB, patch)
2011-11-10 20:45 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v1'. (252.58 KB, patch)
2011-11-10 20:59 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v2. (252.90 KB, patch)
2011-11-14 07:13 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
For security review only (51.54 KB, patch)
2011-11-14 07:42 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Prefs with and without the maint service installed (92.59 KB, image/png)
2011-11-15 18:47 PST, Brian R. Bondy [:bbondy]
limi: ui‑review+
robert.strong.bugs: feedback+
Details
Various additions described in Comment 346 (54.51 KB, patch)
2011-11-15 22:09 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v2'. (249.13 KB, patch)
2011-11-15 22:24 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Incremental changes on the big base patch. v2. (57.62 KB, patch)
2011-11-22 21:19 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
WIP - Adding tests using the maintenance service (43.06 KB, patch)
2011-11-23 17:30 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
Automated tests (v1) (68.33 KB, patch)
2011-11-25 09:13 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
All maintenance service and related code. v3. (271.72 KB, patch)
2011-11-27 21:08 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v4. (265.14 KB, patch)
2011-11-28 10:10 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v5. (266.01 KB, patch)
2011-11-29 10:46 PST, Brian R. Bondy [:bbondy]
ian.melven: feedback+
Details | Diff | Review
All maintenance service and related code. v6. (266.83 KB, patch)
2011-11-29 18:19 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Automated tests (v2) (72.82 KB, patch)
2011-11-30 07:27 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
Automated tests (v3) (74.61 KB, patch)
2011-11-30 15:09 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
File info / licensing info rc file (2.64 KB, patch)
2011-12-01 03:05 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Alternate file info / licensing info rc file as per :rs' suggestion (2.62 KB, patch)
2011-12-01 03:25 PST, Brian R. Bondy [:bbondy]
gerv: review+
Details | Diff | Review
All maintenance service and related code. v7. (266.01 KB, patch)
2011-12-01 04:23 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v7'. (266.01 KB, patch)
2011-12-01 04:42 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Automated tests (v4) (79.57 KB, patch)
2011-12-01 09:14 PST, :Ehsan Akhgari (busy, don't ask for review please)
robert.strong.bugs: review+
Details | Diff | Review
All maintenance service and related code. v8 (268.33 KB, patch)
2011-12-02 11:08 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Makefile hooks for signing (18.81 KB, patch)
2011-12-02 12:40 PST, Chris AtLee [:catlee]
ted: review+
robert.strong.bugs: review+
lukasblakk+bugs: approval‑mozilla‑esr10+
emorley: checkin+
Details | Diff | Review
All maintenance service and related code. v9 (270.23 KB, patch)
2011-12-03 06:38 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v10 (271.45 KB, patch)
2011-12-04 05:11 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v11 (272.68 KB, patch)
2011-12-04 20:19 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
telemetry patch emailed to me from Ehsan (2.03 KB, patch)
2011-12-05 23:00 PST, Robert Strong [:rstrong] (use needinfo to contact me)
no flags Details | Diff | Review
All maintenance service and related code. v12 (274.85 KB, patch)
2011-12-06 10:44 PST, Brian R. Bondy [:bbondy]
netzen: review+
Details | Diff | Review
All maintenance service and related code. v13 (276.39 KB, patch)
2011-12-06 17:24 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
All maintenance service and related code. v13' (273.29 KB, patch)
2011-12-06 18:11 PST, Brian R. Bondy [:bbondy]
no flags Details | Diff | Review
Automated tests (ready for landing) (112.56 KB, patch)
2011-12-07 08:16 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
Make sure that the maintenance service tests are only run when the service is enabled (12.95 KB, patch)
2011-12-08 16:05 PST, :Ehsan Akhgari (busy, don't ask for review please)
no flags Details | Diff | Review
Make sure that the maintenance service tests are only run when the service is enabled (12.12 KB, patch)
2011-12-08 16:07 PST, :Ehsan Akhgari (busy, don't ask for review please)
robert.strong.bugs: review+
Details | Diff | Review
All maintenance service and related code. v14 (273.37 KB, patch)
2011-12-08 19:53 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review-
Details | Diff | Review
Fallback key uninstall fix (2.90 KB, patch)
2011-12-11 18:37 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
Minor fix for post update process for XP (7.55 KB, patch)
2011-12-11 19:28 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
All maintenance service and related code. v15 (275.09 KB, patch)
2011-12-14 22:57 PST, Brian R. Bondy [:bbondy]
robert.strong.bugs: review+
Details | Diff | Review
All maintenance service and related code. v16 (276.21 KB, patch)
2011-12-15 08:55 PST, Brian R. Bondy [:bbondy]
netzen: review+
Details | Diff | Review

Description René 2009-03-05 22:53:40 PST
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7

As Vista becomes popular, quite a lot of internet cafes use an unprivileged user for customers to access the web. Therefore Firefox will not be updated unless the administrator logs in from time to time. As most internet cafes are not aware of the importance of regular updates, old Firefox versions will be in use for a long time.

I think it would be a good idea to include a windows service with sufficient privileges checking and installing Firefox updates regularly regardless what user is logged in.

Reproducible: Always
Comment 1 Matthias Versen [:Matti] 2009-03-06 02:35:22 PST
An User Account doesn't have the permissions to upgrade the files and displaying an alert will annoy users because they can't upgrade.
Comment 2 fcp 2009-03-31 18:04:56 PDT
I second this proposal.

Please change the summary to “Provide a Windows service to update applications without asking Administrator password” or something like that.  The problem which this bug tries to address is not specific to Internet cafes.

Using a Windows service is a well-known technique to update programs without user intervention on Windows.  For example, at least on Windows Vista, a service named “Windows Update” is enabled by default and used in the automatic update feature.  Some of the programs provided by Google also install an apparently similar service named “Google Software Updater.”

I'd like to note that Bug 318855 is related to this bug because both try to address some of the problems arising in the update without privileges.  They are not duplicates.

@Matthias: That is why a Windows service is desirable.
Comment 3 René 2009-04-03 04:20:47 PDT
Summary changed. As Windows provides automatic SW updates (if not disabled) for its core components (read IE), security critical bugs in IE will be automatically fixed. 
As Firefox is not a core component of Windows (yet ;), security fixes will not be automatically downloaded and applied if the user does not have sufficient privileges. Thus, Firefox could be one step behind.
Comment 5 Kevin Brosnan 2009-04-15 08:13:24 PDT
Released under the Apache License 2.0. As I understand that is incompatible with the MPL.
Comment 6 René 2009-04-29 01:32:07 PDT
Another issue I experienced that would be solved with this enhancement:
1) Admin-Role on XP installed Firefox 3.0.5
2) User-Role used Firefox 3.0.5, eventually an update to 3.0.6 was downloaded. User does not have privilege to install update.
3) Admin-Role updated to Firefox 3.0.8
4) User-Role: Every time (now updated) Firefox 3.0.8 was started, Firefox warned about an old update to 3.0.6 as it was still pending.

=> From my point of view I would expect old updates to be deleted or ignored on startup. I guess this behaviour should be filed as a new bug?

=> How to proceed with this bug? Who decides in general if a windows service is desired to automatically update Firefox? Otherwises this bug will get old...
Comment 7 Robert Strong [:rstrong] (use needinfo to contact me) 2009-04-29 01:45:05 PDT
(In reply to comment #6)
>...
> => From my point of view I would expect old updates to be deleted or ignored on
> startup. I guess this behaviour should be filed as a new bug?
That is fixed for the next Firefox release which is 3.5 by bug 485624 and bug 313057.

> => How to proceed with this bug? Who decides in general if a windows service is
> desired to automatically update Firefox? Otherwises this bug will get old...
I've had a couple of discussions this week about whether this bug should be implemented. There is at least one other option which would be to implement MSI's and use its update mechanism since it supports updating for non-admin users. This most likely won't get fixed for Firefox 3.5 or any of the 3.5.x releases but there is a decent chance it will be fixed in one form or another for the next major release after 3.5
Comment 8 René 2009-04-29 05:21:36 PDT
- Thank you for the prompt answer. As a regular and enthusiastic Firefox user I understand if this bug cannot be implemented for 3.5 anymore. On the other hand I also understand if people get a bad impression if SW updates do not run smoothly. 
As with my sister, they cannot handle the problem mentioned in #6 and don't want to bother. In this situation they need assistance. I assume the next major release would probably be in, let's say, 15 months? How do I explain to my sister Firefox 3.5 got a new feature as such and such but her "problem" is not solved?

- A few thoughts I have:
  - Firefox grew steadily in popularity, which is definitively great. But popularity brings also user expectations such as easy and safe upgrade and security fix procedures. Can we afford to postpone this for annother 15 months?

  - As I mentioned earlier, many internet cafes/shops install their computers once with a kiosk solution. Firefox 3.5 or newer may not be installed for a long time after release as they do not renew their computers/kiosk sw. Can we afford to have a lot of internet cafes run very old Firefox releases that are never updated? Of course it is the responsability of this shops, but most of them (at least in South East Asia) are just not aware of updates and their (security) impacts.

I just returned from a 6 month trip in South East Asia and thus visited internet cafes often. I managed to even see them running Firefox 1.5/2.0! If this is the case and Firefox cannot be upgraded manually, I have to use IE... which, in turn, is not good for "spreading the Firefox word".

  - As this enhancement would not solve the kiosk problem immediately, it would definitely solve it for the XP- and Vista Admin-/User Role. Internet Cafes not using kiosk sw tend to install sw with Admin rights only (which is a good thing). And it would solve it for non-tech people like my sister.

What do you think about my concerns/thoughts?
Comment 9 Robert Strong [:rstrong] (use needinfo to contact me) 2009-04-29 13:55:27 PDT
Release dates for software products are typically very fluid and I won't comment on when I personally think the next release is.

This bug in one form or another has always existed in Firefox during which time the popularity has grown. As far as app update is concerned chances are the majority of users haven't had a problem with it to the point where they weren't able to update. The problem still should be fixed though the way it is fixed may be different than implementing a service.

I am less concerned about kiosk systems than general user experience since kiosks are typically locked down to prevent users from adding / modifying / updating software. We would also have to provide the ability for the admins of these kiosks to not allow users updating which will likely take the form as an option in the installer. Are there examples of software that allow users on kiosks to update vs. requiring the admin to update it?

What about Firefox 1.5/2.0 being installed forces someone to use IE? This statement doesn't make sense to me.

We are constrained by time and resources to implement this and it simply won't be fixed for Firefox 3.5.
Comment 10 René 2009-04-29 15:26:36 PDT
1) The SW usually used was http://www.tinasoft.com/easycafe. It allows fine control what a user can do. It was setup in general to allow an user to do almost everything - even installing SW - but after logout the machine was rebooted to a reverted, previous, clean and virus free state.

2) The main argument about not using old and maybe unmaintained FF is about not patched security issues (gaining admin-privileges, stealing private data...). Web standard support is also much better in FF 3 thus using older FF versions causes sometimes display problems. If I find an old FF and an IE 7, my choice is pretty clear - I prefer the more or less patched but more current browser.
Comment 11 Robert Strong [:rstrong] (use needinfo to contact me) 2010-12-21 22:09:06 PST
*** Bug 615141 has been marked as a duplicate of this bug. ***
Comment 12 [Baboo] 2011-08-21 13:47:03 PDT
Is this bug just about Firefox?
Comment 13 Dave Garrett 2011-08-21 14:32:58 PDT
(In reply to [Baboo] from comment #12)
> Is this bug just about Firefox?

The product is listed up top is "Toolkit", thus it could be used by any application using the Mozilla Toolkit. Firefox would of course be the first to do so, but this could also be applied to Thunderbird and SeaMonkey as well as any other application that uses the Mozilla Platform.
Comment 14 Brian R. Bondy [:bbondy] 2011-09-19 20:02:48 PDT
Created attachment 561107 [details] [diff] [review]
Intermediate work in progress patch

Just providing a work in progress patch, not close to done yet nor do I need any feedback yet. 

Done so far:
- Created service base code
- Created service cmd line for install and uninstall + wait for stop
- icon & requireAdministrator manfiest
- Created makefile / build process + only build on winnt platform
- Added service to nsis installer and uninstaller.
Comment 15 Brian R. Bondy [:bbondy] 2011-09-23 13:56:13 PDT
Just wanted to provide an intermediate status update here:
- Created service base code (in attached patch)
- Created service cmd line for install and uninstall + wait for stop (in attached patch)
- icon & requireAdministrator manifest (in attached patch)
- Created makefile / build process + only build on winnt platform (in attached patch)
- Added service to nsis installer and uninstaller.  (in attached patch)
- Added code for directory change watching and parsing out values of pending update meta data (not yet uploaded)
- Added code for launching processes in different sessions (not yet uploaded)
- Added code for obtaining linked administrative token of a token of a given session ID to launch as administrator of (not yet uploaded)

Pending: 
- Writing out the update meta data file instead of calling updater.exe directly on startup
- Refactoring and Testing
- Add code for first time updates installing the service (nsis script)
- Create process flow documentation so that I can get a security review
- Anything else I forgot
Comment 16 Brian R. Bondy [:bbondy] 2011-09-27 20:18:17 PDT
Created attachment 562959 [details] [diff] [review]
Patch 1 of 6: Base Windows service code (works but still in progress)
Comment 17 Brian R. Bondy [:bbondy] 2011-09-27 20:20:06 PDT
Created attachment 562960 [details] [diff] [review]
Patch 2 of 6: Service main logic for work items to run updater.exe (works but still in progress)
Comment 18 Brian R. Bondy [:bbondy] 2011-09-27 20:21:27 PDT
Created attachment 562961 [details] [diff] [review]
Patch 3 of 6: /toolkit/xre code launching service work items (works but still in progress)
Comment 19 Brian R. Bondy [:bbondy] 2011-09-27 20:22:50 PDT
Created attachment 562962 [details] [diff] [review]
Patch 4 of 6: Proposed removing UAC elevation prompt from within js (works but still in progress)
Comment 20 Brian R. Bondy [:bbondy] 2011-09-27 20:29:13 PDT
Created attachment 562964 [details] [diff] [review]
Patch 5 of 6: Build procss
Comment 21 Brian R. Bondy [:bbondy] 2011-09-27 20:33:07 PDT
Created attachment 562965 [details] [diff] [review]
Patch 6 of 6: Service installer patch (works but still in progress, first time updates work todo)
Comment 22 Brian R. Bondy [:bbondy] 2011-09-27 20:46:27 PDT
Above I attached the first proof of concept in the form of 6 patches.  There are still a few TODOs for some edge cases I need to do inline in the patches.  Also we are currently too permissive on who can install updates.  So I need to think of a way to fix that.  Also I still need to do some things like check hashes to ensure we're executing the right things.

The way it works is that there is a new Windows service installed:
Display name: Mozilla Application Updater
Service name: MozillaUpdater
File name: updater_svc.exe

The service watches a directory for requested 'work items'. A 'work item' is just a file that contains the info that is usually passed to updater.exe to perform an update.  

Instead of Firefox.exe launching updater.exe on Windows, it will instead write a 'work item' and then shuts down on startup as normal.  If the service is not started, disabled, does not exist, or any other error occurs we will fall back to launching updater.exe directly again.

The service immediately picks up the 'work item' and figures out the correct session, windows station, and desktop to run the update as.  The service gets the user token for this session and then gets its linked token if running on Vista (UAC elevated one).  The service then executes updater.exe with this elevated token.  This is where the magic happens and it bypasses UAC. 

Since I'm not touching the logic of actually applying the updates, I think it is a much lower chance of regressions from what it could be if I tried to do the updates myself inside the service.  I thought it was best to leverage the stable and tested code as much as possible.

The service doesn't need to know about the different installations you currently have since it is just delegates the command line parameters and working directory to updater.exe.
Comment 23 Robert Kaiser (not working on stability any more) 2011-09-28 07:53:07 PDT
The one thing I'm worried about here is how easy is it for a malicious app running as an unprivileged user to put something in the right place and permanently harming Firefox or even gain admin privs through that?
Comment 24 Brian R. Bondy [:bbondy] 2011-09-28 08:09:04 PDT
> The one thing I'm worried about here is how easy is it for a malicious app running as an unprivileged user to put something in the right place 

Right this is mainly what I was talking about at the top of Comment 22.   I'd like to add a hash-check on updater.exe to ensure we're always executing the app which is our code only from the service.  Maybe even code to verify the app is signed by us before executing it (but I'm not sure if we sign aurora and other channels yet).

As for limited account users, I don't think we want to allow them to update though, so I think I need to build some other mechanism there to make sure they can't initiate an update. They will not have a linked elevated token though so I think it would fall back to the way it works currently (which we want to fix by asking for elevated permissions).
Comment 25 Brian R. Bondy [:bbondy] 2011-09-28 08:12:46 PDT
For my reference, code for checking signature of an exe:
http://msdn.microsoft.com/en-us/library/aa382384%28VS.85%29.aspx
Comment 26 [Baboo] 2011-09-28 08:18:54 PDT
To what extend will the Mozilla Updater Service now be a separate product?
What if in the future Thunderbird and Firefox both use this and then the user uninstalls one of them?
What about different versions? (e.g.: user first installs Firefox Beta then installs Thunderbird stable which ships with an older version of the updater service)
Will there be a possibility to uninstall or even install it independently from the Mozilla applications using it?
Comment 27 Brian R. Bondy [:bbondy] 2011-09-28 08:32:12 PDT
Re Baboo: Some of these are still open questions, but below I provided my thoughts.

> To what extend will the Mozilla Updater Service now be a separate product?

It is my understanding that it will be shipped directly inside the installers of each product.  We may have a checkbox for optional install.

> What if in the future Thunderbird and Firefox both use this and then the user uninstalls one of them?

It would stay alive until the last product/channel is alive or my preference would be that we would just have a separate installer/uninstaller that gets executed silent for it from each product.

> What about different versions? [channels]

We would always have at most one service installed.  Initial task were 1 per product (e.g. firefox vs thunderbird) but I see no reason currently to even have it distinct from product to product.  I think the service with the highest core version number would always win and it would have to be always backwards compatible.

> Will there be a possibility to uninstall or even install it independently from the Mozilla applications using it?

If it's not started, disabled or not installed it will fall back to the old way of updating.  So I think we should allow both of these things via some documentation.
Comment 28 Ben Turner (not reading bugmail, use the needinfo flag!) 2011-09-28 08:57:04 PDT
Brian, how do you envision us being able to update the updater service? Presumably updater.exe will need to be taught some new tricks?
Comment 29 Brian R. Bondy [:bbondy] 2011-09-28 09:25:16 PDT
> how do you envision us being able to update the updater service? Presumably updater.exe will need to be taught some new tricks?

I envision the nsis installer which is executed on update stopping the service if it's newer than what is available, then replacing then starting the service.
Comment 30 Robert Strong [:rstrong] (use needinfo to contact me) 2011-09-30 14:11:17 PDT
Comment on attachment 562959 [details] [diff] [review]
Patch 1 of 6: Base Windows service code (works but still in progress)

Looks good... please remove the progress bar.
Comment 31 Robert Strong [:rstrong] (use needinfo to contact me) 2011-09-30 14:13:00 PDT
(In reply to Robert Strong [:rstrong] (do not email) from comment #30)
> Comment on attachment 562959 [details] [diff] [review] [diff] [details] [review]
> Patch 1 of 6: Base Windows service code (works but still in progress)
> 
> Looks good... please remove the progress bar.
and ui, etc.
Comment 32 Justin Wood (:Callek) 2011-09-30 17:26:26 PDT
Comment on attachment 562961 [details] [diff] [review]
Patch 3 of 6: /toolkit/xre code launching service work items (works but still in progress)

Patch summary says still in progress but has a review request open, so commenting anyway

>+  // Init the update directory path and ensure it exists.
>+  // Example: C:\programData\Mozilla\Firefox\updates
>+  WCHAR programData[MAX_PATH + 1];
>+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
>+    SHGFP_TYPE_CURRENT, programData);
>+  if (FAILED(hr)) {
>+    return FALSE;
>+  }
>+  if (!::PathAppendW(programData, L"Mozilla")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(programData, NULL);
>+  if (!::PathAppendW(programData, L"Firefox")) {
>+    return FALSE;

More applications than just Firefox exist here and should be accounted for, especially 3'rd party compilations of Firefox (that can't use the trademark).
Comment 33 Robert Strong [:rstrong] (use needinfo to contact me) 2011-09-30 17:28:14 PDT
I had a similar comment.

>+BOOL GetUpdateDirectoryPath(WCHAR *path) 
>+{
>+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
>+    SHGFP_TYPE_CURRENT, path);
>+  if (FAILED(hr)) {
>+    return FALSE;
>+  }
>+  if (!::PathAppendW(path, L"Mozilla")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(path, NULL);
>+  if (!::PathAppendW(path, L"Firefox")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(path, NULL);
>+
>+  ::CreateDirectoryW(path, NULL);
>+  if (!::PathAppendW(path, L"updates")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(path, NULL);
>+  return TRUE;
>+}
This won't work for other apps. I'd go with passing this data instead perhaps using the same file used to pass the session id.
Comment 34 Brian R. Bondy [:bbondy] 2011-09-30 17:33:58 PDT
Thanks for the feedback thus far!

> This won't work for other apps. I'd go with passing this data instead perhaps using the same file used to pass the session id.

I'll just monitor the parent directory (is Mozilla/updates ok?) and there is no need to pass the data.  Changes to any subdirectory of that folder would be seen by the service.  I couldn't pass the data anyway because the service needs to know which directory to watch before it watches it.
Comment 35 Robert Strong [:rstrong] (use needinfo to contact me) 2011-09-30 17:38:19 PDT
That should be fine with bug 572162 also fixed though it would be possible to use a directory that is writeable by All Users such as ProgramData on Win7.
Comment 36 Robert Strong [:rstrong] (use needinfo to contact me) 2011-09-30 17:42:50 PDT
Come to think of it doing that isn't dependent on bug 572162
Comment 37 [Baboo] 2011-10-01 02:04:13 PDT
(In reply to Justin Wood (:Callek) from comment #32)
> especially 3'rd party compilations of Firefox (that can't use the trademark).

Speaking of them, will they be even be able to use this service if it's going to check for (hardcoded?) signatures?
Comment 38 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-01 02:08:05 PDT
At this time we are primarily prototyping and the end result will allow others to use the service.
Comment 39 Brian R. Bondy [:bbondy] 2011-10-02 19:43:49 PDT
Here is the list of things left to do that I'll be tackling this week:

- Make wiki page for the software update process flow.
- Ask to schedule a security review once the wiki page is up
- Limited user accounts should allow updates at all times, if the service is installed.
- Add NSPR logging throughout the base service code and main service logic
- Add NSPR logging into update process inside Firefox
- Make updater code have a separate silent uninstaller that shows up under add/remove programs.
- Make updater code have a checkbox option on install for whether to install it or not.
- If a limited user account installs without elevation, then don't install the service nor show the option in the installer.
- Add an about:config option (or use an existing one) for whether to use the service for updates or not.  
- Default the new about:config option to off, but turn it to on by default for Firefox in particular.
- Possibly add new UI in Firefox preferences for whether or not to use the service.
- Separate out the update and post update process, investigate doing the main update as the system account in session 0 and the post update process as the user's session using his token.
- Fix up directory we are monitoring to not be Firefox specific
- Don't always use the same name for the update meta files so that 2 different updates from 2 different channels or applications can happen at the same time.
- Investigate how to deal with x86 + x64 installs.  (Currently planned to use the newest version regardless of x86 or x64)
- Ensure that only one service gets installed at most.
- Move the service code under /toolkit/components/ as it may later be used for other things than just updating.
- Find a better more generalized name for the service, something like Mozilla Maintenance Service?
- Make updates stop the service, apply the update, and then restart the service so that the service itself can be replaced.  
- Add first time update install of the service
- Add code to only install/update the service if the version number is greater than the last installed version number for the service.
- Possibly add telemetry so we know I) who will be using the service and ii) who isn't using the service explicitly, and iii) who is trying to but falling back to the old way because of some error.
- Possibly add check to make sure we signed the updater.exe if Nightly builds get signed.
- Possibly do a new hash check on the MAR file which is being applied (will likely be done in the context of another bug ID)
- No longer copy the updater.exe outside of Program files to execute it, that way we don't need to do a hash check on the file since it (should be) installed in a location that requires elevation like Program Files.  Use different name in same directory before executing updater.exe from the service.
Comment 40 Kelley Cook 2011-10-03 06:00:47 PDT
One additional thing to check for with the move to a service: a long standing annoyance** exists for Windows computers using Fast User Switch (multiple semi-simultaneous users).  The updater does not realize when there are multiple copies of Firefox running under different accounts.  After an update is downloaded, it prompts you to restart firefox, but since it attempts to overwrite still open files (e.g. DLLs), it fails.

The separate updater service should be able to overcome this problem.  Possibly by only launching the updater process when all the instances of Firefox are down and notifying the current user that other users are still running Firefox.

** I actually don't see this issue in bugzilla, I can do that tonight.
Comment 41 Brian R. Bondy [:bbondy] 2011-10-03 06:31:01 PDT
I believe that we only apply updates currently if we can lock firefox.exe (open without sharing access).  So this would ensure it's not already in use.  But we probably fail to apply in the case you mentioned and the service could help here.  Sure please post a bug for this as the service can shutdown all instances across all windows sessions.  We can plan in the context of that ticket what to do exactly.
Comment 42 Brian R. Bondy [:bbondy] 2011-10-04 08:28:55 PDT
A lot of people in comments of the blog post are asking for this, so adding to the list of things to do:

- Figure out how to allow the service to be started/ stopped from unelevated processes.
- Add ability to start the service only on demand before applying the update and then stop again right after.
Comment 43 André Ziegler 2011-10-04 10:21:33 PDT
Why a service? Run a task with the task scheduler. I use this to workaround the UAC prompts and from what I see google also uses this way to update chrome.
Comment 44 Brian R. Bondy [:bbondy] 2011-10-04 10:29:44 PDT
We have previously discussed windows scheduled tasks but decided to do a service. 

As far as I know there are 2 or 3 different APIs for Windows task scheduler for different Windows operatian elevated administrator process you need to enter the user's username and ng systems.  Also at least in some of those APIs to run as password.   We need a solution that will work on all operating systems Win2k and above consistently. A service seems to be the cleanest way to do that.  As per comment 42 the service will be very low overhead.  Unlike Google we usually install into Program Files which requires elevation to write to.
Comment 45 Brian R. Bondy [:bbondy] 2011-10-04 10:37:52 PDT
Sorry I think that last message got garbled from drag and drop. 

Here it is again (corrected):

We have previously discussed windows scheduled tasks but decided to do a service.

As far as I know there are 2 or 3 different APIs for Windows task scheduler, each for different Windows operating systems.  

Also at least in some of those APIs to run as an elevated administrator process you need to enter the user's username and password.   We need a solution that will work on all operating systems Win2k and above consistently. A service seems to be the cleanest way to do that.  As per comment 42 the service will be very low overhead.  Unlike Google we usually install into Program Files which requires elevation to write to.
Comment 46 Masatoshi Kimura [:emk] 2011-10-04 11:44:09 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #45)
> Unlike Google we usually
> install into Program Files which requires elevation to write to.
Google Updater can update applications installed under Program Files without elevation or password.
Chrome is installed under user's profile by default to avoid UAC dialog on installation (NOT update).
Comment 47 James Ross 2011-10-04 13:53:33 PDT
I would prefer a scheduled task (Vista+ at least) but if a service is the chosen route I'd like back the plan that it should be strictly demand-start. I believe you can do this by using SetServiceObjectSecurity to add the SERVICE_START permission for local authenticated users (DOMAIN_ALIAS_RID_USERS?) to the service's ACL. That will allow any local user to start the service, and the service can shut itself down when it completes the update.
Comment 48 Brian R. Bondy [:bbondy] 2011-10-04 13:56:00 PDT
The ACE work was done earlier today and yes it was done using SetServiceObjectSecurity. The process is described already in the wiki for this service and silent updates here: https://wiki.mozilla.org/Windows_Service_Silent_Update

But that wiki is still heavily in progress, it would be better to read it tomorrow when I'm done with edits.
Comment 49 André Ziegler 2011-10-04 16:11:39 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #44)
> We have previously discussed windows scheduled tasks but decided to do a
> service. 

ok, thanks :)
Comment 50 Joe Drew (not getting mail) 2011-10-06 15:06:02 PDT
I have one feature request, which is that as part of the checking-for-work-to-do, the updater also checks for further updates from whatever source Firefox uses (on a similar schedule so as not to DoS the updater web site); that way, we'll always have the most up-to-date Firefox, even if we haven't run Firefox lately.
Comment 51 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-06 15:22:40 PDT
(In reply to Joe Drew (:JOEDREW!) from comment #50)
That is bug 353804. The patch has a bug in it that I need to find time to investigate.
Comment 52 Joe Drew (not getting mail) 2011-10-06 15:29:20 PDT
Sorry, I was not clear enough. My feature request is for a service that will always keep us up to date without needing to run Firefox, similar to what (IIUC) Chrome has.
Comment 53 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-06 15:38:28 PDT
A couple of things I don't want to tackle in this bug are the security attack vectors that come with having a service or task that has access to the network, how we allow a user to disable updating from within Firefox if there is a service that does the download (keep in mind that a single install can have multiple profiles and multiple users), and the warning when the update will disable add-ons.

So, Firefox product drivers would need to at the very least decide to do the following first:
1. remove the option from within Firefox to disable updates
2. remove the warning when an update will disable add-ons

These two issues are difficult enough to solve that I don't want to hold back the implementation as it stands today for them to be fixed or decided upon in order to have the service perform the download as well.
Comment 54 Joe Drew (not getting mail) 2011-10-06 17:40:23 PDT
All that sounds great. I also wanted to make sure we didn't accidentally make any design decisions that precluded background update from happening.
Comment 55 Justin Wood (:Callek) 2011-10-11 12:56:38 PDT
Brian,

Without reading too much I just want to comment a requirement for Gecko-Dependant Apps here. We can't *fully* rely on code-signing for this, at least not for SeaMonkey.

I am happy however to rely on it by default, pref it, whatever though. I just don't want us baking in a way that prevents SeaMonkey users from [auto-]updating simply because SeaMonkey has no access to a code-signing machine at present.
Comment 56 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-11 13:05:43 PDT
I am ok with there being a way for SeaMonkey to opt out of the signing requirement as long as it isn't so easy that a malicious app could opt out Firefox. I have no idea if this would be simple to implement and I am fairly certain that a pref definitely wouldn't be acceptable since a malicious app in the user session could change the pref.
Comment 57 Justin Wood (:Callek) 2011-10-11 13:21:52 PDT
(In reply to Robert Strong [:rstrong] (do not email) from comment #56)
> I am ok with there being a way for SeaMonkey to opt out of the signing
> requirement as long as it isn't so easy that a malicious app could opt out
> Firefox. I have no idea if this would be simple to implement and I am fairly
> certain that a pref definitely wouldn't be acceptable since a malicious app
> in the user session could change the pref.

Just to elaborate (since you're not on IRC atm)

I'm of the mindset that a malicious app on users computer means we have already lost :-). And we could require the pref to be app-dir only, like some other prefs we use/have relating to this stuff. In-fact the pref for Update Server restriction doesn't even follow that (profile-level prefs are enough)

In the end, I think if app-dir pref is ok, we should be good. Since in win7/Vista the Program Files dir is restricted by default, and if a malicious program can write/modify there as is, it is just as easy for it to also modify Firefox directly.

This said, if its *easier* to do ifdefs in code, I can accept that, but my experience is the more divergent codepaths exist, the easier it is to break them, especially when they are not part of Gecko/Firefox proper.
Comment 58 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-11 13:28:37 PDT
(In reply to Justin Wood (:Callek) from comment #57)
> (In reply to Robert Strong [:rstrong] (do not email) from comment #56)
> > I am ok with there being a way for SeaMonkey to opt out of the signing
> > requirement as long as it isn't so easy that a malicious app could opt out
> > Firefox. I have no idea if this would be simple to implement and I am fairly
> > certain that a pref definitely wouldn't be acceptable since a malicious app
> > in the user session could change the pref.
> 
> Just to elaborate (since you're not on IRC atm)
:rs on irc... I got complaints about another :rsxxx on bugzilla causing multiple matches

> I'm of the mindset that a malicious app on users computer means we have
> already lost :-). And we could require the pref to be app-dir only, like
> some other prefs we use/have relating to this stuff. In-fact the pref for
> Update Server restriction doesn't even follow that (profile-level prefs are
> enough)
I agree when it comes to the data in question being under program files or in HKLM but not in relation to the profile.

If this were to be opt out I think it would be simplest and safest for it to be build time.
Comment 59 Brian R. Bondy [:bbondy] 2011-10-12 08:13:28 PDT
> If this were to be opt out I think it would be simplest and safest for it to be build time.

A compile time #define flag sounds like a great way to disable the requirement for the exe you are launching to check signing.
There is a security risk there though as we are bypassing UAC, so if this is disabled, an alternate check should also be made like a check on the file hash of updater.exe.

> I'm of the mindset that a malicious app on users computer means we have already lost 

If we executed a malicious app instead of our own exe I think we would be blamed here, since we were the one that didn't show the user a UAC prompt.
I'm really only OK with a flag to disable it if an alternate security fix is implemented at the same time.

> In the end, I think if app-dir pref is ok, we should be good. Since in win7/Vista the Program Files dir is restricted
> by default, and if a malicious program can write/modify there as is, it is just as easy for it to also modify Firefox directly.

I was originally justifying it the same way, but I don't think that is enough justification.
The problem here is that WE will install into a non restricted location if the user is a limited user account.
Also the user can specify a non restricted location.  The former is not the user's fault, the later might be, but would in the end be our fault for not warning them or for allowing the service to be installed.
Comment 60 Nigel 2011-10-13 07:46:36 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #41)
> I believe that we only apply updates currently if we can lock firefox.exe
> (open without sharing access).  So this would ensure it's not already in
> use.  But we probably fail to apply in the case you mentioned and the
> service could help here.  Sure please post a bug for this as the service can
> shutdown all instances across all windows sessions.  We can plan in the
> context of that ticket what to do exactly.

Some people use Firefox Preloader applications to speed it working.  Would you not have a rule that if after a period of n hours that you have not been able to get exclusive access to the Firefox.EXE that on starting Firefox the user is advised that an update from a date of Y is still pending - so if at a kiosk, one could see how out of date Firefox was?

Also re the updater working as a service - unless you can 100% guarantee that only genuine Mozilla products will be actioned by the service, then corporates and most users who want security will disable the service
Comment 61 alanjstr 2011-10-13 08:44:36 PDT
Would it be possible for the updater service to be responsible for downloading the updates, instead of the application?  If you're aware of an enhancement request for that, please let me know.
Comment 62 Brian R. Bondy [:bbondy] 2011-10-13 10:21:57 PDT
> Some people use Firefox Preloader applications to speed it working...

I think there is some kind of notification eventually already.  This code is not being changed in any way, it will work the same as it used to.

> Unless you can 100% guarantee that only genuine Mozilla products will be actioned by the service

I think we will be 100% guaranteeing.

> Would it be possible for the updater service to be responsible for downloading the updates

I think this would be desirable, but is probably out of scope for this first release.
Comment 63 Brian R. Bondy [:bbondy] 2011-10-13 10:28:56 PDT
Just wanted to sync up the todo list for this task. 

Remaining items are:
- Investigate how to deal with x86 + x64 installs.  (Currently planned to use the newest version regardless of x86 or x64)
- Separate out the update and post update process, investigate doing the main update as the system account in session 0 and the post update process as the user's session using his token.
- Limited user accounts should allow updates at all times, if the service is installed.
- If a limited user account installs without elevation, then don't install the service nor show the option in the installer.
- Possibly add new UI in Firefox preferences for whether or not to use the service.
- Version number: Need to check all 4 components NSVersionComparitor *maybe switch to it
- Add NSPR logging into update process inside Firefox (already added into the service itself)
- Support installing to a different directory?
- Add check to make sure we signed the updater.exe if Nightly builds get signed.
- Possibly do a new hash check on the MAR file which is being applied (will likely be done in the context of another bug ID)le since it (should be) installed in a location that requires elevation like Program Files.  Use different name in same directory before executing updater.exe from the service.
- Possibly add telemetry so we know I) who will be using the service and ii) who isn't using the service explicitly, and iii) who is trying to but falling back to the old way because of some error.

Recently done:
- Added a debug command line parameter so you can run the service exe normally when you want to debug.
- Removed progress and dialog resource from the service since those are unused and were copied over by accident from the updater.exe project rc file.
- Changed the service to be on demand only at creation time instead of auto start which it used to be 
- Added code so the service can be started by user processes (set the service to have user access DACL)
- Added code for reading version information of executables so that it can be used to see when the service needs to be updated
- Made new nsAutoHandle and nsAutoServiceHandle classes to cleanup code which may one day lead to handle leaks
- Made updates stop the service, apply the update  so that the service itself can be replaced. 
- Now load WTSQueryUser token dynamically so that it will work on Win2k 
- Got rid of several edge cases that had TODO comments in them
- Updated Firefox code to attempt to start the service before attempting to write the service command.
- Added NSPR logging throughout the base service code and main service logic
- Added a new bool preference (app.update.service) in firefox.js defaulted to true
  - Added code inside nsUpdateDriver.cpp to use this pref, if the pref doesn't exist it defaults to false.
  - If the pref is set to false it will not even attempt to use the service for updates
- Make uninstaller have a components page that you can select what to uninstall
- Made new component installer page with required main app component and optional maintenance checkbox.  
  - User can now pick to install the service or not.  
  - Only shows up if you go to Custom setup. 
  - Defaults to on.
- Fix up directory we are monitoring to not be Firefox specific
- Moved the service code under /toolkit/components/maintenanceservice as it may later be used for other things than just updating.
- Renamed everything to Mozilla Maintenance Service, and removed anything specific in filenames to updater
- Added install of the service when updating
Comment 64 Brian R. Bondy [:bbondy] 2011-10-13 10:31:40 PDT
Created attachment 566880 [details] [diff] [review]
Intermediate backup of service related work

This is just a backup of the work for this task, I will split the patch up into sections again once it is ready for review.
Comment 65 Justin Wood (:Callek) 2011-10-13 13:41:42 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #63)
> Just wanted to sync up the todo list for this task. 
> 
> Remaining items are:

> - Add check to make sure we signed the updater.exe if Nightly builds get
> signed.
> - Possibly do a new hash check on the MAR file which is being applied (will
> likely be done in the context of another bug ID)le since it (should be)
> installed in a location that requires elevation like Program Files.  Use
> different name in same directory before executing updater.exe from the
> service.

Just to be clear, SeaMonkey will need _a_ way to still have updates run even though even our releases are currently unsigned. If that is to add a new build-time define, sure. I say this again simply because you mentioned the "hash" thing would be a requirement of that, but you want to split that out.
Comment 66 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-13 15:34:09 PDT
(In reply to Justin Wood (:Callek) from comment #65)
Updates will still fallback to the current method if we aren't able to come up with a decent method for preventing this attack vector without using signing so SeaMonkey will still be able to update using the current method.
Comment 67 Brian R. Bondy [:bbondy] 2011-10-13 15:49:03 PDT
Created attachment 566962 [details]
Ideas for optional install process

We need to make the installer for Firefox have the ability to optionally install the service.  This UX review is to get feedback on the best way to do that.  This is only a temporary solution that will be shipped until the new installer stub is ready.

I proposed some ideas in the attached screenshot.  Red circles in the attachment indicate each place we could put the new option.  Also acceptable would be a whole new page.

Also I need feedback on the uninstall process.  Whether there should be a separate item in add/remove programs, a components whole installer step, or just a checkbox.
Comment 68 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-14 15:28:16 PDT
Comment on attachment 566962 [details]
Ideas for optional install process

Let's just install the service, and then potentially allow users to stop it from somewhere in advanced preferences.  The problem with introducing it here is:

-increased complexity to the install process
-there is no way to undo the action later
Comment 69 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-14 15:33:23 PDT
Alex, there has been a decent amount of flack type comments regarding not providing an opt out option in the installer and I would prefer not to alienate these people.

Also, this will be installed by the installer anyways which is what the installer is good at and the increased complexity is actually quite small for this because of this.

There will be a way to uninstall this via Add / Remove Programs and Programs and Features.

Adding an option in advanced preferences on the other hand requires Firefox to launch a separate binary to perform the task of disabling it. This is somewhat complicated.
Comment 70 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-14 15:38:42 PDT
Comment on attachment 566962 [details]
Ideas for optional install process

Alex, please reconsider after reading comment #69. I'm rather dead-set against the approach you suggested.
Comment 71 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-14 15:46:03 PDT
>I would prefer not to alienate these people.

as would I, my premise being that establishing a pref inside of Firefox provides more control because they always have access to where the preference is (uninstalling the entire application by contrast isn't as much control).

>Adding an option in advanced preferences on the other hand requires Firefox to launch
>a separate binary to perform the task of disabling it. This is somewhat complicated.

If it is only somewhat complicated then I think it might be worth it as it provides more user control.  We could also add that level of control later in addition to a check box in the installer.
Comment 72 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-14 15:47:56 PDT
Comment on attachment 566962 [details]
Ideas for optional install process

let's go with just the third screen in (choose components, which is in the flow after they select custom install).
Comment 73 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-14 15:51:06 PDT
(In reply to Alex Faaborg [:faaborg] (Firefox UX) from comment #71)
> >I would prefer not to alienate these people.
> 
> as would I, my premise being that establishing a pref inside of Firefox
> provides more control because they always have access to where the
> preference is (uninstalling the entire application by contrast isn't as much
> control).
I am ok with that but these people do not want the service installed at all. When the service is already installed whether it is from another Mozilla based application or a second installation of Firefox then the option would not be displayed.

The plan is for the service to only run on demand already and I am fine with having a pref that makes Firefox not use the service when it is installed.

> 
> >Adding an option in advanced preferences on the other hand requires Firefox to launch
> >a separate binary to perform the task of disabling it. This is somewhat complicated.
> 
> If it is only somewhat complicated then I think it might be worth it as it
> provides more user control.  We could also add that level of control later
> in addition to a check box in the installer.
See above. Having Firefox not use the service I am fine with.
Comment 74 Dave Garrett 2011-10-14 17:05:02 PDT
Adding an option to the installer is pretty straightforward, but what about the first installation of the service via Firefox update?
Comment 75 Brian R. Bondy [:bbondy] 2011-10-14 17:47:00 PDT
> but what about the first installation of the service via Firefox update?

The plan is currently to install it via the update for existing users, and people can remove it if they don't want it.

> The plan is for the service to only run on demand already and I am fine with
> having a pref that makes Firefox not use the service when it is installed

I think that pref is independent of the instsall option. I'll put another proposed screenshot for that pref for UX review.
Rob are you OK with the components page on install and a separate uninstall (for the current temporary installer). If not how about a new custom page that just has a checkbox and its own step without the list box?
Comment 76 Brian R. Bondy [:bbondy] 2011-10-14 17:52:10 PDT
Created attachment 567232 [details]
A proposed new option for whether or not the product should use the silent update service.  Only shows up if service is installed.
Comment 77 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-14 17:54:19 PDT
We definitely can't use that NSIS provided ui used in the screenshot unless it has had some fairly major rework done to it sometime in the last few years. We tried to when I implemented the installer for Firefox 2 and had to remove it since it isn't screen reader friendly.

Creating a custom page as is done for the shortcuts page would be a decent alternative. Might be a good thing to include some explanation about why it is a good thing not to uncheck the box on the page as well. Perhaps dynamically show it as is done for the profile removal in the uninstaller so it calls attention to itself if the user unchecks it.

Also, I don't think we want to show the page on install if the service is already installed.
Comment 78 Brian R. Bondy [:bbondy] 2011-10-14 18:00:07 PDT
OK I'll proceed with the new page similar to the component selection page but screen reader friendly.  (i.e. the same idea as the shortcut page). It'll have extra explanation and context inline on the page we can tweak later.

For the uninstaller it will show up as a separate product.

If you have any objections faaborg please let us know.

> Also, I don't think we want to show the page on install if the service is already installed.

Good catch, yup I agree.
Comment 79 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-14 18:58:15 PDT
Comment on attachment 567232 [details]
A proposed new option for whether or not the product should use the silent update service.  Only shows up if service is installed.

Brian, I recall you mentioning plans to bypass the UAC when manually applying an update if the service was present as well. If that is the case then it would make sense to align it with the radio buttons. Also, the ui changed recently in bug 600505.
Comment 80 Brian R. Bondy [:bbondy] 2011-10-14 18:59:03 PDT
Created attachment 567241 [details] [diff] [review]
Patch for service certificate check on updater.exe and related logic

Here is the certificate check code implemented.  It checks to make sure the information on the file we are executing's cert is correct and also checks to make sure it is trusted.

It is currently hard coded to the Mozilla info, but I have a TODO comment in there talking about moving that into HKLM registry.

This is a pretty early patch so I'm pretty sure it will have a security-review-minus for now but I would really appreciate any feedback and help on if I'm checking the certs correctly and if I'm susceptible to any sort of attacks.

When you look at the patch, it's easiest to read it if you start with:
> diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp

Then follow the implementations of CheckCertificateForPEFile and  VerifyCertificateTrustForFile.

On a side note it might be worth also having this check from within Firefox and if it fails don't even try to use the service (fall back to the old method).  The check would be done as well in the service regardless.  If you agree it should be in Firefox as well, please advise on the best place in the tree to put the related PE file certificate check code (certificatecheck.cpp/.h).
Comment 81 Brian R. Bondy [:bbondy] 2011-10-14 19:02:13 PDT
>  If that is the case then it would make sense to align it with the radio buttons

It doesn't do this now but that makes sense.  I'll align it with the radio buttons as you suggested. We can change it later if we want.
Comment 82 Brian R. Bondy [:bbondy] 2011-10-14 19:13:18 PDT
The r? for security is only looking for feedback, I just didn't have a second feedback? field to use.  So please only provide feedback for now then cancel the flag.
Comment 83 Brian R. Bondy [:bbondy] 2011-10-14 19:32:23 PDT
Comment on attachment 567241 [details] [diff] [review]
Patch for service certificate check on updater.exe and related logic

Didn't realize you could do comma separated fields for feedback/reviews, thx for the tip callek.
Comment 84 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-17 10:41:44 PDT
Comment on attachment 567232 [details]
A proposed new option for whether or not the product should use the silent update service.  Only shows up if service is installed.

this is too detailed, let's leave off silent, which is controversial, UAC, which is jargon, and branding it as Mozilla Maintence, and just say:

"Use a background service to install the update"
Comment 85 Justin Wood (:Callek) 2011-10-17 15:04:44 PDT
(In reply to Alex Faaborg [:faaborg] (Firefox UX) from comment #84)
> Comment on attachment 567232 [details]
> 
> "Use a background service to install the update"

bikeshed: "Use a background service to install updates"
Comment 86 Brian R. Bondy [:bbondy] 2011-10-17 20:59:33 PDT
Created attachment 567667 [details]
Screenshot: New installer step inside Firefox installer
Comment 87 Brian R. Bondy [:bbondy] 2011-10-17 21:00:28 PDT
Created attachment 567668 [details]
Screenshot: Program files directory showing output files of Maintenance Service Installer
Comment 88 Brian R. Bondy [:bbondy] 2011-10-17 21:01:11 PDT
Created attachment 567669 [details]
Screenshot: Showing up separately inside Add/Remve programs (icon pending)
Comment 89 Brian R. Bondy [:bbondy] 2011-10-17 21:20:18 PDT
Created attachment 567671 [details] [diff] [review]
New installer process pretty much completed minus some code cleanup

Attached is the new installer work redone.  

The installer process works by having a completely separate installer for the maintenance service.
You can run this standalone if you want with a GUI, but typically it will be run silently.
This new installer shows up in add/remove programs as Mozilla Maintenance Service.
 
The existing Firefox installer has a new property page the same as the shortcuts page but asking if you want to install the maintenance service.
The new NSIS maintenance service installer gets included and executed silently if the checkbox for Install Maintenance service is specified.

For feedback I'm looking for a general "yup you're in the right direction" or not. 
Also I'd like to know if there's a better way inside toolkit/mozapps/installer/windows/nsis/makensis.mk
from what I'm doing.

In particular in the new NSI I'm using File directives and to get those to work I have to copy the files I want into the installer dir makensis.mk:
$(INSTALL) $(addprefix $(shell pwd)/../../../dist/bin/,maintenanceservice.exe nspr4.dll) $(CONFIG_DIR)
+
I'd like to get rid of that line from makensis.mk for cleaner code.
Comment 90 Brian R. Bondy [:bbondy] 2011-10-18 06:04:48 PDT
Created attachment 567723 [details] [diff] [review]
New installer process pretty much completed minus some code cleanup. v2

Same as last patch w/ minor fix for default install service value and proper checking if it is checked.
Comment 91 Ian Melven :imelven 2011-10-18 06:22:15 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #64)
> Created attachment 566880 [details] [diff] [review] [diff] [details] [review]
> Intermediate backup of service related work
> 
> This is just a backup of the work for this task, I will split the patch up
> into sections again once it is ready for review.

i took a pass through this patch and the earlier iterations of it and sent bbondy a couple of small pieces of feedback. 

my primary findings security wise are that the service ACL allows access from the "Users" group (all authenticated users on the machine), which means any user can start, stop, etc. the service. If the service isn't running and can't be started the installer will fall back to the regular install with a UAC prompt. My chief concern is still the session ID spoofing mechanism, since any user can drop an update file with an arbitrary session ID into the 'pickup' directory, the post install actions can be run by an arbitrary user as an arbitrary user. This means it's crucial an attacker can't influence what actions are run or the parameters to these actions. Additionally, the service is running as SYSTEM so before landing, these needs another more thorough code review to look for input validation/overflow type problems.
Comment 92 Brian R. Bondy [:bbondy] 2011-10-18 06:33:49 PDT
Comment on attachment 567667 [details]
Screenshot: New installer step inside Firefox installer

Adding UX review for new installer step.
Pls + w/ nits if it is just text changes
Comment 93 Brian R. Bondy [:bbondy] 2011-10-18 07:18:30 PDT
Created attachment 567740 [details]
A proposed new option for whether or not the product should use the silent update service. Only shows up if service is installed. v2

Proposed changes to the preferences dialog. 
This one is not a mockup but actually working.
Please ui-review+ w/ nits if just text changes.
Comment 94 Ian Melven :imelven 2011-10-18 09:04:00 PDT
Comment on attachment 567241 [details] [diff] [review]
Patch for service certificate check on updater.exe and related logic

i took a pass through the patch, the windows-y/c++ part of it looks great.

from my reading, the code takes a hardcoded issuer, cert name, and signer info (program name, publisher and more info link), and uses this to look up a cert with matching details in the OS cert store, it then checks that the retrieved cert matches the hardcoded data it's looking for. if the cert specifies program and publisher info, this is also verified against the hardcoded set we are looking for. the code also verifies the signing certificate on the file chains to a root cert in the OS root store and that the certificate has the ability to sign code. it does not check for cert revocation. 

bbondy, can you elaborate on why in VerifyCertifcateTrustForFile an error TRUST_E_TIME_STAMP ends up saying the file is validated ?

also, your comment questioning the handling of CRYPT_E_SECURITY_SETTINGS is interesting. from searching it appears that AV programs and 3rd party firewalls can often result in this error - it appears that this error results from an admin not trusting the cert and prohibiting the user from trusting the cert - s my inclination is that this should NOT verify the file - however i'm wary of possible support issues if something messes up the cert store, BUT i also found postings saying that this error occurred when trying to update windows itself. so overall, my opinion is that we should fail the verification if this error occurs.

one more question, how does the signer cert initially get into the OS cert store ? additionally, what's the reasoning behind trying to find the match in the OS cert store and using that to compare against the hardcoded info rather than comparing the actual signing cert on the binary itself ? 

adding a feedback? to bsmith to take a look at the crypto specific code/functionality of the patch.
Comment 95 Brian R. Bondy [:bbondy] 2011-10-18 09:11:47 PDT
Thanks for the feedback.

> from my reading...
 
Yup.

> bbondy, can you elaborate on why in VerifyCertifcateTrustForFile an error TRUST_E_TIME_STAMP ends up saying the file is validated ?

What I was concerned with here is an update that is installed after a certificate is expired, or a downgrade. If you think it is safest though I can return an error in that case and it would fall back to the normal update process without the service.

>  so overall, my opinion is that we should fail the verification if this error occurs.

OK I'll change that.  I'd rather only use this service update when we're sure it's safe in all cases. 

> one more question, how does the signer cert initially get into the OS cert store ?

I'm not sure about this, but my understadning is that there are several preloaded trusted authorities.  I have limited authenticode knowledge though.
This is also the first time I write any certificate checking code and I relied on several MSDN code samples that I pulled together.

> what's the reasoning behind trying to find the match in the OS cert store and using that to compare against the hardcoded info rather than comparing the actual signing cert on the binary itself

I think by doing it that way we have access to more information.
Comment 96 Robert Kaiser (not working on stability any more) 2011-10-18 10:38:07 PDT
(In reply to Ian Melven :imelven from comment #94)
> from my reading, the code takes a hardcoded issuer, cert name, and signer
> info (program name, publisher and more info link)

Is there some mechanism to allow e.g. Firefox to switch the cert or issuer in case of expiring or the issuer becoming unused (we recently had a case where SeaMonkey's update service needed to switch issuers because the previous one has been phased out)?
Comment 97 Brian R. Bondy [:bbondy] 2011-10-18 10:52:33 PDT
> Is there some mechanism to allow e.g. Firefox to switch the cert or issuer in case of expiring or the issuer becoming unused

There's a new patch coming later today that will store the info for allowed certs in a trusted location in HKLM.  It's already done actually just needs testing. 

So yes we can update that location as we go, and add new certs for other prodcuts there as well.
Comment 98 Ian Melven :imelven 2011-10-18 14:46:36 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #95)
>
> What I was concerned with here is an update that is installed after a
> certificate is expired, or a downgrade. If you think it is safest though I
> can return an error in that case and it would fall back to the normal update
> process without the service.

right, i think an expired cert should probably fail here. as far as downgrades, i'd be concerned about an arbitrary user on the machine forcing a downgrade, which seems bad. i'm not sure forcing the UAC path on a downgrade necessarily helps here though.

> I'm not sure about this, but my understadning is that there are several
> preloaded trusted authorities.  I have limited authenticode knowledge though.
> This is also the first time I write any certificate checking code and I
> relied on several MSDN code samples that I pulled together.

right, the root cert we use to validate the cert will be preloaded in the OS cert store but our signing cert most likely won't, i would think.
 
> I think by doing it that way we have access to more information.

that was my assumption, that we could get the extended fields like the url and such from the cert itself in the store but not necc. from the cert on the binary itself.
Comment 99 Brian R. Bondy [:bbondy] 2011-10-18 15:23:43 PDT
> Re:imelven cert changes:

Fixed both of these in favor of being more restrictive.
Comment 100 Henri Sivonen (:hsivonen) 2011-10-19 04:12:22 PDT
Why are certs involved at all as opposed to hard-coding the Mozilla code signing public key into the service?
Comment 101 Ian Melven :imelven 2011-10-19 06:34:48 PDT
(In reply to Henri Sivonen (:hsivonen) from comment #100)
> Why are certs involved at all as opposed to hard-coding the Mozilla code
> signing public key into the service?

one reason is that it gives us the ability to revoke or change the cert we use while not requiring a respin of the installers and allowing existing installers in the wild to continue functioning.
Comment 102 Brian R. Bondy [:bbondy] 2011-10-19 13:00:27 PDT
> Why are certs involved at all as opposed to hard-coding the Mozilla code signing public key into the service?

Thanks for the suggestion. 
I am not an expert in this area so I will leave divergence from the current Authenticode certificate sign checks patch for the security team to advise further on.
Comment 103 Brian R. Bondy [:bbondy] 2011-10-19 13:30:45 PDT
The install process is working and tested (just by me) now; works as follows:

Administrative users or Limited Users who are elevated when installing:
The optional service install page will show up unless the service is already installed.
If the service is not already installed the user can chose whether or not to install the service.
If the service is already installed the page will be skipped but the service will be upgraded.
If the user is attempting to install an older build when a newer service is already installed, the service will not be replaced. 

Limited users who are not elevated:
- The optional service page will NOT show up and the service will NOT be installed.
Comment 104 Brian R. Bondy [:bbondy] 2011-10-19 13:47:44 PDT
Created attachment 568174 [details] [diff] [review]
Intermediate backup of service related work

This is a backup of the code.  
The final code is still in progress and once done will be split up end of this week for review.

Ian there is some new code related to registry + authenticode checks.
The main code for this is in registrycertificates.cpp.
The other suggestions were updated inside of certificatecheck.cpp.
And then workmonitor.cpp was changed from hard coded info check to using CheckIfBinaryMatchesAllowedCertificates.

Rob if you are interested you can checkout the installer work early.
Comment 105 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:03:43 PDT
Comment on attachment 567667 [details]
Screenshot: New installer step inside Firefox installer

Checkboxes are aligned with the text in the installer. Otherwise it looks good though faaborg still needs to approve the text. I personally think that Optional Recommended Components is a tad awkward.
Comment 106 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:07:14 PDT
Comment on attachment 567740 [details]
A proposed new option for whether or not the product should use the silent update service. Only shows up if service is installed. v2

Please update this since the new UI has already landed on nightly. Also, make sure the checkbox looks a little to the left of where it should be.
Comment 107 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:10:32 PDT
Comment on attachment 567668 [details]
Screenshot: Program files directory showing output files of Maintenance Service Installer

Though it is unlikely it is possible that another binary for a different app might be named maintenanceservice.exe. Might be a good thing to name it mozmaintenanceservice.exe or mozmaintsvc.exe.

What do you think?
Comment 108 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:12:50 PDT
Comment on attachment 567669 [details]
Screenshot: Showing up separately inside Add/Remve programs (icon pending)

Looks good. I think it is best to not bother with putting the version in there like we do with Firefox since the only reason we do that with Firefox is so it is easy to differentiate side by side installs which won't be supported for the maintenance service.
Comment 109 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:31:21 PDT
Comment on attachment 567241 [details] [diff] [review]
Patch for service certificate check on updater.exe and related logic

Instead of feedback reviewing this I am going to try to feedback review "Intermediate backup of service related work" - attachment #568174 [details] [diff] [review]
Comment 110 Brian R. Bondy [:bbondy] 2011-10-19 16:43:05 PDT
Regarding the nsis service installer page feedback:

> Checkboxes are aligned with the text in the installer.

The alignment of the checkbox vs the text is currently consistent with the shortcuts page.

Please let me know your preference:
a) Align the service page checkbox more to the left with the above text, to be inconsistent with the shortcuts page?
b) Change the alignment on both the service page and the shortcuts page to be aligned with the text?
c) Leave as is in that screenshot?
Comment 111 Brian R. Bondy [:bbondy] 2011-10-19 16:50:44 PDT
Created attachment 568253 [details]
Service checkbox; Preferences page option; v3.
Comment 112 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 16:58:01 PDT
Comment on attachment 568253 [details]
Service checkbox; Preferences page option; v3.

Nice! In the past, we've put additional space between elements to denote the different functionality but I don't know if that is the case now so leaving that to faaborg.
Comment 113 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 17:01:17 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #110)
> Regarding the nsis service installer page feedback:
> 
> > Checkboxes are aligned with the text in the installer.
> 
> The alignment of the checkbox vs the text is currently consistent with the
> shortcuts page.
> 
> Please let me know your preference:
> a) Align the service page checkbox more to the left with the above text, to
> be inconsistent with the shortcuts page?
> b) Change the alignment on both the service page and the shortcuts page to
> be aligned with the text?
> c) Leave as is in that screenshot?
That's a bumer. :)

The main reason we went with aligning the checkbox with the installer is to allow more space for the locales that need it. I'll leave that to faaborg but I *think* I prefer going with aligning them and just fixing the shortcuts page.
Comment 114 Brian R. Bondy [:bbondy] 2011-10-19 17:36:03 PDT
Created attachment 568265 [details]
Showing up separately inside Add/Remve programs. v2.

Here's a better screenshot of the add/remove programs showing all fields and the icon.
Comment 115 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-19 17:37:15 PDT
Comment on attachment 568265 [details]
Showing up separately inside Add/Remve programs. v2.

Looks good!
Comment 116 Ian Melven :imelven 2011-10-20 11:15:10 PDT
Comment on attachment 568174 [details] [diff] [review]
Intermediate backup of service related work

in DoesCertificateMatch, it seems a little strange that if no infoToMatch.issuer and infoToMatch.name are passed it, TRUE is returned. from the calling code it looks like this probably can't happen since we error if we can't get the signer info previously. 

the new code looks good to me, it goes through the allowed signing certs we have stored in the registry and returns TRUE on finding the first one that is successfully verified. The registry key used is under HKLM so non-Admin users should not have access to it by default.
Comment 117 Brian R. Bondy [:bbondy] 2011-10-21 09:21:42 PDT
Created attachment 568674 [details] [diff] [review]
All changes in one for easy applying
Comment 118 Brian R. Bondy [:bbondy] 2011-10-21 09:22:42 PDT
Created attachment 568676 [details] [diff] [review]
Patch 1 - Base service code
Comment 119 Brian R. Bondy [:bbondy] 2011-10-21 09:24:17 PDT
Created attachment 568677 [details] [diff] [review]
Patch 2 - Main service logic
Comment 120 Brian R. Bondy [:bbondy] 2011-10-21 09:25:34 PDT
Created attachment 568678 [details] [diff] [review]
Patch 3 - Code for installing the service
Comment 121 Brian R. Bondy [:bbondy] 2011-10-21 09:26:44 PDT
Created attachment 568679 [details] [diff] [review]
Patch 4 - Certificate check code inside service
Comment 122 Brian R. Bondy [:bbondy] 2011-10-21 09:28:04 PDT
Created attachment 568680 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code
Comment 123 Brian R. Bondy [:bbondy] 2011-10-21 09:29:09 PDT
Created attachment 568682 [details] [diff] [review]
Patch 6 - Firefox preferences update page changes
Comment 124 Brian R. Bondy [:bbondy] 2011-10-21 09:33:07 PDT
Created attachment 568684 [details] [diff] [review]
Patch 7 - RAII base helpers
Comment 125 Brian R. Bondy [:bbondy] 2011-10-21 09:34:25 PDT
Created attachment 568685 [details] [diff] [review]
Patch 8 - NSPR wide string logging support on Windows
Comment 126 Brian R. Bondy [:bbondy] 2011-10-21 09:36:03 PDT
Created attachment 568686 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes
Comment 127 Brian R. Bondy [:bbondy] 2011-10-21 09:37:33 PDT
Created attachment 568688 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade
Comment 128 Brian R. Bondy [:bbondy] 2011-10-21 09:38:43 PDT
Created attachment 568689 [details] [diff] [review]
Patch 11 - New maintenance service installer
Comment 129 Brian R. Bondy [:bbondy] 2011-10-21 09:40:14 PDT
Created attachment 568690 [details] [diff] [review]
Patch 12 - Simple SC plugin inclusion
Comment 130 Brian R. Bondy [:bbondy] 2011-10-21 09:41:15 PDT
Created attachment 568691 [details] [diff] [review]
Patch 13 - Moving the update check after init
Comment 131 Brian R. Bondy [:bbondy] 2011-10-21 09:42:11 PDT
Created attachment 568693 [details] [diff] [review]
Patch 14 - Build process
Comment 132 Brian R. Bondy [:bbondy] 2011-10-21 09:45:04 PDT
All the patches thus far are uploaded.
The task isn't 100% complete, but it is working and ready for review while I finish up minor things in a couple follow up patches. 

I'm building a release config installer right now for testing as well which I'll upload later today.  It will include a signed updater.exe from FF7 so should work out of the box.
Comment 133 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-21 14:41:31 PDT
Some questions as part of the ui-review:

Does this windows service do anything other than update the application, do we have any plans for hang detection, or performance telemetry, or anything else? (trying to get a sense of if we should call it an update service since that is a more literal name).  Does it only update Firefox?

Where in the install wizard does "setup optional components" appear?  You can reference the numbers here: https://bug513414.bugzilla.mozilla.org/attachment.cgi?id=397421

I'm assuming we didn't group "use a background service to install updates" under automatic install because we might still use the service to apply manual installs?  At the very least we should perhaps put it under the button for update history so that it doesn't visually group as one of the top level options (3 radio buttons and a checkbox accidentally parses as 4 radio buttons)
Comment 134 Ian Melven :imelven 2011-10-21 15:09:44 PDT
is the service executable going to be signed also ?
Comment 135 Brian R. Bondy [:bbondy] 2011-10-21 17:57:39 PDT
> is the service executable going to be signed also ?

I think all EXE and DLL get signed, but I'm not sure if I need to add another bug specifically for the service.  Should I add a bug for releng for that Rob? (that's not important for me in nightly but just in general)
Comment 136 Brian R. Bondy [:bbondy] 2011-10-21 18:04:46 PDT
> Does this windows service do anything other than update the application, 
> do we have any plans for hang detection, or performance telemetry, or anything else?

Yes we have plans for other things, but for the initial release it will only be for silent updates without UAC prompts.

> Does it only update Firefox?

Initially it will only be launched for Firefox but it has support initially for other programs as well.  It will be used by more than Firefox.


> Where in the install wizard does "setup optional components" appear?  
> You can reference the numbers here: 
> https://bug513414.bugzilla.mozilla.org/attachment.cgi?id=397421

It would appear as a new step 5.5 just before the shortcuts page (only if the user selects custom).

> I'm assuming we didn't group "use a background service to install updates" 
> under automatic install because we might still use the service to apply manual installs?  

That is correct.

> At the very least we should perhaps put it under the button for update history so that it doesn't visually 
> group as one of the top level options (3 radio buttons and a checkbox accidentally parses as 4 radio buttons)

Sounds good, I was uncomfortable having it right under the radio buttons as well.
I'll move the checkbox under the update history button.
Comment 137 Brian R. Bondy [:bbondy] 2011-10-21 19:10:06 PDT
You can see a working demo on your computer here:
http://people.mozilla.com/~bbondy/InstallerWithService.zip

Instructions:
0. If UAC is off, turn UAC on with default value. Reboot.
1. Install the installer firefox-10.0a1.en-US.win32.installer.exe
2. Stage the update by extracting Nightly.zip into:
C:\Users\<username>\AppData\Local\Mozilla\Firefox
After extracting, your path should look like:
C:\Users\<username>\AppData\Local\Mozilla\Firefox\Nightly\updates\0
3. Do something like mark firefox.exe as hidden so you will know it got replaced.
4. Open Firefox to begin the update.

No UAC prompt should be shown. You should see a progress bar currently but that will probably be disabled later.

If this doesn't work, enable NSPR logging and get back to me with the log file.
For more info on NSPR logging, see the Logging section at: http://www.brianbondy.com/mozilla/cheatsheet/
The NSPR module name is: nsFirefoxService
Comment 138 Zlip792 2011-10-21 20:50:46 PDT
Does not want to interrupt you developers, but isn't "Mozilla Update" better name than "Maintenance Service"?
Also Keep up the good work!
Comment 139 alanjstr 2011-10-21 21:14:56 PDT
The service has the potential to do more than just update, such as other maintenance tasks.
Comment 140 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-22 06:32:48 PDT
Comment on attachment 567667 [details]
Screenshot: New installer step inside Firefox installer

Not ideal for us to add an extra step, but we'll be landing the stub installer at some point in the future.  so ui-review+ on the assumption that this is going to eventually get collapsed into a single options dialog in the stub installer.
Comment 141 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-22 06:34:56 PDT
Comment on attachment 568253 [details]
Service checkbox; Preferences page option; v3.

looks good, just need to move down to below the update history button to break the  visual grouping (gestalt principal of proximity for the cog sci geeks playing along at home)
Comment 142 Alex Faaborg [:faaborg] (Firefox UX) 2011-10-22 06:35:46 PDT
Comment on attachment 568265 [details]
Showing up separately inside Add/Remve programs. v2.

This is fine for now, we're going to want to roll out a new icon for the service in the future, but we obviously don't need to block on that.
Comment 143 Brian R. Bondy [:bbondy] 2011-10-23 08:41:22 PDT
Created attachment 568949 [details]
Screenshot: Services checkbox moved under show update history button

Was already +'ed by faaborg so just marking as +.  Including the updated screenshot just in case there is further feedback.
Comment 144 Brian R. Bondy [:bbondy] 2011-10-23 08:43:54 PDT
Created attachment 568950 [details] [diff] [review]
Patch 6 - Firefox preferences update page changes. v2.

Moved checkbox under update history button.
Comment 145 Brian R. Bondy [:bbondy] 2011-10-23 08:48:01 PDT
Re: Ian:
> in DoesCertificateMatch, it seems a little strange that if no infoToMatch.issuer
> and infoToMatch.name are passed it, TRUE is returned. 
> from the calling code it looks like this probably can't happen since we
> error if we can't get the signer info previously

The reasoning was because you can specify any of those 5 criteria for people who want to add cert checking but sign in different ways than we do.  It was more recently updated to NULL or empty string returns TRUE.
Comment 146 Brian R. Bondy [:bbondy] 2011-10-23 08:51:57 PDT
re: rs
> Though it is unlikely it is possible that another binary for a different 
> app might be named maintenanceservice.exe. Might be a good thing to name 
> it mozmaintenanceservice.exe or mozmaintsvc.exe.

The same name wouldn't cause a problem for the exe name.
The svcname is MozillaMaintenance and the friendly name is Mozilla Maintenance Service.  Only the svcname could conflict with other services and not the exe name.  So I think we should just leave as is.  But if you prefer the moz prefix I can add that.
Comment 147 Ian Melven :imelven 2011-10-23 10:33:56 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #145)
> Re: Ian:
> > in DoesCertificateMatch, it seems a little strange that if no infoToMatch.issuer
> > and infoToMatch.name are passed it, TRUE is returned. 
> > from the calling code it looks like this probably can't happen since we
> > error if we can't get the signer info previously
> 
> The reasoning was because you can specify any of those 5 criteria for people
> who want to add cert checking but sign in different ways than we do.  It was
> more recently updated to NULL or empty string returns TRUE.

makes sense, thanks for the explanation.
Comment 148 Brian R. Bondy [:bbondy] 2011-10-23 18:48:53 PDT
Created attachment 568993 [details] [diff] [review]
Patch 2 - Main service logic. v2

- Wasn't stopping the service after the update, fixed.
- Now wait for return code of updater.exe before returning for better error handling and also better logging.
- Added compile time flag DISABLE_SERVICE_AUTHENTICODE_CHECK for when running on try tests
- Some code cleanup / refactoring
Comment 149 Brian R. Bondy [:bbondy] 2011-10-24 10:36:51 PDT
Created attachment 569103 [details] [diff] [review]
Patch 15 - Added support for Limited user account upgrades and improved handling of tokens

- Added new UACHelper class with new code for determining token types and user types
- Added handling for ensuring the token we're using is an elevated token
- Added handling for limited user account upgrades
Comment 150 Brian R. Bondy [:bbondy] 2011-10-24 10:57:24 PDT
Created attachment 569109 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v2.

Summary of changes since v1 of the patch:
- Maintenance service page aligned with text to the left instead of having 15px margin 
- Shortcuts page checkboxes aligned with text to the left instead of having 15px margin
- Installation type radio buttons aligned with text to the left instead of having 15px margin.  
  Associated text for each radio button moved from 30px to 15px.
Comment 151 Brian R. Bondy [:bbondy] 2011-10-24 11:02:48 PDT
Created attachment 569110 [details]
Screenshot of affected pages for installer alignment changes

Radio button and checkbox alignment changes.

For context of why this change was done, please see:
comment 105, comment 110, comment 113, and comment 150.
Comment 152 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 12:14:14 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #146)
> re: rs
> > Though it is unlikely it is possible that another binary for a different 
> > app might be named maintenanceservice.exe. Might be a good thing to name 
> > it mozmaintenanceservice.exe or mozmaintsvc.exe.
> 
> The same name wouldn't cause a problem for the exe name.
> The svcname is MozillaMaintenance and the friendly name is Mozilla
> Maintenance Service.  Only the svcname could conflict with other services
> and not the exe name.  So I think we should just leave as is.  But if you
> prefer the moz prefix I can add that.
Understood... the reason I asked for this is *if* we ever need to deal with the process from the installer it would be even more likely to be unique and as a side benefit it is easier to spot and associate to Mozilla in the task manager whereas maintenanceservice.exe is so generic that people would have to investigate to figure out what the process is.
Comment 153 Brian R. Bondy [:bbondy] 2011-10-25 12:16:10 PDT
Gotcha ok, I'll do that change towards the end as it'll need a refresh of most of the patches.
Comment 154 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 12:23:36 PDT
Comment on attachment 568676 [details] [diff] [review]
Patch 1 - Base service code

>diff --git a/toolkit/components/maintenanceservice/maintenanceservice.cpp b/toolkit/components/maintenanceservice/maintenanceservice.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/maintenanceservice.cpp
>@@ -0,0 +1,285 @@
>+/* ***** 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 Application Update service.
s/Application Update service/Mozilla Maintenance service/.
Replace here and elsewhere in the license headers

>...
>+int wmain(int argc, WCHAR **argv)
>+{
>+#ifdef PR_LOGGING
>+  if (!gServiceLog) {
>+    gServiceLog = PR_NewLogModule("nsFirefoxService");
s/nsFirefoxService/nsMaintenanceService/
Here and elsewhere

>+  }
>+#endif
>+
>+  // If command-line parameter is "install", install the service
>+  // or upgrade if already installed.
>+  // If command-line parameter is "upgrade", upgrade the service
>+  // but do not install it if it is not already installed.
>+  // If command line parameter is "uninstall", uninstall the service.
>+  // Otherwise, the service is probably being started by the SCM.
>+  if (lstrcmpi(argv[1], L"install") == 0) {
>+    
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Installing service..."));
nit: for consistency, start code immediately after conditional here and elsewhere.

>+
>+DWORD WINAPI StartMonitoringThreadProc(LPVOID param) 
>+{
>+  StartDirectoryChangeMonitor();
>+  return 0;
>+}
>+
>+// Entry point for the service
>+void WINAPI SvcMain(DWORD dwArgc, LPWSTR *lpszArgv)
>+{
>+#ifdef PR_LOGGING
>+  if (!gServiceLog) {
>+    gServiceLog = PR_NewLogModule("nsFirefoxService");
s/nsFirefoxService/nsMaintenanceService/

>+  }
>+#endif
>+
>+  // Register the handler function for the service
>+  gSvcStatusHandle = RegisterServiceCtrlHandlerW(SVC_NAME, SvcCtrlHandler);
>+  if (!gSvcStatusHandle) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("RegisterServiceCtrlHandler failed (%d)", ::GetLastError()));
>+    return; 
>+  } 
>+
>+  // These SERVICE_STATUS members remain as set here
>+  gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
>+  gSvcStatus.dwServiceSpecificExitCode = 0;
>+
>+  // Report initial status to the SCM
>+  ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
>+
>+  // Perform service-specific initialization and work.
>+  SvcInit(dwArgc, lpszArgv);
>+}
>+
>+// Service initialization
>+void SvcInit(DWORD dwArgc, LPWSTR *lpszArgv)
>+{
>+  // Be sure to periodically call ReportSvcStatus() with 
>+  // SERVICE_START_PENDING. If initialization fails, call
>+  // ReportSvcStatus with SERVICE_STOPPED.
This comment is vague especially regarding what periodically actually means.

The second half regarding SERVICE_STOPPED should be before the call to ReportSvcStatus when initialization fails and it should note that initialization has failed.

>+
>+  // Create an event. The control handler function, SvcCtrlHandler,
>+  // signals this event when it receives the stop control code.
>+  ghSvcStopEvent = CreateEvent(
>+    NULL,    // default security attributes
>+    TRUE,    // manual reset event
>+    FALSE,   // not signaled
>+    NULL);   // no name
>+
>+  if (NULL == ghSvcStopEvent) {
>+    ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
Is NO_ERROR correct for this case?

>+    return;
>+  }
>+
>+  DWORD threadID;
>+  HANDLE thread = ::CreateThread(NULL, 0, StartMonitoringThreadProc, 0, 0, &threadID);
>+
>+  // Report running status when initialization is complete.
>+  ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
>+
>+
>+  // Perform work until service stops.
>+  for(;;) {
>+    // Check whether to stop the service.
>+    WaitForSingleObject(ghSvcStopEvent, INFINITE);
>+
>+    WCHAR stopFilePath[MAX_PATH +1];
>+    if (!GetUpdateDirectoryPath(stopFilePath)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not obtain update directory path, terminating thread forcefully."));
>+      TerminateThread(thread, 1);
>+    }
>+
>+    gServiceStopping = true;
>+    if (!::PathAppendSafe(stopFilePath, L"stop")) {
Please add a comment regarding the purpose of the stop file.

>+      TerminateThread(thread, 2);
>+    }
>+    HANDLE stopFile = CreateFile(L"stop", GENERIC_READ, 0, 
>+                                 NULL, CREATE_ALWAYS, 0, NULL);
stopFilePath?

>+    if (stopFile == INVALID_HANDLE_VALUE) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create stop file, terminating thread forcefully."));
>+      TerminateThread(thread, 3);
>+    } else {
>+      CloseHandle(stopFile);
>+      DeleteFile(stopFilePath);
>+    }
>+
>+    ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
>+    return;
>+  }
>+}
>+
>+// Sets the current service status and reports it to the SCM.
>+// Parameters:
>+//   dwCurrentState - The current state (see SERVICE_STATUS)
>+//   dwWin32ExitCode - The system error code
>+//   dwWaitHint - Estimated time for pending operation, 
>+//     in milliseconds
Please use a javadoc style comment

>+void ReportSvcStatus(DWORD dwCurrentState, 
>+                     DWORD dwWin32ExitCode, 
>+                     DWORD dwWaitHint)
>+{
>+  static DWORD dwCheckPoint = 1;
>+
>+  // Fill in the SERVICE_STATUS structure.
>+  gSvcStatus.dwCurrentState = dwCurrentState;
>+  gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
>+  gSvcStatus.dwWaitHint = dwWaitHint;
>+
>+  if (SERVICE_START_PENDING == dwCurrentState) {
>+    gSvcStatus.dwControlsAccepted = 0;
>+  } else {
>+    gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
>+  }
>+
>+  if ((SERVICE_RUNNING == dwCurrentState) ||
>+      (SERVICE_STOPPED == dwCurrentState)) {
>+    gSvcStatus.dwCheckPoint = 0;
>+  } else {
>+    gSvcStatus.dwCheckPoint = dwCheckPoint++;
>+  }
>+
>+  // Report the status of the service to the SCM.
>+  SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
>+}
>+
>+// Called by SCM whenever a control code is sent to the service
>+// using the ControlService function.
>+void WINAPI SvcCtrlHandler(DWORD dwCtrl)
>+{
>+  // Handle the requested control code. 
>+  switch(dwCtrl) {
>+  case SERVICE_CONTROL_STOP: 
>+    ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
>+
>+    // Signal the service to stop.
>+    SetEvent(ghSvcStopEvent);
>+    ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
>+    return;
nit: break;

>+  case SERVICE_CONTROL_INTERROGATE: 
>+    break; 
>+  default: 
>+    break;
>+  } 
>+}
Comment 155 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 12:33:59 PDT
Comment on attachment 568676 [details] [diff] [review]
Patch 1 - Base service code

>diff --git a/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest b/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/maintenanceservice.exe.manifest
>@@ -0,0 +1,34 @@
>+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
>+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
>+<assemblyIdentity
>+        version="1.0.0.0"
>+        processorArchitecture="*"
>+        name="Updater"
Please change Updater to something like MaintenanceService

>+        type="win32"
>+/>
>+<description>Updater</description>
>+<dependency>
>+        <dependentAssembly>
>+                <assemblyIdentity
>+                        type="win32"
>+                        name="Microsoft.Windows.Common-Controls"
>+                        version="6.0.0.0"
>+                        processorArchitecture="*"
>+                        publicKeyToken="6595b64144ccf1df"
>+                        language="*"
>+                />
Does this really need common controls? I think this is just a copy / paste inclusion from another manifest.

>+        </dependentAssembly>
>+</dependency>
>+<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
>+  <ms_asmv3:security>
>+    <ms_asmv3:requestedPrivileges>
>+      <ms_asmv3:requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
Out of curiousity, could this safely be asInvoker instead? If so, is there any reason not to just use asInvoker?

>+    </ms_asmv3:requestedPrivileges>
>+  </ms_asmv3:security>
>+</ms_asmv3:trustInfo>
>+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
>+    <application>
>+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
Why isn't Vista included here?

>+    </application>
>+  </compatibility>
>+</assembly>
Comment 156 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 13:01:10 PDT
Comment on attachment 568676 [details] [diff] [review]
Patch 1 - Base service code

>diff --git a/toolkit/components/maintenanceservice/servicebase.cpp b/toolkit/components/maintenanceservice/servicebase.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/servicebase.cpp
>...

>+#include "servicebase.h"
>+
>+// Shared code with Firefox and updater.exe
I don't think this comment adds much value but if you want it go with something like
//Shared code between applications and updater.exe

>+#include "nsWindowsRestart.cpp"
>diff --git a/toolkit/components/maintenanceservice/servicebase.h b/toolkit/components/maintenanceservice/servicebase.h
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/servicebase.h
>@@ -0,0 +1,45 @@
>+/* ***** 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 Application Update service base code.
Mozilla Maintenance service.

Looks good though I'd like another quick pass at this before r+'ing
Comment 157 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 14:39:20 PDT
Comment on attachment 568993 [details] [diff] [review]
Patch 2 - Main service logic. v2

>diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp b/toolkit/components/maintenanceservice/workmonitor.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
>@@ -0,0 +1,417 @@
>+/* ***** 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 Application Update service fs monitoring.
s/Application Update service fs/Maintenance service file system monitoring/

>...
>+static const int FILE_SHARE_ALL = FILE_SHARE_READ | 
>+                                  FILE_SHARE_WRITE | 
>+                                  FILE_SHARE_DELETE;
>+
>+// Wait 5 minutes for an update operation to run at most.
>+static const int TIME_TO_WAIT_ON_UPDATER = 5 * 60 * 1000;
note: we have had bug reports from people that update an install on a share with a very slow connection. I think this timeout should be sufficient... just calling it out.

>+
>+HANDLE QueryUserToken(DWORD sessionID)
>+{
>+  HMODULE module = LoadLibraryW(L"wtsapi32.dll");
>+  HANDLE token = NULL;
>+  LPWTSQueryUserToken wtsQueryUserToken = (LPWTSQueryUserToken)GetProcAddress(module, "WTSQueryUserToken");
>+  if (wtsQueryUserToken) {
>+    wtsQueryUserToken(sessionID, &token);
>+  }
>+  FreeModule(module);
>+  return token;
>+}
>+
>+HANDLE GetElevatedTokenFromToken(HANDLE token) 
>+{
>+  // Magic below...
>+  // UAC creates 2 tokens.  One is the restricted token which we have.
>+  // the other is the UAC elevated one. Since we are running as a service
>+  // as the system account we have access to both.
nit: this seems like it would be better to have as a javadoc function comment
http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

>+  TOKEN_LINKED_TOKEN tlt;
>+  HANDLE hNewLinkedToken = NULL;
>+  DWORD len;
>+  if(::GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, 
>+                           &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
>+    token = tlt.LinkedToken;
>+    hNewLinkedToken = token;
>+  }
>+  return hNewLinkedToken;
>+}
>+
>+// Returns TRUE if the process was run and returned 0
nit: always use javadoc style comments for documenting functions

>+BOOL StartElevatedProcessInSession(DWORD sessionID, LPCWSTR appToStart, LPCWSTR workingDir, LPWSTR cmdLine)
>+{
>+  BOOL processStarted;
>+  DWORD myProcessID = GetCurrentProcessId();
>+  DWORD mySessionID;
>+  ProcessIdToSessionId(myProcessID, &mySessionID);
>+
>+  STARTUPINFO si = {0};
>+  si.cb = sizeof(STARTUPINFO);
>+  si.lpDesktop = L"winsta0\\Default";
>+  PROCESS_INFORMATION pi = {0};
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Starting process in an elevated session.  Service session ID: %d; Requested session ID: %d", mySessionID, sessionID));
Attempting to start process in an elevated session

There are a few lines greater than 80 that could fairly easily be under 80

>+
>+  // We must be running this from an older OS than Vista or else
>+  // we are running this code as a user process instead of via the service.
>+  // In that case just execute the process in the normal way.
>+  if (mySessionID == sessionID) {
>+    processStarted = ::CreateProcessW(appToStart, cmdLine, 
>+                                      NULL, NULL, FALSE, 
>+                                      CREATE_DEFAULT_ERROR_MODE | 
>+                                      CREATE_UNICODE_ENVIRONMENT, 
>+                                      NULL, workingDir, &si, &pi);
>+    DWORD lastError = ::GetLastError();
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not create process as current user, last error: %d; appToStart: %S; cmdLine: %S", 
>+       lastError, appToStart, cmdLine));
This is always logged... should be conditional
if (!processStarted) {
...
}

>+
nit: extra blank line

>+  } else {
>+    HANDLE userToken = QueryUserToken(sessionID);
>+
>+    // Check if we are running Vista or later.
>+    OSVERSIONINFO osInfo;
>+    osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
>+    if (GetVersionEx(&osInfo) && osInfo.dwMajorVersion >= 6) {
>+
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Windows Vista or later detected, attempting to find linked token."));
>+
>+      HANDLE elevatedToken = GetElevatedTokenFromToken(userToken);
>+      if (elevatedToken) {
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Elevated token was found, using it."));
>+
>+        ::CloseHandle(userToken); 
>+        userToken = elevatedToken;
>+      } else {
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("An elevated token was not found, will use regular token."));
>+      }
>+    }
>+
>+    // Create an environment block for the process we're about to start using the
>+    // user's token.
>+    LPVOID lpvEnv(NULL);
>+    if (!::CreateEnvironmentBlock(&lpvEnv, userToken, TRUE)) {
>+      
nit: extra blank line here and else where
In case you haven't seen it
https://developer.mozilla.org/En/Mozilla_Coding_Style_Guide

>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create an environment block, setting it to NULL."));
>+
>+      lpvEnv = NULL;
>+    }
>+
>+    // Launch the process in the specified working directory application and command line
>+    processStarted = ::CreateProcessAsUserW(userToken, appToStart, cmdLine, 
>+                                            NULL, NULL, FALSE,
>+                                            CREATE_DEFAULT_ERROR_MODE | 
>+                                            CREATE_UNICODE_ENVIRONMENT, 
>+                                            lpvEnv, workingDir, &si, &pi);
>+    if (!processStarted) {
>+      DWORD lastError = ::GetLastError();
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create process with user token, last error: %d; appToStart: %S; cmdLine: %S", 
>+         lastError, appToStart, cmdLine));
>+    }
>+    ::CloseHandle(userToken);
>+  }
>+
>+  if (processStarted) {
>+
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Process was started... waiting on result.")); 
>+
>+    // Wait for the updater process to finish
>+    ::WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
>+
>+    DWORD returnCode;
>+    if (!GetExitCodeProcess(pi.hProcess, &returnCode)) {
nit: consistency :: - please check other usage as well.
if (!::GetExitCodeProcess(pi.hProcess, &returnCode)) {

>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Process finished but could not obtain return code.")); 
>+      return FALSE;
>+    }
>+
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Process finished with return code %d.", returnCode)); 
>+
>+    // updater returns 0 if successful.
>+    return returnCode == 0;
>+  } else {
>+    // The process couldn't be started.
>+    return FALSE;
>+  }
>+}
>+
>+// Returns true if we want the service to stop
>+// For now this is every time we process a .mz file.
nit: javadoc comment - here and else where as applicable

>+bool ProcessWorkItem(LPCWSTR monitoringBasePath, FILE_NOTIFY_INFORMATION &notifyInfo)
>+{
>+  int filenameLength = notifyInfo.FileNameLength / 
>+                       sizeof(notifyInfo.FileName[0]); 
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Processing new command meta file: %S", notifyInfo.FileName));
>+
>+  // When the file is ready for processing it will be renamed 
>+  // to have a .mz extension
>+  bool haveWorkItem = notifyInfo.Action == FILE_ACTION_RENAMED_NEW_NAME && 
>+                      notifyInfo.FileNameLength > 3 && 
>+                      notifyInfo.FileName[filenameLength - 3] == L'.' &&
>+                      notifyInfo.FileName[filenameLength - 2] == L'm' &&
>+                      notifyInfo.FileName[filenameLength - 1] == L'z';
>+  if (!haveWorkItem) {
>+    // We don't have a work item, keep looking
>+    return false;
consistency nit: use uppercase FALSE

>+  }
>+
>+  WCHAR fullMetaUpdateFilePath[MAX_PATH+1];
nit: MAX_PATH + 1 here and elsewhere

>+  wcscpy(fullMetaUpdateFilePath, monitoringBasePath);
>+
>+  // We only support file paths in monitoring directories that are MAX_PATH chars or less.
>+  if (!PathAppendSafe(fullMetaUpdateFilePath, notifyInfo.FileName)) {
>+    // Cannot process update, skipfileSize it.
>+    return true;
nit: consistency - uppercase TRUE

>+  }
>+
>+  nsAutoHandle metaUpdateFile = ::CreateFile(fullMetaUpdateFilePath, GENERIC_READ, 
>+                                             FILE_SHARE_ALL, NULL, OPEN_EXISTING,
>+                                             0, NULL);
>+  if (INVALID_HANDLE_VALUE == metaUpdateFile) {
>+
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not open command meta file: %S", notifyInfo.FileName));
>+    return true;
>+  }
>+
>+  DWORD fileSize = GetFileSize(metaUpdateFile, NULL);
>+  DWORD sessionID = 0;
>+  // The file should be in WIDECHAR's so if it's of odd size it's
s/WIDECHAR's/wide characters/

>+  // an invalid file
>+  const int kSanityCheckFileSize = 1024*64;
nit: 1024 * 64

>+  if (fileSize == INVALID_FILE_SIZE || 
>+      (fileSize %2) != 0 ||
>+      fileSize > kSanityCheckFileSize ||
>+      fileSize < sizeof(DWORD)) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain file size or an improper file size was encountered for command meta file: %S", notifyInfo.FileName));
>+    return true;
>+  }
>+
>+  // The first 4 bytes are for the process ID
>+  DWORD read1;
nit: use descriptive names here and in the other instances of read#

>+  BOOL result1 = ::ReadFile(metaUpdateFile, &sessionID, 
>+                            sizeof(DWORD), &read1, NULL);
>+  fileSize -= sizeof(DWORD);
>+
>+  // The next MAX_PATH wchar's are for the app to start
>+  WCHAR appToStart[MAX_PATH + 1];
>+  ZeroMemory(appToStart, sizeof(appToStart));
>+  DWORD read2;
>+  BOOL result2 = ::ReadFile(metaUpdateFile, appToStart, 
>+                            MAX_PATH * sizeof(WCHAR), &read2, NULL);
How about
BOOL result;
result |= ::ReadFile
result |= ::ReadFile
etc.

>+  fileSize -= read2;
>+
>+  // The next MAX_PATH wchar's are for the app to start
>+  WCHAR workingDirectory[MAX_PATH + 1];
>+  ZeroMemory(workingDirectory, sizeof(workingDirectory));
>+  DWORD read3;
>+  BOOL result3 = ::ReadFile(metaUpdateFile, workingDirectory, 
>+                            MAX_PATH * sizeof(WCHAR), &read3, NULL);
>+  fileSize -= read3;
>+
>+  // + 2 for wide char termination
>+  nsAutoArrayPtr<char> cmdlineBuffer = new char[fileSize + 2];
>+  DWORD cmdLineBufferRead;
>+  BOOL result4 = ::ReadFile(metaUpdateFile, cmdlineBuffer, 
>+                            fileSize, &cmdLineBufferRead, NULL);
>+  fileSize -= cmdLineBufferRead;
>+
>+  if (!result1 || !result2 || !result3 || !result4 ||
>+      read1 != sizeof(DWORD) || 
>+      read2 != MAX_PATH * sizeof(WCHAR) ||
>+      read3 != MAX_PATH * sizeof(WCHAR) ||
>+      fileSize != 0) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not read command data for command meta file: %S", notifyInfo.FileName));
>+    return true;
>+  }
>+  cmdlineBuffer[cmdLineBufferRead] = L'\0';
>+  cmdlineBuffer[cmdLineBufferRead + 1] = '\0';
>+  WCHAR *cmdlineBufferWide = reinterpret_cast<WCHAR*>(cmdlineBuffer.get());
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("An update command was detected and is being processed for command meta file: %S", notifyInfo.FileName));
>+
>+  // Validate the certificate of the app the user wants us to start.
>+  // Also check to make sure the certificate is trusted.
>+#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
>+  if (CheckIfBinaryMatchesAllowedCertificates(appToStart)) {
>+#endif
>+    if (!StartElevatedProcessInSession(sessionID, appToStart, workingDirectory, cmdlineBufferWide)) {
>+      // TODO: Need to tell the app that we couldn't run the update so 
>+      // the app will do the update the old way. rs do you think via update status file?
>+      // I don't want it to keep using the service in case it keeps getting 
>+      // the same error on each launch.
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Error running process in session %d.  Last error: %d", sessionID, GetLastError()));
>+    } 
>+#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
Seems like this should be for the else branch below

>+    else {
>+      // The update was executed and run successfully
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Updater.exe was launched and run successfully"));
nit: updater.exe

>+    }
>+  } else {
>+    // TODO: Need to tell the app that we couldn't run the update
>+    // because the cert is not valid.  The app will do the update 
>+    // itself the old way.  rs do you think via update status file?
>+    // I don't want it to keep using the service in case it keeps getting 
>+    // the same error on each launch.
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not start process due to certificate check.  Last error: %d", GetLastError()));
For the TODO's above a new error code for the update.status file should suffice here
http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/readstrings/errors.h

I'd like to separate the readstrings error codes and the updater error codes but not in this bug.

>+  } 
>+#endif
>+
>+  // We processed a work item, whether or not it was successful.
>+  return true;
>+}
>+
>+
>+BOOL StartDirectoryChangeMonitor() 
>+{
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Starting directory change monitor..."));
>+
>+  // Init the update directory path and ensure it exists.
>+  // Example: C:\programData\Mozilla\Firefox\updates\[channel]
>+  // The channel is not included here as we want to monitor the base directory
>+  WCHAR programData[MAX_PATH+1];
perhaps name this updateData or something similar to avoid confusion with c:\programdata\

>+  if (!GetUpdateDirectoryPath(programData)) {
>+
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain update directory path"));
>+
>+    return FALSE;
>+  }

>diff --git a/toolkit/components/maintenanceservice/workmonitor.h b/toolkit/components/maintenanceservice/workmonitor.h
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/workmonitor.h
>@@ -0,0 +1,41 @@
>+/* ***** 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 Application Update service fs monitoring.
s/Application Update service fs/Maintenance service file system monitoring/

Also, looks good though I need to dive into this one a bit more.
Comment 158 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-25 15:18:42 PDT
Comment on attachment 568678 [details] [diff] [review]
Patch 3 - Code for installing the service

I need to dive into this one a bit deeper but I wanted to give you what I have so far so you have a chance to address the version check comment before I am done.

>diff --git a/toolkit/components/maintenanceservice/serviceinstall.cpp b/toolkit/components/maintenanceservice/serviceinstall.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
>@@ -0,0 +1,382 @@
>+/* ***** 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 Application Update service.
s/Application Update service/Maintenance service/

Not going to call this out in further cases nut please change here and else where.


>+#include <windows.h>
>+#include <aclapi.h>
>+#include <stdlib.h>
>+
>+#include <nsAutoPtr.h>
>+#include <nsAutoServiceHandle.h>
>+#include <nsMemory.h>
>+
>+#include "serviceinstall.h"
>+#include "servicebase.h"
>+#include "shellapi.h"
>+
>+#pragma comment(lib, "version.lib")
>+#define DEFERRED_DELETE_TIMEOUT_SECONDS 10
>+
>+static bool GetVersionNumberFromPath(LPWSTR path, DWORD &version) 
>+{
>+  DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0);
>+  nsAutoArrayPtr<char> fileVersionInfo = new char[fileVersionInfoSize];
>+  if (!GetFileVersionInfoW(path, 0, 
>+    fileVersionInfoSize, fileVersionInfo.get())) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not obtain file info of old service.  (%d)", GetLastError()));
>+      return false;
>+  }
>+
>+  VS_FIXEDFILEINFO *fixedFileInfo = 
>+    reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get());
>+  UINT size;
>+  if (!VerQueryValueW(fileVersionInfo.get(), L"\\", 
>+    reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not query file version info of old service.  (%d)", GetLastError()));
>+      return false;
>+  }  
>+
>+  version = fixedFileInfo->dwFileVersionLS;
I don't think build number will be enough here. Need something like CVersionInfo since it supports full version comparison.
http://www.codeguru.com/cpp/w-p/win32/versioning/article.php/c4539

There is also the following which compares version strings
http://mxr.mozilla.org/mozilla-central/source/xpcom/glue/nsVersionComparator.cpp

>+  return true;
>+}
>+
>+// Return value does not indicate anything except that the 
>+// operation was attempted
>+DWORD DeferredDeletePath(LPCWSTR pathToDelete) 
>+{
>+  WCHAR deferredSlefDeleteCmdLine[MAX_PATH + 32];
>+  wsprintfW(deferredSlefDeleteCmdLine, 
>+            L"/C SLEEP %i && DEL \"%s\"", 
>+            DEFERRED_DELETE_TIMEOUT_SECONDS, 
>+            pathToDelete);
>+
>+  SetLastError(ERROR_SUCCESS);
>+  ShellExecuteW(NULL, L"open", L"cmd", deferredSlefDeleteCmdLine, NULL, SW_HIDE);
>+  return GetLastError();
>+}
>+
>+bool SvcInstall(bool upgradeOnly)
>+{
>+  // Get a handle to the local computer SCM database with full access rights.  
>+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
>+  if (!schSCManager) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not open service manager.  (%d)", GetLastError()));
>+    return false;
>+  }
>+
>+  WCHAR modulePath[MAX_PATH + 1];
>+  if(!GetModuleFileNameW(NULL, modulePath, NS_ARRAY_LENGTH(modulePath))) {
nit: I'd prefer if you used sizeof(modulePath)/sizeof(modulePath[0]) since this is standalone code.
Comment 159 Brian R. Bondy [:bbondy] 2011-10-25 21:19:55 PDT
Created attachment 569605 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v2.

Minor change (removed static for one function) didn't want to put in another patch to avoid future rebasing.
Comment 160 Brian R. Bondy [:bbondy] 2011-10-26 08:03:55 PDT
Created attachment 569679 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated.

This new patch has handling for launching the callback application with the unelevated token.  
The way the new code works is that it trims off the callback application from the command lines and executes that separately.  It calls updater.exe without the callback information.

I also noticed something we didn't think about which is that the callback app is also specified via command line and therefore is suseptible to session ID spoofing.  It's not a huge deal because we execute it with the unelevated token though.  The is not a new problem in this patch, but also a problem even without this latest patch.  To fix it, I added a new check for certificate checking on the callback app as well.

I think it is fine to not have the callback app (firefox.exe) signed for Nightly (until we have it automated) because the only side effect is that the callback application will not be run after updates.
Comment 161 Brian R. Bondy [:bbondy] 2011-10-26 08:05:42 PDT
Created attachment 569681 [details] [diff] [review]
Patch 15 - UAC helper functions.

No changes, just moved some code out of this patch and into patch 2 to avoid future rebasing.
Comment 162 Brian R. Bondy [:bbondy] 2011-10-26 08:08:24 PDT
Created attachment 569683 [details] [diff] [review]
Patch 2 - Main service logic. v3.

Implemented review comments.
Comment 163 Brian R. Bondy [:bbondy] 2011-10-26 08:21:49 PDT
For anyone doing QA on the build, any installer you use after today has the NSPR module changed from nsFirefoxService to nsMaintenanceService.
Comment 164 Brian R. Bondy [:bbondy] 2011-10-26 08:43:44 PDT
Re questions on patch 1 review of manifest file:

> Out of curiousity, could this safely be asInvoker instead? 
> If so, is there any reason not to just use asInvoker?

There is a lot of code in the service that wouldn't work asInvoker so I think it needs to stay asAdministrator.

>+      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
> Why isn't Vista included here?

This was carried over from me originally copying the manifest file from updater.exe.  It is a problem there too, I will include a fix in that manfiest as well in the next patch.
Comment 165 Brian R. Bondy [:bbondy] 2011-10-26 08:55:42 PDT
Created attachment 569692 [details] [diff] [review]
Patch 1 - Base service code. v2.

Implemented review comments.
Comment 166 Ian Melven :imelven 2011-10-26 09:18:16 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #160)
> Created attachment 569679 [details] [diff] [review] [diff] [details] [review]
> Patch 16 - Support for launching callback app as unelevated.
> 
> This new patch has handling for launching the callback application with the
> unelevated token.  
> The way the new code works is that it trims off the callback application
> from the command lines and executes that separately.  It calls updater.exe
> without the callback information.
> 
> I also noticed something we didn't think about which is that the callback
> app is also specified via command line and therefore is suseptible to
> session ID spoofing.  It's not a huge deal because we execute it with the
> unelevated token though.  The is not a new problem in this patch, but also a
> problem even without this latest patch.  To fix it, I added a new check for
> certificate checking on the callback app as well.
> 
> I think it is fine to not have the callback app (firefox.exe) signed for
> Nightly (until we have it automated) because the only side effect is that
> the callback application will not be run after updates.

i took a look through this and it looks good, checking the signature on the callback app helps mitigate some of my worries about the session id spoofing problem as well.
Comment 167 Brian R. Bondy [:bbondy] 2011-10-26 11:31:17 PDT
Created attachment 569744 [details] [diff] [review]
Patch 3 - Code for installing the service. v2.

Implemented review comments (+some review comments from other patches into this one) and better version checking.

nsVersionComparator was not used because I'd have to link to more stuff and it works with strings, but I have the version components in DWORDs.
The codeguru code uses the same APIs as I was using but did save me some time to see how to get the other version components.
Only a small change was needed from my existing code to check the other 3 components of the version.
Comment 168 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 13:00:52 PDT
Many of the PR_LOG messages seem appropriate as event log messages. If you think it would be a good thing to add please file a followup bug.
Comment 169 Brian R. Bondy [:bbondy] 2011-10-26 13:10:42 PDT
I had some code initially that did event log messages (in the first base patch which is now obsolete) but you need to have a special compiling step and also I wasn't sure about how to manage the localization. I know it would be good for enterprise customers though.  I'll post a follow up bug for it, thanks for the suggestion.
Comment 170 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 13:25:28 PDT
Comment on attachment 568685 [details] [diff] [review]
Patch 8 - NSPR wide string logging support on Windows

This needs to be reviewed by one of the NSPR people such as wtc@google.com.

Before asking for review it would be a good thing to make the indentation consistent with the rest of the file.
Comment 171 Brian R. Bondy [:bbondy] 2011-10-26 13:32:09 PDT
> Before asking for review it would be a good thing to make the 
> indentation consistent with the rest of the file.

Sorry looked aligned in my local patch file (tortoiseHg) and Visual Studio. 
They use a lot of mixed tab/space characters.

I'll do th same as them and re-submit.
Comment 172 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 14:04:05 PDT
Comment on attachment 568679 [details] [diff] [review]
Patch 4 - Certificate check code inside service

># HG changeset patch
># Parent f04152f2f05006759cc53b386dcdc35e81035d90
># User Brian R. Bondy <netzen@gmail.com>
>Bug481815 - Windows silent update service; Certificate check code
>
>diff --git a/toolkit/components/maintenanceservice/certificatecheck.cpp b/toolkit/components/maintenanceservice/certificatecheck.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/certificatecheck.cpp
>@@ -0,0 +1,545 @@
>+/* ***** 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 certificate check code
Maintenance service certificate check code.

here and else where

>+ *
>+ * The Initial Developer of the Original Code is
>+ * Mozilla Foundation.
>+ * Portions created by the Initial Developer are Copyright (C) 2011
>+ * the Initial Developer. All Rights Reserved.
>+ *
>+ * Contributor(s):
>+ *   Brian R. Bondy <netzen@gmail.com>
>+ *
>+ * 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 ***** */
>+
>+#include <stdio.h>
>+#include <stdlib.h>
>+#include <windows.h>
>+#include <softpub.h>
>+#include <wintrust.h>
>+
>+#include "certificatecheck.h"
>+#include "servicebase.h"
>+
>+// Link with the Wintrust.lib file.
This comment isn't of any value... is it?

>+#pragma comment(lib, "wintrust.lib")
>+#pragma comment(lib, "crypt32.lib")
>+
>+static const int ENCODING = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
>+
>+
>+// Checks to see if a PE file stored at filename matches the specified issuer
>+// and name.
javadoc comment please

Ideally, we would allow the application to specify what cert attributes to check though I think this is fine at least for now.

>+DWORD CheckCertificateForPEFile(LPCWSTR filename, 
>+                                CertificateCheckInfo &infoToMatch)
filename is really a filepath unless I am mistaken

nit: forgot the following from style guide here and elsewhere... I'm a tad of a stickler with following the style guide with new files.
int
MyFunction(...)
{
  ...
}

int
MyClass::Method(...)
{
  ...
}

>+{
>+  HCERTSTORE hStore = NULL;
>+  HCRYPTMSG hMsg = NULL; 
>+  PCCERT_CONTEXT pCertContext = NULL;
>+  PCMSG_SIGNER_INFO pSignerInfo = NULL;
>+  DWORD lastError = ERROR_SUCCESS;
>+  PublisherInfo progPubInfo;
>+  ZeroMemory(&progPubInfo, sizeof(progPubInfo));
>+
>+  // SEH like exception handling is only used for cleanup of ugly C API calls.
>+  __try
>+  {
nit: __try {

>+    // Get the HCERTSTORE and HCRYPTMSG from the signed file.
>+    DWORD dwEncoding, dwContentType, dwFormatType;
>+    BOOL result = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
>+                                   filename, 
>+                                   CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
>+                                   CERT_QUERY_CONTENT_FLAG_ALL, 
>+                                   0, &dwEncoding, &dwContentType,
>+                                   &dwFormatType, &hStore, &hMsg, NULL);

>...
>+    }
>+  }
>+  __finally
>+  {
nit: __finally {

>+    if (progPubInfo.programName) {
>+      LocalFree(progPubInfo.programName);
>+    }
>+    if (progPubInfo.publisherLink) {

>...
>+// Returns FALSE if the issuer or name do not match or if any error 
>+// occurs in the check
javadoc comment please

>+BOOL DoesCertificateMatch(PCCERT_CONTEXT pCertContext, 
>+                          CertificateCheckInfo &infoToMatch)
>+{
>+  DWORD dwData;

>...
>+LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
>+{
>+  LPWSTR outputString = NULL;
>+
>+  outputString = (LPWSTR)LocalAlloc(LPTR,
>+    (wcslen(inputString) + 1) * sizeof(WCHAR));
>+  if (outputString != NULL)
>+  {
nit:
if (outputString != NULL) {

>+    lstrcpyW(outputString, inputString);
>+  }
>+  return outputString;
>+}
>+
>+BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
>+                             PublisherInfo &info)
>+{
>+  BOOL result = FALSE;
>+  PSPC_SP_OPUS_INFO OpusInfo = NULL;  
>+  DWORD dwData;
>+
>+  __try
>+  {
nit: __try {

>+    // Loop through authenticated attributes and find SPC_SP_OPUS_INFO_OBJID OID.
>+    for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) {           
>+      if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, 
>+                   pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) {
>+        // Get Size of SPC_SP_OPUS_INFO structure.
>+        _CRYPTOAPI_BLOB &attr1 = pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0];
>+        result = CryptDecodeObject(ENCODING,

>...
>+        // Fill in Program Name if present.
>+        if (OpusInfo->pwszProgramName) {
>+          info.programName =
>+            AllocateAndCopyWideString(OpusInfo->pwszProgramName);
>+        } else {
>+          info.programName = NULL;
>+        }
>+
>+        // Fill in Publisher Information if present.
>+        if (OpusInfo->pPublisherInfo) {
>+          switch (OpusInfo->pPublisherInfo->dwLinkChoice)
>+          {
nit: format as follows from the style guide
https://developer.mozilla.org/En/Mozilla_Coding_Style_Guide

switch (...) {  
  case 1: {  
    // When you need to declare a variable in a switch, put the block in braces  
    int var;  
    break;  
  }  
  case 2:  
    ...  
    break;  
  default:  
    break;  
}

>+          case SPC_URL_LINK_CHOICE:
>+            info.publisherLink =
>+              AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
>+            break;
>+
>+          case SPC_FILE_LINK_CHOICE:
>+            info.publisherLink =
>+              AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
>+            break;
>+
>+          default:
>+            info.publisherLink = NULL;
>+            break;
>+          }
>+        } else {
>+          info.publisherLink = NULL;
>+        }
>+
>+        // Fill in More Info if present.
>+        if (OpusInfo->pMoreInfo) {
>+          switch (OpusInfo->pMoreInfo->dwLinkChoice)
>+          {
nit: same as previous formating of switch

>+          case SPC_URL_LINK_CHOICE:
>+            info.moreInfoLink =
>+              AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
>+            break;
>+
>+          case SPC_FILE_LINK_CHOICE:
>+            info.moreInfoLink =
>+              AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
>+            break;
>+
>+          default:
>+            info.moreInfoLink = NULL;
>+            break;
>+          }
>+        } else {
>+          info.moreInfoLink = NULL;
>+        }
>+        result = TRUE;
>+        break; // Break from for loop.
>+      } // lstrcmp SPC_SP_OPUS_INFO_OBJID                 
>+    } // for 
cleanup

>+  }
>+  __finally
>+  {
nit: __finally {

>...
>+  // WinVerifyTrust verifies signatures as specified by the GUID 
>+  // and Wintrust_Data.
>+  LONG lStatus = WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData);
>+  DWORD dwLastError = GetLastError();
>+  BOOL validated = FALSE;
>+  switch (lStatus) {
>+    case ERROR_SUCCESS:
>+      // The hash that represents the subject is trusted and there were no
>+      // verification errors.  No publisher nor time stamp chain errors.
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS, 
>+             ("The file \"%S\" is signed and the signature was verified.",
>+              pwszSourceFile));
>+      validated = TRUE;
>+      break;
>+    case TRUST_E_NOSIGNATURE:
>+      // The file was not signed or had a signature that was not valid.
>+      // Get the reason for no signature.
>+      if (TRUST_E_TIME_STAMP == dwLastError) {
>+        // The file was not signed.
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS, 
>+               ("The file \"%S\" is had a timestamp error.", pwszSourceFile));
nit: The file \"%S\" has a timestamp error.

>+      } else if (TRUST_E_NOSIGNATURE == dwLastError ||
>+                TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError ||
>+                TRUST_E_PROVIDER_UNKNOWN == dwLastError) {
>+        // The file was not signed.
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS, 
>+               ("The file \"%S\" is not signed.", pwszSourceFile));
>+      } else {
>+        // The signature was not valid or there was an error 
>+        // opening the file.
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS, 
>+               ("An unknown error occurred trying to verify the signature of the \"%S\" file.", pwszSourceFile));
nit: I'm not too much of a stickler for line length but this and a few other lines in the other patches are really long.

>+      }
>+      break;
>+    case TRUST_E_EXPLICIT_DISTRUST:
>+      // The hash that represents the subject or the publisher 
>+      // is not allowed by the admin or user.
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS, 
>+             ("The signature is present, but specifically disallowed."));
>+      break;
>+    case TRUST_E_SUBJECT_NOT_TRUSTED:
>+      // The user clicked "No" when asked to install and run.
This implies that we present the user with ui to click "No". Please elaborate.

>diff --git a/toolkit/components/maintenanceservice/certificatecheck.h b/toolkit/components/maintenanceservice/certificatecheck.h
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/certificatecheck.h
>@@ -0,0 +1,65 @@
>...
>+#ifndef _CERTIFICATECHECK_H_
>+#define _CERTIFICATECHECK_H_
>+
>+#include <wincrypt.h>
>+
>+struct PublisherInfo
>+{
>+  LPWSTR programName;
>+  LPWSTR publisherLink;
>+  LPWSTR moreInfoLink;
>+};
>+
>+struct CertificateCheckInfo
>+{
>+  LPCWSTR name;
>+  LPCWSTR issuer;
>+  PublisherInfo signerInfo;
>+};
>+
>+BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, 
>+                             PublisherInfo &info);
nit: perhaps GetProgramAndPublisherInfo

>+BOOL DoesCertificateMatch(PCCERT_CONTEXT pCertContext, 
>+                          CertificateCheckInfo &infoToMatch);
nit: perhaps DoCertificateAttributesMatch

>+DWORD VerifyCertificateTrustForFile(LPCWSTR pwszSourceFile);
nit: consistency - drop the hungarian notation
nit: I'd just go with filepath

>+DWORD CheckCertificateForPEFile(LPCWSTR filename, 
>+                                CertificateCheckInfo &infoToMatch);
nit: I'd just go with filepath

>+
>+#endif
>diff --git a/toolkit/components/maintenanceservice/registrycertificates.cpp b/toolkit/components/maintenanceservice/registrycertificates.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/registrycertificates.cpp
>@@ -0,0 +1,179 @@
>...
>+BOOL CheckIfBinaryMatchesAllowedCertificates(LPCWSTR application) 
nit: A tad verbose of a name... need to think about it a bit. For example, you have DoesCertificateMatch and this might be better as DoesCertificateMatchFile though that could likely be improved as well.

nit: application is just a filepath.

>+{ 
>+  LPCWSTR maintenanceServiceKey = L"SOFTWARE\\Mozilla\\Certs";
>+
>+  // We use KEY_WOW64_64KEY to always force 64-bit view.
This comment leaves me wondering why... please elaborate.

It isn't clear to me whether it is better to use a common key for all cert attributes for all installs or to have each install store the cert attributes in an install specific key. I am leaning install specific keys since another app should not be able to provide cert attributes that are then used by another app. What do you think?

Holding off on the rest of the registry code review until the above is answered
Comment 173 Brian R. Bondy [:bbondy] 2011-10-26 14:11:29 PDT
Created attachment 569786 [details] [diff] [review]
(PUSHED) Patch 8 - NSPR wide string logging support on Windows. v2.

I don't fully understand the whitespacing in that file:
sometimes a line has tab + 4 spaces, sometimes 2 tabs, and sometimes all spaces.

But I made it all consistent with the similar parts of the file.
Comment 174 Brian R. Bondy [:bbondy] 2011-10-26 14:14:52 PDT
Because of the CSS styling by the way somtimes the green stuff looks 1 space off but it is the same whitespace, I have show whitespace on.
Comment 175 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 14:19:30 PDT
Comment on attachment 569786 [details] [diff] [review]
(PUSHED) Patch 8 - NSPR wide string logging support on Windows. v2.

Thanks! Looks much more consistent with the existing code.

I'm fairly certain that wtc will need to review these changes so changing reviewer.
Comment 176 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 14:53:36 PDT
Comment on attachment 569605 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v2.

>diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp
>--- a/toolkit/xre/nsUpdateDriver.cpp
>+++ b/toolkit/xre/nsUpdateDriver.cpp
>@@ -475,18 +480,29 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
>     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
>   }
> 
>   LOG(("spawning updater process [%s]\n", updaterPath.get()));
> 
> #if defined(USE_EXECV)
>   execv(updaterPath.get(), argv);
> #elif defined(XP_WIN)
>-  if (!WinLaunchChild(updaterPathW.get(), argc, argv))
>-    return;
>+
>+  bool attemptToUseService = 
>+    mozilla::Preferences::GetBool(kPrefAppUpdateService, false);
Have you tested this? Unless I'm much mistaken this won't work because we don't actually have a profile at this time. Not sure how this should be handled if this is the case at this time.

>+  // First try to launch the update operation using the service
nit: this should call out "if the user hasn't disabled updating with the service"

>+  if (!attemptToUseService || 
>+      !WinLaunchServiceCommand(updaterPathW.get(), argc, argv)) {
>+    // If that fails then launch the update using updater.exe
nit: remove "If that fails" and just go with "Launch the update using updater.exe"

>+    if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
>+      return;
>+    }
>+  }
>+
>+  // We are going to process an update so we should exit now
>   _exit(0);

>diff --git a/toolkit/xre/nsWindowsRestart.cpp b/toolkit/xre/nsWindowsRestart.cpp
>--- a/toolkit/xre/nsWindowsRestart.cpp
>+++ b/toolkit/xre/nsWindowsRestart.cpp
>...
>@@ -219,16 +230,219 @@ FreeAllocStrings(int argc, PRUnichar **a
>   while (argc) {
>     --argc;
>     delete [] argv[argc];
>   }
> 
>   delete [] argv;
> }
> 
>+BOOL 
>+EnsureWindowsServiceStarted() {
>+  // Get a handle to the SCM database.
>+  nsAutoServiceHandle serviceManager(OpenSCManager(NULL, NULL, 
>+                                                   SC_MANAGER_CONNECT | 
>+                                                   SC_MANAGER_ENUMERATE_SERVICE));
>+  if (!serviceManager)  {
>+    return FALSE;
>+  }
>+
>+  // Get a handle to the service.
>+  nsAutoServiceHandle service(OpenServiceW(serviceManager, 
>+                                           L"MozillaMaintenance", 
>+                                           SERVICE_QUERY_STATUS | SERVICE_START));
>+  if (!service) { 
>+    return FALSE;
>+  }
>+
>+  // Make sure the service is not stopped.
>+  SERVICE_STATUS_PROCESS ssp;
>+  DWORD bytesNeeded;
>+  if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
>+    sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
>+      return FALSE;
>+  }
>+
>+  if (ssp.dwCurrentState == SERVICE_STOPPED) {
>+    if (!::StartService(service, 0, NULL)) {
>+      return FALSE;
>+    }
>+
>+    // Make sure we can get into a started state without waiting too long.
>+    DWORD totalWaitTime = 0;
>+    static const int maxWaitTime = 1000 * 5; // Never wait more than 5 seconds
What is the normal flow for this case? Specifically, does the service typically have to be started in this code path? How long does it take for the service to start on your system?

>+    while (QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
>+        sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
>+      if (ssp.dwCurrentState == SERVICE_RUNNING) {
>+        break;
>+      } else if (ssp.dwCurrentState == SERVICE_START_PENDING &&
>+        totalWaitTime > maxWaitTime) {
>+          // We will probably eventually start, but we can't wait any longer.
>+          break;
>+      } else if (ssp.dwCurrentState != SERVICE_START_PENDING) {
>+        return FALSE;
>+      }
>+
>+      Sleep(ssp.dwWaitHint);
>+      totalWaitTime += (ssp.dwWaitHint + 10);
Where does 10 come from?

>+    }
>+  }
>+
>+  return ssp.dwCurrentState == SERVICE_RUNNING;
>+}
>+
>+
>+BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra)
>+{
>+  if (wcslen(base) + wcslen(extra) > MAX_PATH) {
>+    return FALSE;
>+  }
>+
>+  return ::PathAppendW(base, extra);
>+}
>+
>+BOOL GetUpdateDirectoryPath(WCHAR *path) 
>+{
>+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
>+    SHGFP_TYPE_CURRENT, path);
>+  if (FAILED(hr)) {
>+    return FALSE;
>+  }
>+  if (!PathAppendSafe(path, L"Mozilla")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(path, NULL);
>+
>+  if (!PathAppendSafe(path, L"updates")) {
>+    return FALSE;
>+  }
>+  ::CreateDirectoryW(path, NULL);
I am fairly certain that these directories will be created with only the current user and accounts running as admin having write access.

A better method might be to do something similar to XRE_UPDATE_ROOT_DIR and set the permissions on creation.
http://mxr.mozilla.org/mozilla-central/source/toolkit/xre/nsXREDirProvider.cpp#261

Another option is to have the installer set the permissions on the Mozilla directory under CSIDL_COMMON_APPDATA. We already have the NSIS plugin for setting the permissions.

>+  return TRUE;
>+}
>+
>+/**
>+ * Launch a service initiated action with the specified arguments.
>+ * @note argv[0] is ignored
>+ * @note The form of this function that takes char **argv expects UTF-8
>+ */
Include the parameters as well
http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

>+BOOL
>+WinLaunchServiceCommand(const PRUnichar *exePath, int argc, PRUnichar **argv)
>+{
>+  // Ensure the service is started, if not we should try to start it, if it is
>+  // not in a started state we cannot execute a service command.
s/started state/running state/

>+  if (!EnsureWindowsServiceStarted()) {
>+    return FALSE;
>+  }
>+
>+  WCHAR programData[MAX_PATH + 1];
Perhaps updateData?

>+  if (!GetUpdateDirectoryPath(programData)) {
>+    return FALSE;
>+  }
>+
>+  // Get a new GUID for the filename
I'm curious why you are using a GUID for the filename instead of just a temp file, etc.?

>+  UUID uuid;
>+  ::ZeroMemory(&uuid, sizeof(UUID));
>+  ::UuidCreate(&uuid);
>+  WCHAR* wUUID = NULL;
>+  ::UuidToStringW(&uuid, (RPC_WSTR*)&wUUID);
>+  if(!wUUID) {
>+    return FALSE;
>+  }
>+  
>+  // Generate the filename for the service command
>+  WCHAR fileName[128];
>+  wsprintfW(fileName, L"%s.ma", wUUID);
>+  ::RpcStringFreeW((RPC_WSTR*)&wUUID);
>+  wUUID = NULL;
>+  if (!PathAppendSafe(programData, fileName)) {
>+    return FALSE;
>+  }
>+  const int FILE_SHARE_NONE = 0;
>+  nsAutoHandle updateMetaFile(CreateFileW(programData, GENERIC_WRITE, 
>+                                          FILE_SHARE_NONE, NULL, CREATE_NEW, 
>+                                          0, NULL));
>+  if (updateMetaFile == INVALID_HANDLE_VALUE) {
>+    return FALSE;
>+  }
>+
>+  // Write out the command line arguments that needs to be passed to updater.exe
>+  PRUnichar *commandLineBuffer = MakeCommandLine(argc, argv);
>+  DWORD wrote1, wrote2, wrote3, wrote4;
Please use descriptive names for wrote#

>+  
>+  DWORD sessionID = 0;
>+  ProcessIdToSessionId(GetCurrentProcessId(), &sessionID);
>+  BOOL result1 = ::WriteFile(updateMetaFile, &sessionID, 
>+                             sizeof(DWORD), 
>+                             &wrote1, NULL);
>+
>+  WCHAR appBuffer[MAX_PATH + 1];
>+  ZeroMemory(appBuffer, sizeof(appBuffer));
>+  wcscpy(appBuffer, exePath);
>+  BOOL result2 = ::WriteFile(updateMetaFile, appBuffer, 
>+                             MAX_PATH * sizeof(WCHAR), 
>+                             &wrote2, NULL);
>+
>+  WCHAR workingDirectory[MAX_PATH + 1];
>+  ZeroMemory(workingDirectory, sizeof(appBuffer));
>+  GetCurrentDirectoryW(sizeof(workingDirectory)/sizeof(workingDirectory[0]), 
nit: sizeof(workingDirectory) / sizeof(workingDirectory[0])

>+                       workingDirectory);
>+  BOOL result3 = ::WriteFile(updateMetaFile, workingDirectory, 
>+                             MAX_PATH * sizeof(WCHAR), 
>+                             &wrote3, NULL);
>+
>+  DWORD commandLineLength = wcslen(commandLineBuffer) * sizeof(WCHAR);
>+  BOOL result4 = ::WriteFile(updateMetaFile, commandLineBuffer, 
>+                             commandLineLength, 
>+                             &wrote4, NULL);
How about?
BOOL result;
result |= ::WriteFile
result |= ::WriteFile
etc.

>+  free(commandLineBuffer);
>+  if (!result1 || !result2 || !result3 || !result4 ||
>+      wrote1 != sizeof(DWORD) ||
>+      wrote2 != MAX_PATH * sizeof(WCHAR) ||
>+      wrote3 != MAX_PATH * sizeof(WCHAR) ||
>+      wrote4 != commandLineLength) {
>+    updateMetaFile.forget();
>+    DeleteFileW(programData);
>+    return FALSE;
>+  }
>+
>+  // Note we construct the 'service work' meta object with a ma extension
>+  // When we want the service to start processing it we simply rename it to
>+  // have a .mz extension.  This ensures that the service will never try to
>+  // process a partial update work meta file. 
Would it make sense to create the file in a different directory (temp?) and when it is ready move it to the directory?

>+  updateMetaFile.forget();
>+  WCHAR completedMetaFilePath[MAX_PATH + 1];
>+  wcscpy(completedMetaFilePath, programData);
>+  completedMetaFilePath[wcslen(completedMetaFilePath) -1] = 'z';
>+  return MoveFileExW(programData, completedMetaFilePath, 
>+                     MOVEFILE_REPLACE_EXISTING);
>+}
>+
>+BOOL
>+WinLaunchServiceCommand(const PRUnichar *exePath, int argc, char **argv)
>+{
>+  PRUnichar** argvConverted = new PRUnichar*[argc];
>+  if (!argvConverted)
>+    return FALSE;
>+
>+  for (int i = 0; i < argc; ++i) {
>+    argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
>+    if (!argvConverted[i]) {
>+      FreeAllocStrings(i, argvConverted);
>+      return FALSE;
>+    }
>+  }
>+
>+  BOOL ok = WinLaunchServiceCommand(exePath, argc, argvConverted);
>+  FreeAllocStrings(argc, argvConverted);
>+  return ok;
>+}
>+
>+
>+
> /**
>  * Launch a child process with the specified arguments.
>  * @note argv[0] is ignored
>  * @note The form of this function that takes char **argv expects UTF-8
>  */
Include the parameters as well
http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

> 
> BOOL
> WinLaunchChild(const PRUnichar *exePath, int argc, PRUnichar **argv);
Comment 177 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 14:59:44 PDT
Comment on attachment 568950 [details] [diff] [review]
Patch 6 - Firefox preferences update page changes. v2.

># HG changeset patch
># Parent 7de33ffbb8ecba3bd67dcbb39e25f77a2fd01b94
># User Brian R. Bondy <netzen@gmail.com>
>Bug481815 - Windows silent update service; Preferences for whether or not to use the service.
>
>diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
>--- a/browser/app/profile/firefox.js
>+++ b/browser/app/profile/firefox.js
>@@ -184,16 +184,19 @@ pref("app.update.showInstalledUI", false
> 
> // 0 = suppress prompting for incompatibilities if there are updates available
> //     to newer versions of installed addons that resolve them.
> // 1 = suppress prompting for incompatibilities only if there are VersionInfo
> //     updates available to installed addons that resolve them, not newer
> //     versions.
> pref("app.update.incompatible.mode", 0);
> 
>+// Whether or not to attempt using the service for updates.
>+pref("app.update.service", true);
As stated in the last patch, I'm not certain this pref will be available when the update is applied during startup.

minusing until the pref issue is figured out.
Comment 178 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 15:04:45 PDT
Comment on attachment 568684 [details] [diff] [review]
Patch 7 - RAII base helpers

Since this is new code under XPCOM let's get Jim to review it and bsmedberg to sr it since he owns XPCOM after it has been reviewed by Jim.
Comment 179 Brian R. Bondy [:bbondy] 2011-10-26 15:31:12 PDT
>+  bool attemptToUseService = 
>+    mozilla::Preferences::GetBool(kPrefAppUpdateService, false);
> Have you tested this? Unless I'm much mistaken this won't work because we don't
> actually have a profile at this time. Not sure how this should be handled if this
> is the case at this time.

Yup I've tested it.
I did have that problem and but please see Patch 13 - Moving the update check after init.
Comment 180 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 15:43:27 PDT
I forgot that Ted is a peer for NSPR... feel free to change the request
Comment 181 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:00:31 PDT
Comment on attachment 568688 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade

>diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi
>--- a/browser/installer/windows/nsis/installer.nsi
>+++ b/browser/installer/windows/nsis/installer.nsi
>...
>@@ -34,16 +35,17 @@
> #
> # ***** END LICENSE BLOCK *****
> 
> # Required Plugins:
> # AppAssocReg   http://nsis.sourceforge.net/Application_Association_Registration_plug-in
> # ApplicationID http://nsis.sourceforge.net/ApplicationID_plug-in
> # ShellLink     http://nsis.sourceforge.net/ShellLink_plug-in
> # UAC           http://nsis.sourceforge.net/UAC_plug-in
>+# SimpleSC      http://nsis.sourceforge.net/NSIS_Simple_Service_Plugin
Remove if you go with the suggested method below

> 
> ; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
> !verbose 3
> 
> ; 7-Zip provides better compression than the lzma from NSIS so we add the files
> ; uncompressed and use 7-Zip to create a SFX archive of it
> SetDatablockOptimize on
> SetCompress off
>@@ -363,16 +382,22 @@ Section "-Application" APP_IDX
>     ${If} $AddDesktopSC == 1
>     ${OrIf} $AddStartMenuSC == 1
>       WriteRegDWORD HKLM "$0" "IconsVisible" 1
>     ${Else}
>       WriteRegDWORD HKLM "$0" "IconsVisible" 0
>     ${EndIf}
>   ${EndIf}
> 
>+  ${IF} $InstallMaintenanceService == 1
>+    ; The user wants to install the maintenance service, so execute
>+    ; the pre-packaged maintenance service installer. /S for silent.
>+    nsExec::Exec '"$INSTDIR\maintenanceservice_installer\maintenanceservice_installer.exe" /S' 
note: I think this will end up being different than this but I'll call it out in the review of the new install code.

>@@ -787,16 +812,53 @@ Function leaveShortcuts
>     ${MUI_INSTALLOPTIONS_READ} $AddQuickLaunchSC "shortcuts.ini" "Field 4" "State"
>   ${EndUnless}
> 
>   ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
>     Call CheckExistingInstall
>   ${EndIf}
> FunctionEnd
> 
>+Function preComponents
>+  ; Don't show the custom components page if the
>+  ; user is not an admin
>+  Call IsUserAdmin
>+  Pop $R9
>+  ${If} $R9 != "true"
>+    Abort
>+  ${EndIf}
>+
>+  ; If the service already exists, don't show this page
>+  ; We will always install again (which will upgrade)
>+  ; as long as the user is admin
>+  ${Unicode2Ansi} "MozillaMaintenance" $R8
>+  SimpleSC::ExistsService "$R8"
Instead of using the plugin couldn't you just check for the existence of the binary since we require it to be installed into a specific location under %ProgramFiles% or %ProgramFiles(x86)%? It would require two checks but it would remove the need for the plugin and the Unicode2Ansi or recompiling the plugin as x64.

>diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi
>--- a/browser/installer/windows/nsis/uninstaller.nsi
>+++ b/browser/installer/windows/nsis/uninstaller.nsi
>@@ -14,16 +14,17 @@
> # The Original Code is the Mozilla Installer code.
> #
> # The Initial Developer of the Original Code is Mozilla Foundation
> # Portions created by the Initial Developer are Copyright (C) 2006
> # the Initial Developer. All Rights Reserved.
> #
> # Contributor(s):
> #  Robert Strong <robert.bugzilla@gmail.com>
>+#  Brian R. Bondy <netzen@gmail.com>
> #
> # 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
>@@ -155,26 +156,28 @@ ShowUnInstDetails nevershow
> !define MUI_WELCOMEPAGE_TITLE_3LINES
> !define MUI_HEADERIMAGE
> !define MUI_HEADERIMAGE_RIGHT
> !define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
> 
> ; Use a right to left header image when the language is right to left
> !ifdef ${AB_CD}_rtl
> !define MUI_HEADERIMAGE_BITMAP_RTL wizHeaderRTL.bmp
>+!define MUI_HEADERIMAGE_UNBITMAP_RTL wizHeaderRTL.bmp
> !else
> !define MUI_HEADERIMAGE_BITMAP wizHeader.bmp
>+!define MUI_HEADERIMAGE_UNBITMAP wizHeader.bmp
> !endif
It uses the correct header anyway due to the custom branding code. Since we don't display the installer ui for this code could you check if it does the right thing with MUI_HEADERIMAGE_BITMAP and MUI_HEADERIMAGE_BITMAP_RTL undefined and that the default headers aren't included then?

> /**
>  * Uninstall Pages
>  */
> ; Welcome Page
>-!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
>-!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
>+;!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
>+;!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome
These are needed so the UI can use custom branding.

> !insertmacro MUI_UNPAGE_WELCOME
> 
> ; Custom Uninstall Confirm Page
> UninstPage custom un.preConfirm un.leaveConfirm
> 
> ; Remove Files Page
> !insertmacro MUI_UNPAGE_INSTFILES
> 
>diff --git a/browser/locales/en-US/installer/custom.properties b/browser/locales/en-US/installer/custom.properties
>--- a/browser/locales/en-US/installer/custom.properties
>+++ b/browser/locales/en-US/installer/custom.properties
>@@ -52,28 +52,32 @@
> 
> REG_APP_DESC=$BrandShortName delivers safe, easy web browsing. A familiar user interface, enhanced security features including protection from online identity theft, and integrated search let you get the most out of the web.
> CONTEXT_OPTIONS=$BrandShortName &Options
> CONTEXT_SAFE_MODE=$BrandShortName &Safe Mode
> OPTIONS_PAGE_TITLE=Setup Type
> OPTIONS_PAGE_SUBTITLE=Choose setup options
> SHORTCUTS_PAGE_TITLE=Set Up Shortcuts
> SHORTCUTS_PAGE_SUBTITLE=Create Program Icons
>+COMPONENTS_PAGE_TITLE=Set Up Optional Components
>+COMPONENTS_PAGE_SUBTITLE=Optional Recommended Components
> SUMMARY_PAGE_TITLE=Summary
> SUMMARY_PAGE_SUBTITLE=Ready to start installing $BrandShortName
> SUMMARY_INSTALLED_TO=$BrandShortName will be installed to the following location:
> SUMMARY_REBOOT_REQUIRED_INSTALL=A restart of your computer may be required to complete the installation.
> SUMMARY_REBOOT_REQUIRED_UNINSTALL=A restart of your computer may be required to complete the uninstall.
> SUMMARY_TAKE_DEFAULTS=U&se $BrandShortName as my default web browser
> SUMMARY_INSTALL_CLICK=Click Install to continue.
> SUMMARY_UPGRADE_CLICK=Click Upgrade to continue.
> SURVEY_TEXT=&Tell us what you thought of $BrandShortName
> LAUNCH_TEXT=&Launch $BrandShortName now
> CREATE_ICONS_DESC=Create icons for $BrandShortName:
>+OPTIONAL_COMPONENTS_DESC=The Maintenance Service will allow you to update $BrandShortName silently in the background.
> ICONS_DESKTOP=On my &Desktop
>+MAINTENANCE_SERVICE_CHECKBOX_DESC=Install &Maintenance Service
Just put OPTIONAL_COMPONENTS_DESC and MAINTENANCE_SERVICE_CHECKBOX_DESC directly following COMPONENTS_PAGE_SUBTITLE.

Please get faaborg or limi to approve the text.

Overall looks good and this should be a quick review after it is resubmitted
Comment 182 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:02:58 PDT
Comment on attachment 568688 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade

That was meant for patch 9 :/
Comment 183 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:08:37 PDT
Comment on attachment 568688 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade

>diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh
>--- a/browser/installer/windows/nsis/shared.nsh
>+++ b/browser/installer/windows/nsis/shared.nsh
>@@ -14,32 +14,39 @@
>...
> !macro PostUpdate
>+
>+  ; If a service already exists, the command line parameter will stop the
>+  ; service and only install itself if it is newer than the already installed
>+  ; service.
>+  nsExec::Exec '"$INSTDIR\maintenanceservice_installer\maintenanceservice_installer.exe" /S /Upgrade' 
note: I think this will end up being different than this but I'll call it out in the review of the new install code.

>@@ -935,19 +942,57 @@
>   Push "nspr4.dll"
>   Push "nssdbm3.dll"
>   Push "mozsqlite3.dll"
>   Push "xpcom.dll"
>   Push "crashreporter.exe"
>   Push "updater.exe"
>   Push "${FileMainEXE}"
> !macroend
>+
>+!define Unicode2Ansi "!insertmacro Unicode2Ansi"
>+!macro Unicode2Ansi String outVar
>+  System::Call 'kernel32::WideCharToMultiByte(i 0, i 0, w "${String}", i -1, t .s, i ${NSIS_MAX_STRLEN}, i 0, i 0) i'
>+  Pop "${outVar}"
>+!macroend
Won't be needed if the file check approach is used.

>+
>+; Copied from: http://nsis.sourceforge.net/IsUserAdmin
>+Function IsUserAdmin
>+  Push $R0
>+  Push $R1
>+  Push $R2
>+ 
>+  ClearErrors
>+  UserInfo::GetName
>+  IfErrors Win9x
>+  Pop $R1
>+  UserInfo::GetAccountType
>+  Pop $R2
>+ 
>+  StrCmp $R2 "Admin" 0 Continue
>+  StrCpy $R0 "true"
>+  Goto Done
>+ 
>+  Continue:
>+
>+  StrCmp $R2 "" Win9x
>+  StrCpy $R0 "false"
>+  Goto Done
>+ 
>+  Win9x:
>+  StrCpy $R0 "true"
>+ 
>+  Done:
>+  Pop $R2
>+  Pop $R1
>+  Exch $R0
>+FunctionEnd
>+
I'd prefer if IsUserAdmin was turned into a macro and added to common.nsh so all apps can use it

Should be a quick review with those changes
Comment 184 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:12:55 PDT
Comment on attachment 568689 [details] [diff] [review]
Patch 11 - New maintenance service installer

>diff --git a/browser/installer/windows/nsis/maintenanceservice_installer.nsi b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
It seems to me that this could always run silently and thereby simplify the code quite a lot.

Also, it would lessen the size of the overall installer if this installer didn't include the files inside of it. Instead, you can just install the files in the root of the installation directory and copy them to the destination directory.

Are you ok with doing it that way?
Comment 185 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:14:21 PDT
Regarding "always run silently", I am referring to the install process. I haven't looked at the uninstall process yet.
Comment 186 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:16:18 PDT
Comment on attachment 568689 [details] [diff] [review]
Patch 11 - New maintenance service installer

This is dependent on whether the file check is sufficient or the plugin is actually needed. Removing review until that is answered.
Comment 187 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:17:25 PDT
Comment on attachment 568690 [details] [diff] [review]
Patch 12 - Simple SC plugin inclusion

Too many reviews! :)

This is the patch that is dependent on whether the file check is sufficient or the plugin is actually needed. Removing review until that is answered.
Comment 188 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 16:30:28 PDT
Comment on attachment 568691 [details] [diff] [review]
Patch 13 - Moving the update check after init

I'm overall ok with this though it does scare me a bit since there may be unintended consequences. Does the profile manager display twice when it is set to be displayed for example?

I'm going to test this out before r+ing and we might want to list the behavior changes so they are understood.
Comment 189 Brian R. Bondy [:bbondy] 2011-10-26 17:00:31 PDT
> I'm overall ok with this though it does scare me a bit

Me too, I hate moving things around at init.  I haven't sen any issues yet though.

> Does the profile manager display twice when it is set to be displayed for example?

Do you mean just: 
firefox.exe -ProfileManager ?

If that's what you mean no it only displays once.  There isn't a second init call it's just a move of the update call a little lower in the init.

Or did you mean when there is an update applied?
Comment 190 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 17:37:18 PDT
When an update is applied and all cases where the profile manager is displayed. I suspect it does in that case and we added code elsewhere to cover that case.
Comment 191 Brian R. Bondy [:bbondy] 2011-10-26 18:38:09 PDT
Patch 13 - Moving the update check after init.

OK I tested it and you're absolutely right.

When you start Firefox -ProfileManager when there is a pending update, it will show the profile selection screen.
You select the profile you want, then it does the update.

After the update, the service then relaunches with -ProfileManager but the profile selection screen doesn't display. 
So there must be some other code taking care of that already even know the command line was passed.

If there are some other special cases like this that are problems, we can filter out those command line parameters similar to how Patch 16 works.
Comment 192 Brian R. Bondy [:bbondy] 2011-10-26 20:21:43 PDT
> It isn't clear to me whether it is better to use a common key for 
> all cert attributes for all installs or to have each install store 
> the cert attributes in an install specific key. I am leaning install 
> specific keys since another app should not be able to provide cert 
> attributes that are then used by another app. What do you think?

The way it currently works is that you can have any amount of subfolders under the main Certs key.  Each subkey has the attributes which are allowed.  How do you envision appliction specific locations working? How does the service know where to look?
Comment 193 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 20:27:28 PDT
We could use the install path or a hash of the install path in the registry.
Comment 194 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-26 20:28:40 PDT
See bug 577867 for an example
Comment 195 Brian R. Bondy [:bbondy] 2011-10-26 20:43:43 PDT
Created attachment 569895 [details] [diff] [review]
Patch 4 - Certificate check code inside service. v2.

Implemented review comments.

> Ideally, we would allow the application to specify what cert attributes to check 
> though I think this is fine at least for now.

This is already supported just leave the fields blank to skip that check.
Comment 196 Brian R. Bondy [:bbondy] 2011-10-26 20:45:24 PDT
Created attachment 569896 [details] [diff] [review]
Patch 2 - Main service logic. v3'

Same as last patch but updated function name.
Comment 197 Brian R. Bondy [:bbondy] 2011-10-26 20:47:13 PDT
Created attachment 569897 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v1'

Same as original patch, just has updated function name.
Comment 198 Brian R. Bondy [:bbondy] 2011-10-27 06:35:41 PDT
>+  // Note we construct the 'service work' meta object with a ma extension
>+  // When we want the service to start processing it we simply rename it to
>+  // have a .mz extension.  This ensures that the service will never try to
>+  // process a partial update work meta file. 
> Would it make sense to create the file in a different directory (temp?) and when 
> it is ready move it to the directory?

I'd prefer to keep it in the same directory to avoid complications.  For example if the temp directory was in a different volume and you move the file across volumes you will lose some informatin like security descriptors and it might be of a different fs type hence losing even more info. Also you'd need to specify the flag MOVEFILE_COPY_ALLOWED in that case which uses more space and could fail due to really low disk space situations.  On a different volume w/ MoveFileEx copying it might even cause the service to process a partial work item file.
Comment 199 Emanuel Hoogeveen [:ehoogeveen] 2011-10-27 06:57:37 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #198)
> > Would it make sense to create the file in a different directory (temp?) and when it is ready move it to the directory?
> 
> I'd prefer to keep it in the same directory to avoid complications.  For
> example if the temp directory was in a different volume and you move the
> file across volumes you will lose some informatin like security descriptors
> and it might be of a different fs type hence losing even more info. Also
> you'd need to specify the flag MOVEFILE_COPY_ALLOWED in that case which uses
> more space and could fail due to really low disk space situations.  On a
> different volume w/ MoveFileEx copying it might even cause the service to
> process a partial work item file.
Brian, are you aware of bug 307181?
Comment 200 Brian R. Bondy [:bbondy] 2011-10-27 07:01:35 PDT
> Brian, are you aware of bug 307181?

Yup this MoveFileEx call is admittedly much smaller of a potential problem than that but I was just explaining why I chose to keep it in the same directory while programming it.  I do plan on doing a feedback review on bug 307181 though so thanks for checking to make sure.
Comment 201 Brian R. Bondy [:bbondy] 2011-10-27 08:08:15 PDT
Created attachment 569977 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v3.

Review comments implemented.
Regarding the CreateDirectory / owner issue you brought up I'll add the directory creations as well to the installer to avoid this problem.
Comment 202 Brian R. Bondy [:bbondy] 2011-10-27 08:30:46 PDT
>+  SimpleSC::ExistsService "$R8"
> Instead of using the plugin couldn't you just check for the existence of the binary since 
> we require it to be installed into a specific location under %ProgramFiles% or %ProgramFiles(x86)%? 
> It would require two checks but it would remove the need for the plugin and the Unicode2Ansi or recompiling the plugin as x64.

I think even for x64 compilations NSIS always produces x86 installers.
There is no real need security wise or coding logic wise that requires the service to be in one of those 2 locations.
Also in case someone manually uninstalls the service but the file exists I think the check for existence of the service itself is best.

Also I had planned to change this eventually but for any future version after this first release, we'll need to use Simple SC's stop service as well before installing the service.
It's not a problem right now because nspr4.dll will never be new (since it's a first install) but there is potential right now for it being in use if a user manually started the service.

For now I'll go ahead with keeping Simple SC and also adding the Stop Service call.
But please let me know if you are strongly for not including this plug-in, then I will rethink the situation and make a different patch.
Comment 203 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 11:18:16 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #202)
> >+  SimpleSC::ExistsService "$R8"
> > Instead of using the plugin couldn't you just check for the existence of the binary since 
> > we require it to be installed into a specific location under %ProgramFiles% or %ProgramFiles(x86)%? 
> > It would require two checks but it would remove the need for the plugin and the Unicode2Ansi or recompiling the plugin as x64.
> 
> I think even for x64 compilations NSIS always produces x86 installers.
> There is no real need security wise or coding logic wise that requires the
> service to be in one of those 2 locations.
> Also in case someone manually uninstalls the service but the file exists I
> think the check for existence of the service itself is best.
> 
> Also I had planned to change this eventually but for any future version
> after this first release, we'll need to use Simple SC's stop service as well
> before installing the service.
> It's not a problem right now because nspr4.dll will never be new (since it's
> a first install) but there is potential right now for it being in use if a
> user manually started the service.
> 
> For now I'll go ahead with keeping Simple SC and also adding the Stop
> Service call.
> But please let me know if you are strongly for not including this plug-in,
> then I will rethink the situation and make a different patch.
My main concern is that this will need to work with nsis 2.33u (I don't recall if it supports ANSI plugins) since that is what is deployed to the build machines since bug 594474 hasn't been fixed yet.
Comment 204 Brian R. Bondy [:bbondy] 2011-10-27 11:20:16 PDT
> My main concern is that this will need to work with nsis 2.33u (I don't recall if > it supports ANSI plugins) since that is what is deployed to the build machines > since bug 594474 hasn't been fixed yet.

Would you be OK if I make SimpleSC part of the build process and make it unicode?
Comment 205 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 12:05:35 PDT
That won't work (at least not easily) because localizers repackage builds for locales at which time we rebuild the installer. You could convert it to unicode and checkin the binary as is done here:
http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/
Comment 206 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 13:48:16 PDT
Comment on attachment 569681 [details] [diff] [review]
Patch 15 - UAC helper functions.

>diff --git a/toolkit/components/maintenanceservice/Makefile.in b/toolkit/components/maintenanceservice/Makefile.in
>--- a/toolkit/components/maintenanceservice/Makefile.in
>+++ b/toolkit/components/maintenanceservice/Makefile.in
>@@ -44,16 +44,17 @@ include $(DEPTH)/config/autoconf.mk
> 
> CPPSRCS = \
>   maintenanceservice.cpp \
> 	serviceinstall.cpp \
> 	workmonitor.cpp \
> 	certificatecheck.cpp \
> 	servicebase.cpp \
> 	registrycertificates.cpp \
>+	uachelper.cpp \
spaces are prefered

>diff --git a/toolkit/components/maintenanceservice/uachelper.cpp b/toolkit/components/maintenanceservice/uachelper.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/uachelper.cpp
>@@ -0,0 +1,157 @@
>+/* ***** 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 UAC helper functions.
Maintenance service UAC helper functions

>+ *
>+ * The Initial Developer of the Original Code is
>+ * Mozilla Foundation.
>+ * Portions created by the Initial Developer are Copyright (C) 2011
>+ * the Initial Developer. All Rights Reserved.
>+ *
>+ * Contributor(s):
>+ *   Brian R. Bondy <netzen@gmail.com>
>+ *
>+ * 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 ***** */
>+
>+#include <windows.h>
>+#include "uachelper.h"
>+
>+typedef BOOL (WINAPI *LPWTSQueryUserToken)(ULONG, PHANDLE);
>+
>+// static
>+bool UACHelper::IsVistaOrLater() 
as mentioned earlier the function name should be on its own line here and elsewhere

>+{
>+  // Check if we are running Vista or later.
>+  OSVERSIONINFO osInfo;
>+  osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
>+  return GetVersionEx(&osInfo) && osInfo.dwMajorVersion >= 6;
>+}
>+
>+// static
>+HANDLE UACHelper::OpenUserToken(DWORD sessionID)
>+{
>+  HMODULE module = LoadLibraryW(L"wtsapi32.dll");
>+  HANDLE token = NULL;
>+  LPWTSQueryUserToken wtsQueryUserToken = (LPWTSQueryUserToken)GetProcAddress(module, "WTSQueryUserToken");
>+  if (wtsQueryUserToken) {
>+    wtsQueryUserToken(sessionID, &token);
>+  }
>+  FreeModule(module);
>+  return token;
>+}
>+
>+// static
>+HANDLE UACHelper::OpenLinkedToken(HANDLE token) 
>+{
>+  // Magic below...
>+  // UAC creates 2 tokens.  One is the restricted token which we have.
>+  // the other is the UAC elevated one. Since we are running as a service
>+  // as the system account we have access to both.
>+  TOKEN_LINKED_TOKEN tlt;
>+  HANDLE hNewLinkedToken = NULL;
>+  DWORD len;
>+  if(::GetTokenInformation(token, (TOKEN_INFORMATION_CLASS)TokenLinkedToken, 
>+    &tlt, sizeof(TOKEN_LINKED_TOKEN), &len)) {
nit: alignment

>+      token = tlt.LinkedToken;
>+      hNewLinkedToken = token;
>+  }
>+  return hNewLinkedToken;
>+}
>+
>+// static
>+bool UACHelper::IsUserAdmin(HANDLE token, BOOL &isAdmin)
>+{
>+  bool success = false;
>+  SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
>+  PSID administratorsGroup; 
>+  if (AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
>+                               DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
>+                               &administratorsGroup)) {
>+    if (CheckTokenMembership(token, administratorsGroup, &isAdmin)) {
>+      success = true;
nit I'm fine with either TRUE / FALSE or true / false for the standalone app... just be consistent across the files.

>+    } 
>+    FreeSid(administratorsGroup); 
>+  }
>+  return success;
>+}
>+
>+// static
>+bool UACHelper::GetElevationType(HANDLE token, UserType &userType)
>+{
>+  if (!IsVistaOrLater()) {
>+    BOOL isAdmin;
>+    if (!IsUserAdmin(token, isAdmin)) {
>+      return false;
>+    }
>+
>+    if (isAdmin) {
>+      userType = AdministratorUACIsOff;
>+    } else {
>+      userType = LimitedUser;
>+    }
>+    return true;
>+  }
>+
>+  // TokenElevationTypeDefault: The token does not have a linked token 
>+  // This happens when the user is a limited account or UAC is off.
>+  // TokenElevationTypeFull: The token is an elevated token.
>+  // TokenElevationTypeLimited: The token is a limited token.
>+  DWORD returnLength = 0;
>+  TOKEN_ELEVATION_TYPE elevationType;
>+  if (!::GetTokenInformation(token, TokenElevationType,
>+    &elevationType, sizeof(elevationType), &returnLength)) {
nit: alignment

>+      return false;
>+  }
>+
>+  switch (elevationType) {
nit: switch alignment as mentioned earlier

>+  case TokenElevationTypeFull:
>+    userType = AdministratorElevated;
>+    break;
>+  case TokenElevationTypeLimited:
>+    userType = AdministratorUnelevated;
>+    break;
>+  case TokenElevationTypeDefault: 
>+    {
>+      // TokenElevationTypeDefault is returned when either the user is a 
>+      // limited account or the user has UAC offa limited user or UAC is off
>+      BOOL isAdmin;
>+      if (!IsUserAdmin(token, isAdmin)) {
>+        return false;
>+      }
>+
>+      if (isAdmin) {
>+        userType = AdministratorUACIsOff;
Kind of prefer just Administrator but I am fine with this.

>+      } else {
>+        userType = LimitedUser;
>+      }
>+    }
>+    break;
>+  default:
>+    return false;
>+  }
>+
>+  // If we had an error we would have returned already
>+  return true;
>+}
Comment 207 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 15:35:50 PDT
Comment on attachment 568691 [details] [diff] [review]
Patch 13 - Moving the update check after init

I don't see any other way we can accomplish this reasonably... we'll just need to keep a close eye on this.
Comment 208 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 16:59:12 PDT
Comment on attachment 568693 [details] [diff] [review]
Patch 14 - Build process

>diff --git a/browser/Makefile.in b/browser/Makefile.in
>--- a/browser/Makefile.in
>+++ b/browser/Makefile.in
>@@ -65,11 +65,11 @@ include $(topsrcdir)/config/rules.mk
> 
> ifeq ($(OS_ARCH),WINNT)
> ifdef MOZ_INSTALLER
> 
> # For Windows build the uninstaller during the application build since the
> # uninstaller is included with the application for mar file generation.
> libs::
> 	$(MAKE) -C installer/windows uninstaller
>-
>+	$(MAKE) -C installer/windows maintenanceservice_installer
> endif
> endif
>diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
>--- a/browser/installer/package-manifest.in
>+++ b/browser/installer/package-manifest.in
>@@ -28,16 +28,17 @@
> @BINPATH@/defaults/profile/mimeTypes.rdf
> @BINPATH@/defaults/profile/chrome/*
> @BINPATH@/update.locale
> @BINPATH@/updater.ini
> @BINPATH@/dictionaries/*
> @BINPATH@/hyphenation/*
> #ifdef XP_WIN32
> @BINPATH@/uninstall/helper.exe
>+@BINPATH@/maintenanceservice_installer/maintenanceservice_installer.exe
Per phone conversation, no maintenanceservice_installer directory and it appears to be proper below.

> #endif
> 
> [xpcom]
> @BINPATH@/dependentlibs.list
> #ifndef MOZ_STATIC_JS
> @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
> #endif
> @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
>@@ -522,16 +523,22 @@ bin/libfreebl_32int64_3.so
> ; [Updater]
> ;
> #ifdef XP_MACOSX
> @BINPATH@/updater.app/
> #else
> @BINPATH@/updater@BIN_SUFFIX@
> #endif
> 
>+; [MaintenanceService]
>+;
>+#ifdef XP_WIN32
>+@BINPATH@/maintenanceservice.exe
>+#endif
>+

>diff --git a/browser/installer/windows/Makefile.in b/browser/installer/windows/Makefile.in
>--- a/browser/installer/windows/Makefile.in
>+++ b/browser/installer/windows/Makefile.in
>...
>@@ -70,16 +71,21 @@ BRANDING_FILES = \
> 
> DEFINES += \
> 	-DAB_CD=$(AB_CD) \
> 	-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
> 	-DMOZ_APP_DISPLAYNAME=${MOZ_APP_DISPLAYNAME} \
> 	-DMOZILLA_VERSION=${MOZILLA_VERSION} \
> 	$(NULL)
> 
>+MAINTENANCESERVICE_FILES = \
>+	maintenanceservice.exe \
>+	nspr4.dll \
>+	$(NULL)
Needs to be updated per discussion

>@@ -99,16 +105,30 @@ uninstaller::
> 	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
> 	$(INSTALL) $(addprefix $(DIST)/branding/,$(BRANDING_FILES)) $(CONFIG_DIR)
> 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) \
> 	  $(srcdir)/nsis/defines.nsi.in > $(CONFIG_DIR)/defines.nsi
> 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
> 	  --preprocess-locale $(topsrcdir) \
> 	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
> 
>+# For building the maintenanceservice installer
>+maintenanceservice_installer::
>+	$(RM) -r $(CONFIG_DIR)
>+	$(MKDIR) $(CONFIG_DIR)
>+	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
>+	$(INSTALL) $(addprefix $(DIST)/branding/,$(BRANDING_FILES)) $(CONFIG_DIR)
>+	$(INSTALL) $(addprefix $(DIST)/bin/,$(MAINTENANCESERVICE_FILES)) $(CONFIG_DIR)
>+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py -Fsubstitution $(DEFINES) $(ACDEFINES) \
>+	  $(srcdir)/nsis/defines.nsi.in > $(CONFIG_DIR)/defines.nsi
>+	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
>+	  --preprocess-locale $(topsrcdir) \
>+	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
Needs to be updated per discussion

>diff --git a/mobile/installer/package-manifest.in b/mobile/installer/package-manifest.in
>--- a/mobile/installer/package-manifest.in
>+++ b/mobile/installer/package-manifest.in
>@@ -30,16 +30,17 @@
> #ifdef MOZ_UPDATER
> @BINPATH@/update.locale
> @BINPATH@/updater.ini
> #endif
> @BINPATH@/dictionaries/*
> @BINPATH@/hyphenation/*
> #ifdef XP_WIN32
> @BINPATH@/uninstall/helper.exe
>+@BINPATH@/maintenanceservice_installer/maintenanceservice_installer.exe
Mobile?

>diff --git a/toolkit/components/maintenanceservice/Makefile.in b/toolkit/components/maintenanceservice/Makefile.in
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/Makefile.in
>@@ -0,0 +1,105 @@
>+# ***** 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 Application Update Service.
Maintenance service.

>+include $(DEPTH)/config/autoconf.mk
>+
>+CPPSRCS = \
>+  maintenanceservice.cpp \
>+	serviceinstall.cpp \
>+	workmonitor.cpp \
>+	certificatecheck.cpp \
>+	servicebase.cpp \
>+	registrycertificates.cpp \
>+  $(NULL)
Just use spaces... iirc this is per Ted and / or Kyle

>+
>+ifneq ($(OS_TARGET),Android)
>+PROGRAM = maintenanceservice$(BIN_SUFFIX)
>+DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX)
>+
>+# Don't link the maintenanceservice against libmozutils. See bug 687139
>+MOZ_UTILS_LDFLAGS =
>+MOZ_UTILS_PROGRAM_LDFLAGS =
>+endif
>+
>+LIBS += \
>+  $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX) \
>+  $(BZ2_LIBS) \
>+  $(NSPR_LIBS) \
>+  $(NULL)
>+
>+ifeq ($(OS_ARCH),WINNT)
>+USE_STATIC_LIBS = 1
>+HAVE_PROGRESSUI = 1
>+RCINCLUDE = maintenanceservice.rc
>+
>+OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
>+DEFINES += -DUNICODE -D_UNICODE
>+ifndef GNU_CC
>+RCFLAGS += -I$(srcdir)
>+else
>+RCFLAGS += --include-dir $(srcdir)
>+endif
>+
>+endif
>+
>+ifndef MOZ_WINCONSOLE
>+ifdef MOZ_DEBUG
>+MOZ_WINCONSOLE = 1
>+else
>+MOZ_WINCONSOLE = 0
>+endif
>+endif
>+
>+include $(topsrcdir)/config/rules.mk
>+
>+DEFINES += -DNS_NO_XPCOM
>+
>+ifdef _MSC_VER
>+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
>+endif
>+
>+ifeq (,$(filter-out WINNT,$(OS_ARCH)))
>+# Pick up nsWindowsRestart.cpp
>+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
>+endif
>+
>+CXXFLAGS += $(BZ2_CFLAGS)
This looks like it was copied and has a bunch of cruft
Comment 209 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 17:00:21 PDT
Comment on attachment 568693 [details] [diff] [review]
Patch 14 - Build process

>diff --git a/toolkit/locales/l10n.mk b/toolkit/locales/l10n.mk
>--- a/toolkit/locales/l10n.mk
>+++ b/toolkit/locales/l10n.mk
>@@ -139,16 +139,18 @@ endif
> repackage-zip: UNPACKAGE="$(ZIP_IN)"
> repackage-zip:  libs-$(AB_CD)
> # Adjust jar logs with the new locale (can't use sed -i because of bug 373784)
> 	mkdir -p $(JARLOG_DIR_AB_CD)
> 	-cp -r $(JARLOG_DIR)/en-US/*.jar.log $(JARLOG_DIR_AB_CD)
> 	-$(PERL) -pi.old -e "s/en-US/$(AB_CD)/g" $(JARLOG_DIR_AB_CD)/*.jar.log
> # call a hook for apps to put their uninstall helper.exe into the package
> 	$(UNINSTALLER_PACKAGE_HOOK)
>+# call a hook for apps to put their maintenance service into the package
>+	$(MAINTENANCESERVICE_PACKAGE_HOOK)
Not all apps will build the maintenance service.

>diff --git a/toolkit/mozapps/installer/windows/nsis/makensis.mk b/toolkit/mozapps/installer/windows/nsis/makensis.mk
>--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
>+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
>@@ -55,25 +55,34 @@ TOOLKIT_NSIS_FILES = \
> 
> CUSTOM_NSIS_PLUGINS = \
> 	AccessControl.dll \
> 	AppAssocReg.dll \
> 	ApplicationID.dll \
> 	InvokeShellVerb.dll \
> 	ShellLink.dll \
> 	UAC.dll \
>+	SimpleSC.dll \
> 	$(NULL)
> 
>+MAINTENANCESERVICE_FILES = \
>+	maintenanceservice.exe \
>+	nspr4.dll \
>+	$(NULL)
Per phone discussion, just use the files in the root of the app dir

>+
>+
> $(CONFIG_DIR)/setup.exe::
> 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
> 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
> 	cd $(CONFIG_DIR) && $(MAKENSISU) installer.nsi
>-# Support for building the uninstaller when repackaging locales
>+# Support for building the uninstaller and maintenance service when repackaging locales
> ifeq ($(CONFIG_DIR),l10ngen)
>+	$(INSTALL) $(addprefix $(DIST)/bin/,$(MAINTENANCESERVICE_FILES)) $(CONFIG_DIR)
Should not be necessary. Also, you can't install it before building it which happens after this

> 	cd $(CONFIG_DIR) && $(MAKENSISU) uninstaller.nsi
>+	cd $(CONFIG_DIR) && $(MAKENSISU) maintenanceservice_installer.nsi
This should only be called when the app has maintenanceservice_installer.nsi

> endif
> 
> $(CONFIG_DIR)/7zSD.sfx:
> 	$(CYGWIN_WRAPPER) upx --best -o $(CONFIG_DIR)/7zSD.sfx $(SFX_MODULE)
> 
> installer::
> 	$(INSTALL) $(CONFIG_DIR)/setup.exe $(DEPTH)/installer-stage
> 	cd $(DEPTH)/installer-stage && $(CYGWIN_WRAPPER) 7z a -r -t7z $(ABS_CONFIG_DIR)/app.7z -mx -m0=BCJ2 -m1=LZMA:d24 -m2=LZMA:d19 -m3=LZMA:d19  -mb0:1 -mb0s1:2 -mb0s2:3
>@@ -85,8 +94,17 @@ installer::
> # For building the uninstaller during the application build so it can be
> # included for mar file generation.
> uninstaller::
> 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
> 	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
> 	cd $(CONFIG_DIR) && $(MAKENSISU) uninstaller.nsi
> 	$(NSINSTALL) -D $(DIST)/bin/uninstall
> 	cp $(CONFIG_DIR)/helper.exe $(DIST)/bin/uninstall
>+
>+# For building the maintenanceservice installer during the application build
>+maintenanceservice_installer::
>+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
>+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
>+	$(INSTALL) $(addprefix $(DIST)/bin/,$(MAINTENANCESERVICE_FILES)) $(CONFIG_DIR)
Should not be necessary

>+	cd $(CONFIG_DIR) && $(MAKENSISU) maintenanceservice_installer.nsi
>+	$(NSINSTALL) -D $(DIST)/bin/maintenanceservice_installer
>+	cp $(CONFIG_DIR)/maintenanceservice_installer.exe $(DIST)/bin/maintenanceservice_installer
Should be in the root of the app dir

I *think* having a separate target is the right choice so other apps that don't use the service don't try to build it but the $(CONFIG_DIR)/setup.exe target builds maintenanceservice_installer.exe so that will break other apps.
Comment 210 Robert Strong [:rstrong] (use needinfo to contact me) 2011-10-27 17:01:14 PDT
Comment on attachment 568689 [details] [diff] [review]
Patch 11 - New maintenance service installer

Needs updating per phone discussion
Comment 211 Brian R. Bondy [:bbondy] 2011-10-27 17:47:05 PDT
Created attachment 570127 [details] [diff] [review]
Patch 15 - UAC helper functions. v2.

Implemented review comments, added javadoc comments and other formatting fixes.  r+'ed myself since it was already r+.  rs, let me know if you have any extra comments though you want me to change.
Comment 212 Brian R. Bondy [:bbondy] 2011-10-27 18:40:13 PDT
Created attachment 570143 [details] [diff] [review]
Patch 1 - Base service code. v3.

Fixed bool/BOOL consistency and return type of functions on its own line
Comment 213 Brian R. Bondy [:bbondy] 2011-10-27 18:43:14 PDT
Created attachment 570145 [details] [diff] [review]
Patch 2 - Main service logic. v4

Fixed bool/BOOL consistency and return type of functions on its own line.
Comment 214 Brian R. Bondy [:bbondy] 2011-10-27 18:46:30 PDT
Created attachment 570146 [details] [diff] [review]
Patch 3 - Code for installing the service. v3.

Fixed bool/BOOL consistency and return type of functions on its own line.
Comment 215 Brian R. Bondy [:bbondy] 2011-10-27 18:51:54 PDT
Comment on attachment 569895 [details] [diff] [review]
Patch 4 - Certificate check code inside service. v2.

Cancelling review on this until I implement changes as per discussion on phone with registry layout (hashing per install path).
Comment 216 Brian R. Bondy [:bbondy] 2011-10-27 18:54:09 PDT
Created attachment 570148 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v4.

Fixed return type of functions on its own line.
Comment 217 Brian R. Bondy [:bbondy] 2011-10-27 18:57:23 PDT
Comment on attachment 568950 [details] [diff] [review]
Patch 6 - Firefox preferences update page changes. v2.

As per the review on patch 6 previously:

> As stated in the last patch, I'm not certain this pref will be 
> available when the update is applied during startup.
> minusing until the pref issue is figured out.

re-marking as r? since I think we're going ahead with this method now.
Comment 218 Brian R. Bondy [:bbondy] 2011-10-27 19:00:39 PDT
Created attachment 570153 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v2

Rebased.
Comment 219 Brian R. Bondy [:bbondy] 2011-10-28 09:23:23 PDT
Created attachment 570282 [details] [diff] [review]
Patch 15 - UAC helper functions. v2'.

Moved out Makefile.in to patch 14.  Was already r+, remarking.
Comment 220 Brian R. Bondy [:bbondy] 2011-10-28 09:31:44 PDT
Turns out SimpleSC is pascal and hence why no one has tried to port it to Unicode.  I'm going to go ahead and make my own plugin, I've made some before so should only take a couple hours.
Comment 221 Brian R. Bondy [:bbondy] 2011-10-28 18:29:39 PDT
Created attachment 570440 [details] [diff] [review]
Patch 12 - Simple SC plugin inclusion. v2.

- Removed SimpleSC.
- Created new plugin ServicesHelper.  

This is created from scratch so I'm not sure of the best location in the tree.
It doesn't seem ideal to put it under other-licenses/nsis/Contrib/ but to keep it alongside the other plugins I think that is best.  Let me know if you want me to move it somewhere else.
Comment 222 Brian R. Bondy [:bbondy] 2011-10-28 18:32:37 PDT
Created attachment 570441 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v3.

- Removed passing /S to the installer since it is silent now for installation.
- Now use the new plugin ServicesHelper instead of SimpleSC.
- Review comments implemented.
Comment 223 Brian R. Bondy [:bbondy] 2011-10-28 18:36:59 PDT
Created attachment 570443 [details] [diff] [review]
Patch 11 - New maintenance service installer. v2.

- Now use the new plugin ServicesHelper.
- Made maintenanceservice installer always silent.
- Fixed up uninstaller to be at par with Firefox uninstaller.
- Review comments implemented.
Comment 224 Brian R. Bondy [:bbondy] 2011-10-28 18:41:42 PDT
Created attachment 570444 [details] [diff] [review]
Patch 14 - Build process. v2.

- Review comments implemented
- removed extra folder for maintenanceservice_installer throughout
Comment 225 Brian R. Bondy [:bbondy] 2011-10-28 18:46:46 PDT
Comment on attachment 567667 [details]
Screenshot: New installer step inside Firefox installer

You previously r+'ed this but rs mentioned he wanted you to specifically r+ the text I came up with.  If you have any other suggestions please let me know.

+COMPONENTS_PAGE_TITLE=Set Up Optional Components
+COMPONENTS_PAGE_SUBTITLE=Optional Recommended Components
+OPTIONAL_COMPONENTS_DESC=The Maintenance Service will allow you to update $BrandShortName silently in the background.
+MAINTENANCE_SERVICE_CHECKBOX_DESC=Install &Maintenance Service
Comment 226 Brian R. Bondy [:bbondy] 2011-10-29 11:16:54 PDT
Created attachment 570493 [details] [diff] [review]
Patch 14 - Build process. v3.

Minor change to fix build when only building maintenanceservice_installer target
Comment 227 Brian R. Bondy [:bbondy] 2011-10-29 11:24:10 PDT
Created attachment 570494 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v3'.

Added extra comment for clarity.
Comment 228 Brian R. Bondy [:bbondy] 2011-10-29 11:27:01 PDT
Created attachment 570495 [details] [diff] [review]
Patch 11 - New maintenance service installer. v3.

Added code for setting a registry DWORD to indicate that the maintenanceservice was previously installed.  This is to ensure we never re-install on upgrade after the first time instsall.
Comment 229 Brian R. Bondy [:bbondy] 2011-10-29 11:30:33 PDT
Created attachment 570496 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade. v2.

- Added code to ensure that the maintenance service installer is launched exactly once for all future upgrades.
- Review comments implemented
Comment 230 Brian R. Bondy [:bbondy] 2011-10-29 17:06:56 PDT
Created attachment 570507 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v4.

minor fix.
Comment 231 Brian R. Bondy [:bbondy] 2011-10-29 21:24:31 PDT
Created attachment 570517 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v3.

Same functionality as before just did some extra refactoring for a callback app function.
Comment 232 Brian R. Bondy [:bbondy] 2011-10-29 21:26:46 PDT
Created attachment 570518 [details] [diff] [review]
Patch 17 - New update.status for when service fails to force no service update

XRE and udpater js code for the new update.status value of "pending-no-service".
If this value is set then the service will not be attempted for updating.  
This status is set by the service when there is an error.
Comment 233 Brian R. Bondy [:bbondy] 2011-10-29 21:29:11 PDT
Created attachment 570519 [details] [diff] [review]
Patch 18 - Write out update.status error code in maintenance service on failure

Now write out the update.status "pending-no-service" code when the service fails. This works very nicely because the callback app is called right after this. 
This means that if the service fails, the user doens't need to start Firefox a second time, it will just look as if the user had a UAC prompt to begin with.
Comment 234 Brian R. Bondy [:bbondy] 2011-10-29 21:43:41 PDT
Created attachment 570522 [details] [diff] [review]
Patch 19 - Create the updatedir from the installer and set permissions on it to full access

As discussed we create the update dir from the maintenanceservice installer now and set the permissions on it with the AccessControl plugin.
Comment 235 Brian R. Bondy [:bbondy] 2011-10-30 06:27:28 PDT
Existing try tests for the 19 patches looking good, altough I haven't created my own new ones yet:
https://tbpl.mozilla.org/?tree=Try&rev=9d974e9b43d2
Comment 236 Brian R. Bondy [:bbondy] 2011-10-30 20:19:57 PDT
Created attachment 570595 [details] [diff] [review]
Patch 20 - Path hashing for unique registry location
Comment 237 Brian R. Bondy [:bbondy] 2011-10-30 20:21:14 PDT
Created attachment 570596 [details] [diff] [review]
Patch 12 - Simple SC plugin inclusion. v3.

Added in new NSIS plugin function for getting cert location based on hash.  Also compiled the plugin using VC++ 6.0 so it's a smaller binary.  (I found some old CDs and installed on an XP machine to build it).
Comment 238 Brian R. Bondy [:bbondy] 2011-10-30 20:22:24 PDT
Created attachment 570597 [details] [diff] [review]
Patch 18 - Write out update.status error code in maintenance service on failure. v2.

Rebasing (Added in base installer path parameter for registry cert check.)
Comment 239 Brian R. Bondy [:bbondy] 2011-10-30 20:23:45 PDT
Created attachment 570598 [details] [diff] [review]
Patch 4 - Certificate check code inside service. v3.

No longer use hardcoded registry base path, get it from the unique registry location calculated from base path for update instead.
Comment 240 Brian R. Bondy [:bbondy] 2011-10-30 20:25:09 PDT
Created attachment 570599 [details] [diff] [review]
Patch 11 - New maintenance service installer. v4.

Same as before but removed the setting allowed cert from the maintenance service installer.  It belongs inside the firefox installer and per product installers.
Comment 241 Brian R. Bondy [:bbondy] 2011-10-30 20:27:02 PDT
Created attachment 570600 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v5.

- Firefox installer now registers its own allowed certificate to the hashed registry path
- Firefox uninstaller now removes it from the hashed registry path
Comment 242 Brian R. Bondy [:bbondy] 2011-10-30 20:28:26 PDT
Created attachment 570601 [details] [diff] [review]
Patch 19 - Create the updatedir from the installer and set permissions on it to full access. v1'.

Minor rebasing to remove fuzz.
Comment 243 Brian R. Bondy [:bbondy] 2011-10-30 20:39:01 PDT
Overall state of this task:

Other than future review comments, all known work for this bug is done.
I still need to test on a variety of different platform and different UAC settings but it's all working nicely on Win7 with the default UAC level.

Everything works best if you define DISABLE_SERVICE_AUTHENTICODE_CHECK since we don't have a signed updater.exe yet on Nightly.  Also this allows the callback to be called since the Nightly firefox.exe isn't signed (and won't be signed with the temp workaround).  The temp workaround of committing a frozen updater.exe will make updates work even if DISABLE_SERVICE_AUTHENTICODE_CHECK  is not defined.

Things to decide before we land:
We need to decide if we want the callback check to be always disabled until we have nightly auto signed, or if we want to NOT call the callback app after updates because it is not signed.  I.e. after this lands, an updated firefox.exe won't be relaunched currently on Nightly if DISABLE_SERVICE_AUTHENTICODE_CHECK is not defined because it is not signed.  We can't use the temp workaround (of committing a frozen) on firefox.exe because it changes too frequently. We can either 1) NOT do the sign check for the callback app (a security problem) or 2) have it not auto relaunch firefox.exe after updates on Nightly.   This concern is all temporary until Nightly gets auto signed.
Comment 244 Ian Melven :imelven 2011-10-31 10:16:22 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #243)
> Things to decide before we land:
> We need to decide if we want the callback check to be always disabled until
> we have nightly auto signed, or if we want to NOT call the callback app
> after updates because it is not signed.  I.e. after this lands, an updated
> firefox.exe won't be relaunched currently on Nightly if
> DISABLE_SERVICE_AUTHENTICODE_CHECK is not defined because it is not signed. 
> We can't use the temp workaround (of committing a frozen) on firefox.exe
> because it changes too frequently. We can either 1) NOT do the sign check
> for the callback app (a security problem) or 2) have it not auto relaunch
> firefox.exe after updates on Nightly.   This concern is all temporary until
> Nightly gets auto signed.

#2 is a pretty bad user experience, but i'm still pretty concerned about running unsigned callback app's in another user's session. even with requiring the signed callback app, you could possibly downgrade another user's Firefox perhaps.
Comment 245 Jim Mathies [:jimm] 2011-10-31 11:25:45 PDT
Comment on attachment 568684 [details] [diff] [review]
Patch 7 - RAII base helpers

Review of attachment 568684 [details] [diff] [review]:
-----------------------------------------------------------------

::: xpcom/base/nsAutoHandle.h
@@ +44,5 @@
> +  nsAutoHandle(HANDLE handle = INVALID_HANDLE_VALUE) : mHandle(handle)
> +  {
> +  }
> +
> +  void forget() 

In XPCOM land, forget() implies the caller is taking ownership of the value so the container should simply forget it (without a close or release). Perhaps these should be something like 'close()' so people don't mistake what they do.

::: xpcom/base/nsAutoRegKey.h
@@ +35,5 @@
> + *
> + * ***** END LICENSE BLOCK ***** */
> +
> +#ifndef nsAutoHandle_h___
> +#define nsAutoHandle_h___

nit - nsAutoRegKey_h___
Comment 246 Ben Turner (not reading bugmail, use the needinfo flag!) 2011-10-31 11:44:44 PDT
Comment on attachment 568684 [details] [diff] [review]
Patch 7 - RAII base helpers

Could these be replaced with nsAutoRef specializations? See nsAutoRef.h
Comment 247 Jim Mathies [:jimm] 2011-10-31 12:08:54 PDT
Comment on attachment 570596 [details] [diff] [review]
Patch 12 - Simple SC plugin inclusion. v3.

Review of attachment 570596 [details] [diff] [review]:
-----------------------------------------------------------------

::: other-licenses/nsis/Contrib/ServicesHelper/Services.cpp
@@ +62,5 @@
> +  SC_HANDLE serviceManager = ::OpenSCManager(NULL, NULL, 
> +                                             SC_MANAGER_ENUMERATE_SERVICE);
> +  if (!serviceManager) {
> +    exists = FALSE;
> +    return FALSE;

Why don't you init exists = FALSE; at the top, then there's no risk of leaking this without a set, and you can ditch the paren here and the assignments to FALSE below. Which should clean things up a bit.

@@ +126,5 @@
> +static BOOL
> +StopService(LPCWSTR serviceName)
> +{
> +  // Get a handle to the local computer SCM database with full access rights.
> +  SC_HANDLE serviceManager = ::OpenSCManager(NULL, NULL, 

nit - you're using '::' here and in some cases your not. I'd suggest either ditching the use or use it everywhere. Your call.

@@ +148,5 @@
> +  static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
> +  BOOL stopped = FALSE;
> +  if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, &status)) {
> +    do {
> +      Sleep(status.dwWaitHint);

So if we get hung up in here for a while, what happens? Does the install UI freeze, or is this only called from the background? If there's UI dependent on this, maybe we need to be spinning a dispatch loop in here?
Comment 248 Brian R. Bondy [:bbondy] 2011-10-31 13:51:48 PDT
Created attachment 570835 [details] [diff] [review]
Patch 12 - NSIS plugin for working with services. v4.

- Removed :: throughout since it would be an uphill battle to keep doing it
- Did suggestion for exists = FALSE at the start

> So if we get hung up in here for a while, what happens? 
> Does the install UI freeze, or is this only called from the background? 
> If there's UI dependent on this, maybe we need to be spinning a dispatch loop in here?
  
It never takes longer than instant for me, but I did put a ::Sleep for 15 seconds for testing purposes to see what happens.
The UI does not freeze and you can move the window around fine so it's not a problem like it would be by default on Win32 on the UI thread.
Comment 249 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-01 11:46:33 PDT
(In reply to ben turner [:bent] from comment #246)
> Comment on attachment 568684 [details] [diff] [review] [diff] [details] [review]
> Patch 7 - RAII base helpers
> 
> Could these be replaced with nsAutoRef specializations? See nsAutoRef.h
Brian, after answering this question please request sr from bsmedberg. Thanks
Comment 250 Brian R. Bondy [:bbondy] 2011-11-01 11:58:59 PDT
> Could these be replaced with nsAutoRef specializations? See nsAutoRef.h
> Brian, after answering this question please request sr from bsmedberg. Thanks

Yes they can, I just finished replacing them and will re-review jimm and if he r+ I'll sr? bsmedberg.  Thanks for the suggestion Ben T.
Comment 251 Brian R. Bondy [:bbondy] 2011-11-01 12:13:40 PDT
Created attachment 571099 [details] [diff] [review]
Patch 7 - RAII base helpers. v2.

Recoded these so they are specializations of nsAutoRef.
Comment 252 Brian R. Bondy [:bbondy] 2011-11-01 12:27:50 PDT
Created attachment 571105 [details] [diff] [review]
Patch 3 - Code for installing the service. v3'.

Same as last patch, just updated syntax at a couple places for nsAutoRef based RAII helpers.
Comment 253 Brian R. Bondy [:bbondy] 2011-11-01 12:36:40 PDT
Created attachment 571107 [details] [diff] [review]
Patch 18 - Write out update.status error code in maintenance service on failure. v2'.

Same as last patch, just updated syntax at a couple places for nsAutoRef based RAII helpers.
Comment 254 Brian R. Bondy [:bbondy] 2011-11-01 12:40:42 PDT
Created attachment 571110 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v4'.

Same as last patch, just updated syntax at a couple places for nsAutoRef based RAII helpers.
Comment 255 Jim Mathies [:jimm] 2011-11-01 12:43:38 PDT
Comment on attachment 570601 [details] [diff] [review]
Patch 19 - Create the updatedir from the installer and set permissions on it to full access. v1'.

So there's an open question here whether we should break this down per user. bbondy will kick off a security review on this with some detail on what goes in here. Code looks ok though for what we are currently doing.
Comment 256 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-01 12:50:32 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #243)
I'd prefer disabling the check for now so it relaunches Firefox. It would be best to run this by the security team first.
Comment 257 Brian R. Bondy [:bbondy] 2011-11-01 12:53:14 PDT
Created attachment 571116 [details] [diff] [review]
Patch 4 - Certificate check code inside service. v3'.

Same as last patch, just updated syntax at a couple places for nsAutoRef based RAII helpers.
Comment 258 Brian R. Bondy [:bbondy] 2011-11-01 12:59:24 PDT
Comment on attachment 570601 [details] [diff] [review]
Patch 19 - Create the updatedir from the installer and set permissions on it to full access. v1'.

Ian we'd like a security review on this patch. 

We aren't sure which user will initially create that directory, so to ensure that there are no issues with non-owners being able to write to it we set full access on that directory.

Work items for the service go into that directory.
I'm not sure if there are any security concerns for putting full access on a folder like that.
Comment 259 Brian R. Bondy [:bbondy] 2011-11-01 20:14:53 PDT
Created attachment 571225 [details] [diff] [review]
Patch 11 - New maintenance service installer. v5.

Fixed bug found when testing upgrades of the maint service itself.
Comment 260 Brian R. Bondy [:bbondy] 2011-11-02 07:44:33 PDT
Comment on attachment 570496 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade. v2.

I'm going to move the updating of the service itself to the end of the service cpp code itself.  After it calls the callback app it'll launch maintenanceservice installer itself.  Doing it from the /PostUpdate won't work because that happens before the callback is launched.  And we launch the callback from within the service.  

I will re-upload this patch later today with the code from shared.nsh removed and a new small patch 21 which does the self update.
Comment 261 Brian R. Bondy [:bbondy] 2011-11-02 09:04:41 PDT
Created attachment 571348 [details] [diff] [review]
Patch 10 - Shared installer code and service upgrade. v3.

Updated code to not do update from /PostUpdate.  See patch 21 for new way service self updates itself.
Comment 262 Brian R. Bondy [:bbondy] 2011-11-02 09:08:15 PDT
Created attachment 571349 [details] [diff] [review]
Patch 21 - New service self update code
Comment 263 Brian R. Bondy [:bbondy] 2011-11-02 13:01:29 PDT
Created attachment 571427 [details] [diff] [review]
Patch 11 - New maintenance service installer. v5'.

Same logic as last patch, just fixed the file's properties. Company name was showing up as  ${CompanyName} before this patch.
Comment 264 Brian R. Bondy [:bbondy] 2011-11-02 14:08:01 PDT
I added writing the update.status file to "pending-no-service" before we launch the service work-item command.  This is basically a catch all to ensure we don't get into a situation were the service always fails (example a crash in the service) when applying updates.  So basically for a single update we will only ever try to use the service at most once. If it fails we automatically go back to the old method.

Updated patch for that coming later today.
Comment 265 Jim Mathies [:jimm] 2011-11-02 14:18:55 PDT
Comment on attachment 570595 [details] [diff] [review]
Patch 20 - Path hashing for unique registry location

Review of attachment 570595 [details] [diff] [review]:
-----------------------------------------------------------------

I plucked this out and ran it in a stand alone app, didn't see any issues. Discussed with Brian possibly using some of our existing md5 code, but in the end we both agreed we liked the self contained nature of this as is and didn't want to hassle with linking up other libraries.

::: toolkit/components/maintenanceservice/pathhash.cpp
@@ +52,5 @@
> +                      LPWSTR hexString)
> +{
> +  WCHAR *p = hexString;
> +  for (DWORD i = 0; i < hashSize; ++i) {
> +    wsprintfW(p, L"%.2x",hash[i]);

silly nit - space after 2x",

@@ +130,5 @@
> +CalculateRegistryPathFromFilePath(const LPCWSTR filePath, 
> +                                  LPWSTR registryPath)
> +{
> +  size_t filePathLen = wcslen(filePath); 
> +  if (filePathLen == 0)

nit - if (!filePathLen)

@@ +149,5 @@
> +  }
> +  
> +  LPCWSTR baseRegPath = L"SOFTWARE\\Mozilla\\"
> +                        L"MaintenanceService\\";
> +  wcsncpy(registryPath, baseRegPath, MAX_PATH);

To avoid the depreciated and security warnings, I think we can use wcsncpy_s here, which was available in vc 2005 and up.
Comment 266 Jim Mathies [:jimm] 2011-11-02 14:32:15 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #257)
> Created attachment 571116 [details] [diff] [review] [diff] [details] [review]
> Patch 4 - Certificate check code inside service. v3'.
> 
> Same as last patch, just updated syntax at a couple places for nsAutoRef
> based RAII helpers.

Having mucked around with the nightmare that is WinVerifyTrust in a previous life, this usage looked ok to me.
Comment 267 Ian Melven :imelven 2011-11-02 15:54:11 PDT
Comment on attachment 570601 [details] [diff] [review]
Patch 19 - Create the updatedir from the installer and set permissions on it to full access. v1'.

The security team discussed this at our team meeting today. Due to the MAR files themselves not being signed, it seems like a malicious local user (or malware, to whom this would be very attractive) could create a work item that's a self-extracting exe containing a signed updater.exe and a bogus MAR file that would install something evil. so we would obviously prefer that the directory not be world writeable. 

Additionally, if the assumption that the callback app from the work item is a path that is executed as the callback, I'm now less worried about the callback app not being signed since it can only be an existing executable that's run in the arbitrary user's session (it's still pretty bad if the work item can pass arguments to the executable though e.g. del /s c:)

we are happy to meet to discuss further if needed :)
Comment 268 Ian Melven :imelven 2011-11-02 15:55:37 PDT
another followup question that came up as well : how does the installer know which Firefox to update if there's more than one installed on the system ? is this one of the arguments/parameters in the work item ?
Comment 269 Brian R. Bondy [:bbondy] 2011-11-02 16:47:10 PDT
Created attachment 571505 [details] [diff] [review]
Patch 22 - Always set pending-no-service when using service to handle unhandled failures

- Moved function WriteStatusPendingNoService to common nsWindowsRestart.cpp code 
- Now set this status before even attempting to use the service in case there is some kind of unhandled failure or bug
Comment 270 Brian R. Bondy [:bbondy] 2011-11-02 17:12:31 PDT
> another followup question that came up as well : how does the installer know
> which Firefox to update if there's more than one installed on the system ? 
> is this one of the arguments/parameters in the work item ?

Yes it is. Here is the command line format of updater.exe. All parameters are passed to the service via work item file and are relayed to start updater.exe inside the service.

updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]



> The security team discussed this at our team meeting today. 
> Due to the MAR files themselves not being signed, it seems like a 
> malicious local user (or malware, to whom this would be very attractive) 
> could create a work item that's a self-extracting exe containing a 
> signed updater.exe and a bogus MAR file that would install something 
> evil. so we would obviously prefer that the directory not be world writeable. 

Even without this patch setting full access on the directory, any directory under c:\programData is writable by any user, even limited user accounts.  Do you suggest to instead monitor per user app data folders and only allow sessions belonging to those users to be used? or?
Comment 271 Brian R. Bondy [:bbondy] 2011-11-02 17:15:42 PDT
@@ +149,5 @@
> +  }
> +  
> +  LPCWSTR baseRegPath = L"SOFTWARE\\Mozilla\\"
> +                        L"MaintenanceService\\";
> +  wcsncpy(registryPath, baseRegPath, MAX_PATH);

> To avoid the depreciated and security warnings, I think we can use wcsncpy_s 
> here, which was available in vc 2005 and up.

This file in particular is compiled in vc6 (pre vc2005) because of the plugin small file size.   It is also compiled shared and compiled in FF.  I'll do other nits but will leave that one because of that reason.
Comment 272 Daniel Veditz [:dveditz] 2011-11-02 17:34:53 PDT
Even if the temp directory were only user writable it's still a vector for a malware install to elevate to system privilege. If we aren't sure we're installing our stuff then this service is dangerous.
Comment 273 Brian R. Bondy [:bbondy] 2011-11-02 18:13:01 PDT
Seems we'll have to do the MAR file signing (follow up task), then land them at the same time.
Comment 274 Brian R. Bondy [:bbondy] 2011-11-02 18:42:26 PDT
Created attachment 571532 [details] [diff] [review]
Patch 20 - Path hashing for unique registry location. v2.

Implemented review nits.
Comment 275 Chris AtLee [:catlee] 2011-11-02 18:49:03 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #273)
> Seems we'll have to do the MAR file signing (follow up task), then land them
> at the same time.

Any thoughts on how to do this? MAR files currently have no support for attaching signatures, and we've never signed mar files before other than generating detached gpg signatures.
Comment 276 Brian R. Bondy [:bbondy] 2011-11-02 19:03:33 PDT
> Any thoughts on how to do this? MAR files currently have no support for 
> attaching signatures, and we've never signed mar files before other 
> than generating detached gpg signatures

I'll let security chime in here but from my understanding we would encrypt the mar file with our private key before deployment.  Then it gets downloaded and decrypted with our public key by updater.exe.
Comment 277 Ian Melven :imelven 2011-11-02 19:50:07 PDT
(In reply to Brian R. Bondy [:bbondy] from comment #276)
> > Any thoughts on how to do this? MAR files currently have no support for 
> > attaching signatures, and we've never signed mar files before other 
> > than generating detached gpg signatures
> 
> I'll let security chime in here but from my understanding we would encrypt
> the mar file with our private key before deployment.  Then it gets
> downloaded and decrypted with our public key by updater.exe.

personally i would be fine with using our certs to verify the mar file. i know that's not necessarily doable for nightly at the moment as we've discussed previously.
Comment 278 Brian R. Bondy [:bbondy] 2011-11-03 06:43:00 PDT
Created attachment 571629 [details] [diff] [review]
Patch 11 - New maintenance service installer. v6.

Now delete the maintenanceservice.exe from the FF directory after installing the maintenance service so that if you uninstall FF it won't keep that file around.
Comment 279 Brian R. Bondy [:bbondy] 2011-11-03 07:40:30 PDT
Created attachment 571638 [details] [diff] [review]
Patch 2 - Main service logic. v4'.

Minor logging fix.
Comment 280 Brian R. Bondy [:bbondy] 2011-11-03 10:20:02 PDT
Created attachment 571693 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v4.

Fixed bug where it would only pass the first callback arg when relaunching the callback app.  So for example "-P Nightly -no-remote" would be treated as only "-P" before hence opening callback app, but with the wrong profile.
Comment 281 Brian R. Bondy [:bbondy] 2011-11-03 10:22:49 PDT
Created attachment 571695 [details] [diff] [review]
Patch 18 - Write out update.status error code in maintenance service on failure. v2''.

Rebased due to changes in patch 16.  Same as before.
Comment 282 Ted Mielczarek [:ted.mielczarek] 2011-11-03 10:54:24 PDT
Comment on attachment 569786 [details] [diff] [review]
(PUSHED) Patch 8 - NSPR wide string logging support on Windows. v2.

Review of attachment 569786 [details] [diff] [review]:
-----------------------------------------------------------------

This looks pretty reasonable.

::: nsprpub/pr/src/io/prprf.c
@@ +86,5 @@
>  	double d;
>  	const char *s;
>  	int *ip;
> +#ifdef WIN32
> +	const WCHAR *ws;

Is there a reason to prefer WCHAR over wchar_t here?
Comment 283 Brian R. Bondy [:bbondy] 2011-11-03 11:45:34 PDT
Created attachment 571723 [details] [diff] [review]
Patch 8 - NSPR wide string logging v 2.2 (same as v2 but based on wchar_t instead of WCHAR)

WCHAR is windows specific whereas wchar_t is cross platform.  
The code change is only specific to Windows, so it's a toss up which one is better.

WCHAR and wchar_t should always be the same. 
From WinNT.h:

#ifndef _MAC
typedef wchar_t WCHAR;    // wc,   16-bit UNICODE character
#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR;    // wc,   16-bit UNICODE character
#endif

I think WCHAR was around longer than wchar_t was around, but I could be wrong on that (I think really really old VC++ versions only supported WCHAR).

Here are the usages of WCHAR and wchar_t currently in use in the same module (not including my code):
Find all "WCHAR", Subfolders, Find Results 1, "C:\projects\mozilla\mozilla-central\nsprpub\pr", "*.c;*.cpp;*.cxx;*.cc;*.tli;*.tlh;*.h;*.hpp;*.hxx;*.hh;*.inl;*.rc;*.resx;*.idl;*.asm;*.inc;*.idl;"
  C:\projects\mozilla\mozilla-central\nsprpub\pr\include\prtypes.h(480):typedef wchar_t PRUnichar;
  C:\projects\mozilla\mozilla-central\nsprpub\pr\src\io\prlog.c(108):    WCHAR *wMsg = (WCHAR *)PR_Malloc(len * sizeof(WCHAR));
  C:\projects\mozilla\mozilla-central\nsprpub\pr\src\md\windows\w95io.c(49):#include <wchar.h>
  C:\projects\mozilla\mozilla-central\nsprpub\pr\tests\testfile.c(120):WCHAR wPathname[256];
  C:\projects\mozilla\mozilla-central\nsprpub\pr\tests\testfile.c(956):        WCHAR tdir[TMPDIR_LEN];
  C:\projects\mozilla\mozilla-central\nsprpub\pr\tests\thrpool_server.c(97):    wchar_t wpath[MAX_PATH];
  C:\projects\mozilla\mozilla-central\nsprpub\pr\tests\tmocon.c(79):    wchar_t wpath[MAX_PATH];
  Matching lines: 11    Matching files: 7    Total files searched: 431


I provided a second variant of Patch 8 based on wchar_t.  
I'll let you make the final call which one to use, they both work for me.
Comment 284 Ted Mielczarek [:ted.mielczarek] 2011-11-03 11:56:01 PDT
Okay, that seems fine. I landed the original patch:
Checking in pr/src/io/prprf.c;
/cvsroot/mozilla/nsprpub/pr/src/io/prprf.c,v  <--  prprf.c
new revision: 3.21; previous revision: 3.20
done
Comment 285 Ted Mielczarek [:ted.mielczarek] 2011-11-03 12:17:20 PDT
And I pushed an NSPR update (including this patch) to inbound:
https://hg.mozilla.org/integration/mozilla-inbound/rev/765f5b79fedf
Comment 286 Wan-Teh Chang 2011-11-03 12:52:51 PDT
Comment on attachment 571723 [details] [diff] [review]
Patch 8 - NSPR wide string logging v 2.2 (same as v2 but based on wchar_t instead of WCHAR)

bbondy: thank you for the patch.  In general NSPR features
are implemented for all platforms.  Why is the %S format
specifier only implemented for Windows?  Can we use the
wcrtomb or wcsrtomb function on Unix?

Is %ls a more standard format than %S for wchar_t strings?
Comment 287 Brian R. Bondy [:bbondy] 2011-11-03 13:51:13 PDT
wtc: I tested on g++4.5 and also vc++ and they both accept the %S formatting specifier and also %ls formatting specifier.

I'll post a linked ticket shortly to do a follow up patch to patch 8 which will add cross platform support and also accept %ls in addition to %S.  I'll CC you on that ticket.
Comment 288 Emanuel Hoogeveen [:ehoogeveen] 2011-11-03 16:16:17 PDT
For the record, MinGW-w32/w64 4.7.0 reject %S when compiling with -pedantic. %ls appears to be ISO-C++.
Comment 289 Brian R. Bondy [:bbondy] 2011-11-03 19:24:29 PDT
Just an FYI in case anyone is interested in the history of %S vs. %ls format specifiers.

The C++ 2003 standard doesn't make any mention of it that I can find.
The C99 standard does though:

"The original intent was to add the new conversion specifiers %S and %C to the existing formatted input and output functions to handle a wide character string and a wide character respectively.  After long discussions about the actual implementation and future library directions, these specifiers were withdrawn. They were replaced with the qualified conversion specifiers, %ls and %lc, with the addition of %l[...] in the formatted input functions"

Likely some compilers implemented %S before it was standardized and never removed support.
Comment 290 Ed Morley [:emorley] 2011-11-04 03:14:53 PDT
(In reply to Ted Mielczarek [:ted, :luser] from comment #284)
> Okay, that seems fine. I landed the original patch:
> Checking in pr/src/io/prprf.c;
> /cvsroot/mozilla/nsprpub/pr/src/io/prprf.c,v  <--  prprf.c
> new revision: 3.21; previous revision: 3.20
> done

Merged to m-c:
https://hg.mozilla.org/mozilla-central/rev/765f5b79fedf
Comment 291 Brian R. Bondy [:bbondy] 2011-11-04 12:16:10 PDT
Created attachment 572040 [details] [diff] [review]
Patch 11 - New maintenance service installer. v7.

No longer do rename maintenanceservice.exe -> maintenanceservice_tmp.exe, copy, then delete source _tmp file inside the FF directory because it will cause problems when doing incremental partial updates.
Comment 292 Wan-Teh Chang 2011-11-04 17:42:07 PDT
bbondy: please do not start using the %S formatting specifier.  Please wait
until bug 699567 is fixed, and then use the %ls formatting specifier instead.

I will unsubscribe from this bug but remain on bug 699567 for the NSPR work.
Thanks!
Comment 293 Brian R. Bondy [:bbondy] 2011-11-04 18:59:20 PDT
Created attachment 572150 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v5.

When updating with limited user accounts it would attempt to show a GUI.  On Vista+ this would show a "service is trying to show a GUI click here to see it dialog" popup.

This is fixed now, we no longer try to show a GUI if trying to start the update in session 0 on Vista or later.
Comment 294 Brian R. Bondy [:bbondy] 2011-11-04 19:06:35 PDT
Re wtc:
> bbondy: please do not start using the %S formatting specifier.  Please wait
> until bug 699567 is fixed, and then use the %ls formatting specifier instead.

Those references will be changed at the same time as bug 699567 is fixed.
Comment 295 Benjamin Smedberg [:bsmedberg] 2011-11-07 08:57:33 PST
Comment on attachment 571099 [details] [diff] [review]
Patch 7 - RAII base helpers. v2.

Please remove the trailing underscores from the #defines e.g. just "nsAutoRegKey_h" not "nsAutoRegKey_h__".

Is there a particular reason to have these all in separate files instead of just nsWindowsHelpers.h or something like that? r=me either way.
Comment 296 Brian R. Bondy [:bbondy] 2011-11-07 13:19:35 PST
Created attachment 572590 [details] [diff] [review]
(PUSHED) Patch 23 - Strings for localization

Extracted strings from other patches.
Comment 297 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-07 13:23:41 PST
Comment on attachment 572590 [details] [diff] [review]
(PUSHED) Patch 23 - Strings for localization

Looks good... please get this landed before the branch merge.
Comment 298 Brian R. Bondy [:bbondy] 2011-11-07 13:32:28 PST
Pushed Patch 23 to mozilla-inbound:
Brian R. Bondy - Bug 481815 - Strings for localization relating to maintenance service. r=rs
http://hg.mozilla.org/integration/mozilla-inbound/rev/375aaf23cc2f
Comment 299 Brian R. Bondy [:bbondy] 2011-11-07 15:35:50 PST
Created attachment 572647 [details] [diff] [review]
Patch 7 - RAII base helpers. v3.

- Fixed header guard style
- Merged the 3 RAII helpers into nsWindowsHelpers.h
- Extracted the build config Makefile.in from patch 14 into this patch (Patch 7).
Comment 300 Brian R. Bondy [:bbondy] 2011-11-07 15:41:24 PST
Created attachment 572652 [details] [diff] [review]
Patch 2 - Main service logic. v4''.

Rebased. Used the renamed nsWindowsHelpers.h instead of old include files after review.
Comment 301 Brian R. Bondy [:bbondy] 2011-11-07 15:45:07 PST
Created attachment 572654 [details] [diff] [review]
Patch 3 - Code for installing the service. v3''.

Rebased. Used the renamed nsWindowsHelpers.h instead of old include files after review.
Comment 302 Brian R. Bondy [:bbondy] 2011-11-07 15:46:44 PST
Created attachment 572655 [details] [diff] [review]
Patch 4 - Certificate check code inside service. v3''.

Rebased. Used the renamed nsWindowsHelpers.h instead of old include files after review.
Comment 303 Brian R. Bondy [:bbondy] 2011-11-07 15:48:16 PST
Created attachment 572657 [details] [diff] [review]
Patch 5 - XUL Runtime Environment code. v4''.

Rebased. Used the renamed nsWindowsHelpers.h instead of old include files after review.
Comment 304 Brian R. Bondy [:bbondy] 2011-11-07 15:50:13 PST
Created attachment 572661 [details] [diff] [review]
Patch 14 - Build process. v3'.

Rebased.  Moved the RAII helper out of this patch and into patch 7.
Comment 305 Ed Morley [:emorley] 2011-11-08 01:42:36 PST
Comment on attachment 572590 [details] [diff] [review]
(PUSHED) Patch 23 - Strings for localization

https://hg.mozilla.org/mozilla-central/rev/375aaf23cc2f
Comment 306 Axel Hecht [:Pike] 2011-11-08 05:16:30 PST
(In reply to Robert Strong [:rstrong] (do not email) from comment #297)
> Comment on attachment 572590 [details] [diff] [review] [diff] [details] [review]
> (PUSHED) Patch 23 - Strings for localization
> 
> Looks good... please get this landed before the branch merge.

Prelanding strings in general isn't a good thing to do, and in this particular case, it only helps so far.

Switching on the display of these prefs changes the dimensions in the (l10n) sized pref window, and as such, effectively breaks string freeze, even if it only shows existing strings.
Comment 307 Brian R. Bondy [:bbondy] 2011-11-08 06:27:33 PST
> Prelanding...

Understood.  Just wanted clarify though that there is a good chance that this will be pushed to v10 so it is more landing on time, and postlanding the other patches.
Comment 308 Brian R. Bondy [:bbondy] 2011-11-08 06:31:01 PST
Created attachment 572799 [details] [diff] [review]
Patch 9 - Firefox installer/uninstaller changes. v5'.

Rebased to mozilla-central tip
Comment 309 Brian R. Bondy [:bbondy] 2011-11-08 06:33:52 PST
Created attachment 572800 [details] [diff] [review]
Patch 14 - Build process. v3''.

Rebased to mozilla-central tip
Comment 310 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-08 09:05:46 PST
(In reply to Axel Hecht [:Pike] from comment #306)
> (In reply to Robert Strong [:rstrong] (do not email) from comment #297)
> > Comment on attachment 572590 [details] [diff] [review] [diff] [details] [review] [diff] [details] [review]
> > (PUSHED) Patch 23 - Strings for localization
> > 
> > Looks good... please get this landed before the branch merge.
> 
> Prelanding strings in general isn't a good thing to do, and in this
> particular case, it only helps so far.
> 
> Switching on the display of these prefs changes the dimensions in the (l10n)
> sized pref window, and as such, effectively breaks string freeze, even if it
> only shows existing strings.
That ui is constrained by width and has a min-height
http://mxr.mozilla.org/mozilla-central/source/browser/locales/en-US/chrome/browser/preferences/preferences.dtd#6

The string itself will wrap and if the ui height is not enough to display the string it should increase in height due to some work I did on this ui several years ago.

Whatever the case, we'll make sure the ui isn't broken before turning this on.
Comment 311 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-09 14:03:06 PST
Comment on attachment 572652 [details] [diff] [review]
Patch 2 - Main service logic. v4''.

>diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp b/toolkit/components/maintenanceservice/workmonitor.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
>+static const int FILE_SHARE_ALL = FILE_SHARE_READ | 
>+                                  FILE_SHARE_WRITE | 
>+                                  FILE_SHARE_DELETE;
>+
>+// Wait 15 minutes for an update operation to run at most.
>+static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
The end result I think we want here is to have the update applied to a copy of the app similar to what Ehsan is doing. Is it feasible to add that at this time and if not when do you see that being added? Is this why you wanted to use the user's account when possible so the ui could be shown?

>+
>+/**
>+ * Runs an update process in the specified sessionID as an elevated process.
>+ *  
>+ * @sessionID     The session ID to run the process as.
http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html
The format should be
@param text  the text of the tool tip

here and elsewhere

check that alignment is correct elsewhere.

I don't think this is an alternative way of formatting but if it is please point me to it.

>+ * @appToStart    The path to the update process to start
>+ * @workingDir    The working directory to execute the update process in
>+ * @cmdLine       The command line parameters to pass to the update process.
>+ * @return        TRUE if the update process was run had a return code of 0.
>+ */
>+BOOL
>+StartUpdateProcess(DWORD sessionID, 
>+                   LPCWSTR appToStart, 
>+                   LPCWSTR workingDir, 
>+                   LPWSTR cmdLine)
>+{
>+  BOOL processStarted = FALSE;
>+  DWORD myProcessID = GetCurrentProcessId();
>+  DWORD mySessionID = 0;
>+  ProcessIdToSessionId(myProcessID, &mySessionID);
>+
>+  STARTUPINFO si = {0};
>+  si.cb = sizeof(STARTUPINFO);
>+  si.lpDesktop = L"winsta0\\Default";
>+  PROCESS_INFORMATION pi = {0};
>+  nsAutoHandle elevatedToken, unelevatedToken;
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Starting process in an elevated session.  Service "
>+     "session ID: %d; Requested session ID: %d", 
>+     mySessionID, sessionID));
>+
>+  // We must be running this from an older OS than Vista or else
>+  // we are running this code as a user process instead of via the service.
>+  // In that case just execute the process in the normal way.
>+  if (mySessionID == sessionID) {
>+    processStarted = ::CreateProcessW(appToStart, cmdLine, 
>+                                      NULL, NULL, FALSE, 
>+                                      CREATE_DEFAULT_ERROR_MODE | 
>+                                      CREATE_UNICODE_ENVIRONMENT, 
>+                                      NULL, workingDir, &si, &pi);
>+    if (!processStarted) {
>+      DWORD lastError = ::GetLastError();
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create process as current user, last error: "
>+         "%d; appToStart: %S; cmdLine: %S", 
>+         lastError, appToStart, cmdLine));
>+    }
>+  } else {
>+    unelevatedToken = UACHelper::OpenUserToken(sessionID);
>+    if (!unelevatedToken) {
>+      // Couldn't obtain user token
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not obtain user token, will start as the SYSTEM account "
>+         "instead."));
>+      return StartUpdateProcess(mySessionID, appToStart, workingDir, 
>+                                cmdLine, sessionID);
>+    }
>+
>+    UACHelper::UserType userType;
>+    if (!UACHelper::GetElevationType(unelevatedToken, userType) ||
>+        userType == UACHelper::LimitedUser) {
>+      // Couldn't determine the user type, so just start the app as 
>+      // the SYSTEM account
// Couldn't determine the user type or limited user

>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not determine elevation type, will start as the SYSTEM "
>+         "account instead."));
>+      return StartUpdateProcess(mySessionID, appToStart, workingDir, 
>+                                cmdLine, sessionID);
>+    }
>+
>+    if (userType == UACHelper::AdministratorUnelevated) {
>+      UACHelper::UserType linkedTokenType;
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Windows Vista or later unelevated detected, attempting to find "
>+         "linked token."));
>+      elevatedToken = UACHelper::OpenLinkedToken(unelevatedToken);
>+      BOOL obtainedElevationType = 
>+        UACHelper::GetElevationType(elevatedToken, linkedTokenType);
>+      if (obtainedElevationType && elevatedToken && 
>+          UACHelper::AdministratorElevated == linkedTokenType) {
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Elevated token was found, using it."));
Why not just use the system account? I think it would be cleaner with less likelihood of there being weird bugs due to using the user's account in some cases and the system account in others. Also, the system account should always have the rights to apply the update whereas a user won't though I do get that these checks try to prevent that from happening.

The rest of the code in StartUpdateProcess looks good but I'm not diving into it deeply since it will likely change a lot if the above route is taken.

>+        // Proceed on...
>+      } else if (obtainedElevationType && elevatedToken &&
>+                 linkedTokenType != UACHelper::AdministratorElevated) {
>+
nit: remove extra line for consistency

>+        // We got a linked token but strangely enough it's not an elevated one?
>+        // Maybe Windows added a new token type or something or they are 
>+        // linking 2 non elevated tokens.
What cases do you think this will happen? For example, couldn't this happen with the Backup Operators group?

>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Obtained linked token but it is not elevated, will start as the "
>+           "SYSTEM account instead"));
>+        return StartUpdateProcess(mySessionID, appToStart, workingDir, 
>+                                  cmdLine, sessionID);
>+      } else if (!obtainedElevationType && elevatedToken) {
>+
nit: remove extra line for consistency

>+        // We could not get the elevation type but we did get a linked token.
>+        // This is almost for sure an administrative token so just write out a 
>+        // warning and continue going on like normal.
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Warning, could not verify elevation type, but did get a linked "
>+           "token so proceeding."));
>+        // Proceed on...
>+      } else {
>+
nit: remove extra line for consistency

>+        // Couldn't get an elevated token so just start the app as the
>+        // SYSTEM account
s/start the app/start the updater/

At first I thought this was the callback application.

>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("An elevated token was not found, will start as the SYSTEM account "
>+           "instead."));
>+        return StartUpdateProcess(mySessionID, appToStart, workingDir, 
>+                                  cmdLine, sessionID);
>+      }
>+    } 
>+    // else { 
>+    // they are AdministratorElevated or AdministratorUACIsOff, so just use 
>+    // the token we already have 
>+    // }
What's going on here?

>+
>+    // Create an environment block for the process we're about to start using
>+    // the user's token.
>+    if (!::CreateEnvironmentBlock(&environmentBlock, elevatedToken, TRUE)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create an environment block, setting it to NULL."));
>+      environmentBlock = NULL;
>+    }
>+
>+    // Make sure that our elevated token is a primary token. 
>+    // It should be, but just do a sanity check to make sure.
>+    // CreateProcessAsUser can only be used with tokens that are primary.
>+    DWORD len;
>+    TOKEN_TYPE tokenType;
>+    if (!::GetTokenInformation(elevatedToken, TokenType, &tokenType, 
>+                               sizeof(tokenType), &len) ||
>+        tokenType != TokenPrimary) {
>+      // This token should be a primary token
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Warning: we should have obtained a primary token, we won't be "
>+         "able to CreateProcessAsUser with the current token, go with the "
>+         "SYSTEM account."));
>+      return StartUpdateProcess(mySessionID, appToStart, workingDir, 
>+                                cmdLine, sessionID);
>+    }
>+ 
>+    // Launch the process in the specified working directory application and 
>+    // command line
>+    processStarted = ::CreateProcessAsUserW(elevatedToken, appToStart, 
>+                                            cmdLine, 
>+                                            NULL, NULL, FALSE,
>+                                            CREATE_DEFAULT_ERROR_MODE | 
>+                                            CREATE_UNICODE_ENVIRONMENT, 
>+                                            environmentBlock, workingDir, 
>+                                            &si, &pi);
>+    if (!processStarted) {
>+      DWORD lastError = ::GetLastError();
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not create update process with user token, last error: "
>+         "%d; appToStart: %S; cmdLine: %S", 
>+         lastError, appToStart, cmdLine));
>+    }
>+  }
>+

I think the following could be cleaned up a bit as follows

>+  BOOL updateWasSuccessful = FALSE;
remove

>+  if (processStarted) {
if (!processStarted)
  return FALSE;

>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Process was started... waiting on result.")); 
>+
>+    // Wait for the updater process to finish
>+    ::WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
>+
>+    // Check the return code of updater.exe to make sure we get 0
>+    DWORD returnCode;
>+    if (!::GetExitCodeProcess(pi.hProcess, &returnCode)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Process finished but could not obtain return code.")); 
>+
>+    // Wait for the updater process to finish
>+    ::WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
>+
>+    // Check the return code of updater.exe to make sure we get 0
>+    DWORD returnCode;
>+    if (!::GetExitCodeProcess(pi.hProcess, &returnCode)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Process finished but could not obtain return code.")); 
>+    } else {
>+
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Process finished with return code %d.", returnCode)); 
>+      // updater returns 0 if successful.
>+      updateWasSuccessful = (returnCode == 0);
>+    }
>+  }
>+
>+  return updateWasSuccessful;
  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
         ("Process was started... waiting on result.")); 

  // Wait for the updater process to finish
  ::WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);

  // Check the return code of updater.exe to make sure we get 0
  DWORD returnCode;
  if (!::GetExitCodeProcess(pi.hProcess, &returnCode)) {
    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
           ("Process finished but could not obtain return code.")); 
    return FALSE;
  }

  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
         ("Process finished with return code %d.", returnCode)); 
  // updater returns 0 if successful.
  return updateWasSuccessful = (returnCode == 0);
>+}
>+
>+/**
>+ * Processes a work item (file by the name of .mz) and executes
>+ * the command within.
>+ *  
>+ * @monitoringBasePath  The base path that is being monitored.
>+ * @notifyInfo          the notifyInfo struct for the changes.
>+ * @return        Returns TRUE if we want the service to stop.
>+ */
>+BOOL
>+ProcessWorkItem(LPCWSTR monitoringBasePath, 
>+                FILE_NOTIFY_INFORMATION &notifyInfo)
>+{
>+  int filenameLength = notifyInfo.FileNameLength / 
>+                       sizeof(notifyInfo.FileName[0]); 
>+  notifyInfo.FileName[filenameLength] = L'\0';
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Processing new command meta file: %S", notifyInfo.FileName));
>+
>+  // When the file is ready for processing it will be renamed 
>+  // to have a .mz extension
>+  BOOL haveWorkItem = notifyInfo.Action == FILE_ACTION_RENAMED_NEW_NAME && 
>+                      notifyInfo.FileNameLength > 3 && 
>+                      notifyInfo.FileName[filenameLength - 3] == L'.' &&
>+                      notifyInfo.FileName[filenameLength - 2] == L'm' &&
>+                      notifyInfo.FileName[filenameLength - 1] == L'z';
>+  if (!haveWorkItem) {
>+    // We don't have a work item, keep looking
>+    return FALSE;
>+  }
>+
>+  WCHAR fullMetaUpdateFilePath[MAX_PATH + 1];
>+  wcscpy(fullMetaUpdateFilePath, monitoringBasePath);
>+
>+  // We only support file paths in monitoring directories that are MAX_PATH
>+  // chars or less.
>+  if (!PathAppendSafe(fullMetaUpdateFilePath, notifyInfo.FileName)) {
>+    // Cannot process update, skipfileSize it.
>+    return TRUE;
>+  }
>+
>+  nsAutoHandle metaUpdateFile = ::CreateFile(fullMetaUpdateFilePath, 
>+                                             GENERIC_READ, 
>+                                             FILE_SHARE_ALL, NULL, 
>+                                             OPEN_EXISTING,
>+                                             0, NULL);
Why is that opened with FILE_SHARE_ALL? Is it in case the service is reading it while it is being written to? Please add a comment as to why.

>+  if (INVALID_HANDLE_VALUE == metaUpdateFile) {
>+
nit: remove extra line for consistency

>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not open command meta file: %S", notifyInfo.FileName));
>+    return TRUE;
>+  }
>+
>+  DWORD fileSize = GetFileSize(metaUpdateFile, NULL);
>+  DWORD sessionID = 0;
>+  // The file should be in wide characters so if it's of odd size it's
>+  // an invalid file.
>+  const int kSanityCheckFileSize = 1024 * 64;
>+  if (fileSize == INVALID_FILE_SIZE || 
>+      (fileSize %2) != 0 ||
>+      fileSize > kSanityCheckFileSize ||
>+      fileSize < sizeof(DWORD)) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain file size or an improper file size was encountered "
>+       "for command meta file: %S", 
>+       notifyInfo.FileName));
>+    return TRUE;
>+  }
>+
>+  // The first 4 bytes are for the process ID
>+  DWORD sessionIDCount;
>+  BOOL result = ::ReadFile(metaUpdateFile, &sessionID, 
>+                            sizeof(DWORD), &sessionIDCount, NULL);
>+  fileSize -= sizeof(DWORD);
>+
>+  // The next MAX_PATH wchar's are for the app to start
>+  WCHAR appToStart[MAX_PATH + 1];
>+  ZeroMemory(appToStart, sizeof(appToStart));
>+  DWORD appToStartCount;
>+  result |= ::ReadFile(metaUpdateFile, appToStart, 
>+                       MAX_PATH * sizeof(WCHAR), &appToStartCount, NULL);
>+  fileSize -= appToStartCount;
>+
>+  // The next MAX_PATH wchar's are for the app to start
>+  WCHAR workingDirectory[MAX_PATH + 1];
>+  ZeroMemory(workingDirectory, sizeof(workingDirectory));
>+  DWORD workingDirectoryCount;
>+  result |= ::ReadFile(metaUpdateFile, workingDirectory, 
>+                       MAX_PATH * sizeof(WCHAR), &workingDirectoryCount, NULL);
>+  fileSize -= workingDirectoryCount;
>+
>+  // + 2 for wide char termination
>+  nsAutoArrayPtr<char> cmdlineBuffer = new char[fileSize + 2];
>+  DWORD cmdLineBufferRead;
>+  result |= ::ReadFile(metaUpdateFile, cmdlineBuffer, 
>+                       fileSize, &cmdLineBufferRead, NULL);
>+  fileSize -= cmdLineBufferRead;
>+
>+  if (!result ||
>+      sessionIDCount != sizeof(DWORD) || 
>+      appToStartCount != MAX_PATH * sizeof(WCHAR) ||
>+      workingDirectoryCount != MAX_PATH * sizeof(WCHAR) ||
>+      fileSize != 0) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not read command data for command meta file: %S", 
>+       notifyInfo.FileName));
>+    return TRUE;
>+  }
>+  cmdlineBuffer[cmdLineBufferRead] = L'\0';
>+  cmdlineBuffer[cmdLineBufferRead + 1] = '\0';
>+  WCHAR *cmdlineBufferWide = reinterpret_cast<WCHAR*>(cmdlineBuffer.get());
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("An update command was detected and is being processed for command meta "
>+     "file: %S", 
>+     notifyInfo.FileName));
>+
>+  // Validate the certificate of the app the user wants us to start.
>+  // Also check to make sure the certificate is trusted.
>+#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
>+  if (DoesBinaryMatchAllowedCertificates(appToStart)) {
>+#endif
>+    if (!StartUpdateProcess(sessionID, appToStart, workingDirectory, 
>+                            cmdlineBufferWide)) {
>+      // TODO: Need to tell the app that we couldn't run the update so 
>+      // the app will do the update the old way. rs do you think via 
>+      // update status file? I don't want it to keep using the service 
>+      // in case it keeps getting the same error on each launch.
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Error running process in session %d.  Last error: %d", 
>+         sessionID, GetLastError()));
>+    } else {
>+      // The update was executed and run successfully
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("updater.exe was launched and run successfully"));
>+    }
>+#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
>+  } else {
>+    // TODO: Need to tell the app that we couldn't run the update
>+    // because the cert is not valid.  The app will do the update 
>+    // itself the old way.  rs do you think via update status file?
Yes

>+    // I don't want it to keep using the service in case it keeps getting 
>+    // the same error on each launch.
Might be a good thing to just have a counter in a pref that gets reset after success and incremented on failure. When the failure count reach X number of failures disable using the service using the pref you added for this purpose.

>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not start process due to certificate check.  Last error: %d", 
>+       GetLastError()));
>+  }
>+#endif
>+
>+  // We processed a work item, whether or not it was successful.
>+  return TRUE;
>+}
>+
>+/**
>+ * Starts monitoring the update directory for work items.
>+ */
>+BOOL
>+StartDirectoryChangeMonitor() 
>+{
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Starting directory change monitor..."));
>+
>+  // Init the update directory path and ensure it exists.
>+  // Example: C:\programData\Mozilla\Firefox\updates\[channel]
>+  // The channel is not included here as we want to monitor the base directory
>+  WCHAR updateData[MAX_PATH + 1];
>+  if (!GetUpdateDirectoryPath(updateData)) {
>+
nit: remove extra line for consistency

>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain update directory path"));
>+
>+    return FALSE;
>+  }
>+
>+  // Obtain a directory handle, FILE_FLAG_BACKUP_SEMANTICS is needed for this.
>+  nsAutoHandle directoryHandle = ::CreateFile(updateData, 
>+                                              FILE_LIST_DIRECTORY, 
>+                                              FILE_SHARE_ALL, 
>+                                              NULL, 
>+                                              OPEN_EXISTING,
>+                                              FILE_FLAG_BACKUP_SEMANTICS, 
>+                                              NULL);
>+  if (INVALID_HANDLE_VALUE == directoryHandle) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain directory handle to monitor"));
>+
>+    return FALSE;
>+  }
>+
>+  // Allocate a buffer that is big enough to hold 128 changes
>+  // Note that FILE_NOTIFY_INFORMATION is a variable length struct
>+  // so that's why we don't create a simple array directly.
>+  char fileNotifyInfoBuffer[(sizeof(FILE_NOTIFY_INFORMATION) + 
>+                            MAX_PATH) * 128];
>+  ZeroMemory(&fileNotifyInfoBuffer, sizeof(fileNotifyInfoBuffer));
>+  
>+  DWORD bytesReturned;
>+  // Listen for directory changes to the update directory
>+  while (ReadDirectoryChangesW(directoryHandle, 
>+                               fileNotifyInfoBuffer, 
>+                               sizeof(fileNotifyInfoBuffer), 
>+                               TRUE, FILE_NOTIFY_CHANGE_FILE_NAME, 
>+                               &bytesReturned, NULL, NULL)) {
>+    char *fileNotifyInfoBufferLocation = fileNotifyInfoBuffer;
>+
>+    // Make sure we have at least one entry to process
>+    while (bytesReturned/sizeof(FILE_NOTIFY_INFORMATION) > 0) {
>+
nit: remove extra line for consistency

>+      // Point to the current directory info which is changed
>+      FILE_NOTIFY_INFORMATION &notifyInfo = 
>+        *((FILE_NOTIFY_INFORMATION*)fileNotifyInfoBufferLocation);
>+      fileNotifyInfoBufferLocation += notifyInfo .NextEntryOffset;
>+      bytesReturned -= notifyInfo .NextEntryOffset;
>+      if (!wcscmp(notifyInfo.FileName, L"stop") && gServiceStopping) {
>+
nit: remove extra line for consistency

>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("A stop command was issued."));
>+
>+        return TRUE;
>+      }
>+
Comment 312 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-09 14:06:13 PST
(In reply to Robert Strong [:rstrong] (do not email) from comment #311)
>...
> >+  return updateWasSuccessful;
>   PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>          ("Process was started... waiting on result.")); 
> 
>   // Wait for the updater process to finish
>   ::WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
> 
>   // Check the return code of updater.exe to make sure we get 0
>   DWORD returnCode;
>   if (!::GetExitCodeProcess(pi.hProcess, &returnCode)) {
>     PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>            ("Process finished but could not obtain return code.")); 
>     return FALSE;
>   }
> 
>   PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>          ("Process finished with return code %d.", returnCode)); 
>   // updater returns 0 if successful.
>   return updateWasSuccessful = (returnCode == 0);
> >+}
s/return updateWasSuccessful = (returnCode == 0);/return (returnCode == 0);/
Comment 313 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-09 14:52:38 PST
Comment on attachment 572654 [details] [diff] [review]
Patch 3 - Code for installing the service. v3''.

>diff --git a/toolkit/components/maintenanceservice/serviceinstall.cpp b/toolkit/components/maintenanceservice/serviceinstall.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
>@@ -0,0 +1,441 @@
>...
>+#include "serviceinstall.h"
>+#include "servicebase.h"
>+#include "shellapi.h"
>+
>+#pragma comment(lib, "version.lib")
>+#define DEFERRED_DELETE_TIMEOUT_SECONDS 10
When ever there is a number without a reason for the value like above I am going to ask why you chose this number unless there is a comment stating why the number was chosen ;)

>+
>+/**
>+ * Obtains the version number from the specified PE file's version information
>+ * Version Format: major.minor.aux.maintenance (Example 10.0.0.300)
>+ *  
>+ * @path        The path of the file to check the version on
>+ * @major       The major part of the version number
>+ * @minor       The minor part of the version number
>+ * @maintenance The maintenance part of the version number
>+ * @return      TRUE if successful
Please add aux

Also note previous review comment regarding formatting

Our naming for each field in a version doesn't fall very well into everyone else's. Instead of using major, minor, etc. since these are really just determined by the app's naming scheme how about using the Windows stuct names for each?
fileVerMS
fileVerLS
prodVerMS
prodVerLS

or something similar to avoid confusion. For example, Firefox uses major, ext_compat_change_for_major_ver, minor, typically_not_used_but_can_be ;)

>+ */
>+static BOOL
>+GetVersionNumberFromPath(LPWSTR path, DWORD &major, DWORD &minor, 
>+                         DWORD &aux, DWORD &maintenance) 
>+{
>+  DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0);
>+  nsAutoArrayPtr<char> fileVersionInfo = new char[fileVersionInfoSize];
>+  if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize,
>+                           fileVersionInfo.get())) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not obtain file info of old service.  (%d)", GetLastError()));
>+      return FALSE;
If we can't get the file info for the old service then I would be concerned that someone replaced our service. Do you think it might be better to just replace it for that case?
Comment 314 Brian R. Bondy [:bbondy] 2011-11-09 20:07:30 PST
> If we can't get the file info for the old service then I would be
> concerned that someone replaced our service.

That sounds fair, I'll make that change.  I'm not sure in which cases that function could fail but likely only in the case you stated.
Comment 315 Brian R. Bondy [:bbondy] 2011-11-09 20:22:43 PST
Created attachment 573420 [details] [diff] [review]
Patch 3.5 temp - Review comments

This patch will be merged into patch 3 once they reach r+.  I'm keeping the reviews on patch 3 separate for now to make reviewing less painful.
Comment 316 Brian R. Bondy [:bbondy] 2011-11-10 07:59:09 PST
> The end result I think we want here is to have the update applied to a copy 
> of the app similar to what Ehsan is doing. Is it feasible to add that at 
> this time and if not when do you see that being added? Is this why you 
> wanted to use the user's account when possible so the ui could be shown?

Right, this task is under the assumption that it will land on Nightly before Ehsan's work.  Once the patches in this ticket get all r+ I will push to Nightly and also push all of the work to the oak branch.  Once it is on the Oak branch Ehsan will also merge his work to the point where it compiles but maybe doesn't have working updates.  From there any additional integration work needed for the service will be done by me.

This process is outlined in these bugs:
Bug 701372 - Push silent update w/ service changes to oak branch (me)
Bug 701375 - Merge background updates to the oak branch (ehsan)
Bug 701379 - Fix silent update service to work with background updates (me)
Comment 317 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 07:59:42 PST
Comment on attachment 572654 [details] [diff] [review]
Patch 3 - Code for installing the service. v3''.

>diff --git a/toolkit/components/maintenanceservice/serviceinstall.cpp b/toolkit/components/maintenanceservice/serviceinstall.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
>@@ -0,0 +1,441 @@
>...
>+ */
>+static BOOL
>+GetVersionNumberFromPath(LPWSTR path, DWORD &major, DWORD &minor, 
>+                         DWORD &aux, DWORD &maintenance) 
>+{
>+  DWORD fileVersionInfoSize = GetFileVersionInfoSizeW(path, 0);
>+  nsAutoArrayPtr<char> fileVersionInfo = new char[fileVersionInfoSize];
>+  if (!GetFileVersionInfoW(path, 0, fileVersionInfoSize,
>+                           fileVersionInfo.get())) {
consistency nit
::GetFileVersionInfoW

here and elsewhere

Also, indentation looks off here and immediately below

>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not obtain file info of old service.  (%d)", GetLastError()));
>+      return FALSE;
>+  }
>+
>+  VS_FIXEDFILEINFO *fixedFileInfo = 
>+    reinterpret_cast<VS_FIXEDFILEINFO *>(fileVersionInfo.get());
>+  UINT size;
>+  if (!VerQueryValueW(fileVersionInfo.get(), L"\\", 
>+    reinterpret_cast<LPVOID*>(&fixedFileInfo), &size)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not query file version info of old service.  (%d)", 
>+         GetLastError()));
>+      return FALSE;
>+  }  
>+
>+  major = HIWORD(fixedFileInfo->dwFileVersionMS);
>+  minor = LOWORD(fixedFileInfo->dwFileVersionMS);
>+  aux = HIWORD(fixedFileInfo->dwFileVersionLS);
>+  maintenance = LOWORD(fixedFileInfo->dwFileVersionLS);
>+  return TRUE;
>+}
>+
>+/**
>+ * Deletes a file after DEFERRED_DELETE_TIMEOUT_SECONDS seconds
>+ *  
>+ * @pathToDelete  The path to delete
>+ * @return        ERROR_SUCCESS if the operation was scheduled
>+ */
>+DWORD
>+DeferredDeletePath(LPCWSTR pathToDelete) 
>+{
>+  WCHAR deferredSlefDeleteCmdLine[MAX_PATH + 32];
>+  wsprintfW(deferredSlefDeleteCmdLine, 
>+            L"/C SLEEP %i && DEL \"%s\"", 
>+            DEFERRED_DELETE_TIMEOUT_SECONDS, 
>+            pathToDelete);
>+
>+  SetLastError(ERROR_SUCCESS);
>+  ShellExecuteW(NULL, L"open", L"cmd", deferredSlefDeleteCmdLine, 
>+                NULL, SW_HIDE);
ewww :(

NSIS has code that handles this for the uninstaller that I suspect is a better solution.

Also, I would suspect that you could do a rename of the file in use to get it out of the way.

>+  return GetLastError();
>+}
>+
>+/**
>+ * Installs or upgrades the service for the process we are running as.
>+ * If an existing service is already installed, we replace it with ourselves
>+ * if we are newer than what is already installed.
>+ *  
>+ * @upgradeOnly   If TRUE works the same but will not install
>+ * @return        TRUE if the service was installed/upgraded
>+ */
>+BOOL
>+SvcInstall(BOOL upgradeOnly)
>+{
For the upgrade case will this happen prior to relaunching the callback application? If so, I don't think we want that. ;)
Comment 318 Brian R. Bondy [:bbondy] 2011-11-10 08:12:58 PST
> For the upgrade case will this happen prior to relaunching the 
> callback application? If so, I don't think we want that. ;)

Nope, this is already worked out.  The service will:
i) launch updater w/o callback parameters in session 0. 
ii) launch the callback in the user session w/ medium integrity (unelevated).
iii) launch a self upgrade, iv) close itself down in session 0.

See "Patch 21 - New service self update code" which fixed your concern.
Comment 319 Brian R. Bondy [:bbondy] 2011-11-10 08:54:06 PST
Created attachment 573531 [details] [diff] [review]
Patch 24 - Fixed javadoc throughout

Should be a quick review, fixed formatting of all javadoc in this patch instead of having to re-upload all of the patches.
Comment 320 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 09:19:24 PST
Comment on attachment 572654 [details] [diff] [review]
Patch 3 - Code for installing the service. v3''.

>diff --git a/toolkit/components/maintenanceservice/serviceinstall.cpp b/toolkit/components/maintenanceservice/serviceinstall.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
>...
>+/**
>+ * Installs or upgrades the service for the process we are running as.
"for the process we are running as" is a tad confusing and could be stated clearer.

>+ * If an existing service is already installed, we replace it with ourselves
>+ * if we are newer than what is already installed.
>+ *  
>+ * @upgradeOnly   If TRUE works the same but will not install
If TRUE will only upgrade an existing install

>+ * @return        TRUE if the service was installed/upgraded
>+ */
>+BOOL
>+SvcInstall(BOOL upgradeOnly)
>+{
>+  // Get a handle to the local computer SCM database with full access rights.
>+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
>+                                                 SC_MANAGER_ALL_ACCESS));
>+  if (!schSCManager) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not open service manager.  (%d)", GetLastError()));
>+    return FALSE;
>+  }
>+
>+  WCHAR modulePath[MAX_PATH + 1];
>+  if(!GetModuleFileNameW(NULL, modulePath, 
>+                         sizeof(modulePath)/sizeof(modulePath[0]))) {
nit: sizeof(modulePath) / sizeof(modulePath[0])

>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not obtain module filename when attempting to "
>+       "install service. (%d)",
>+       GetLastError()));
>+    return FALSE;
>+  }
>+
>+  // Check if we already have an open service
>+  nsAutoServiceHandle schService(OpenServiceW(schSCManager, 
>+                                              SVC_NAME, 
>+                                              SERVICE_ALL_ACCESS));
>+  DWORD lastError = GetLastError();
>+  if (!schService && ERROR_SERVICE_DOES_NOT_EXIST != lastError) {
>+    // The service exists but we couldn't open it
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not open service.  (%d)", GetLastError()));
>+    return FALSE;
>+  } else if(schService) {
nit: break out the else if since the previous block returns early

>+    // The service exists and we opened it
>+    DWORD bytesNeeded;
>+    if (QueryServiceConfigW(schService, NULL, 0, &bytesNeeded) || 
>+        GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could not determine buffer size for query service config.  (%d)", 
>+         GetLastError()));
How is this error condition handled?

>+    }
>+
>+    // Get the service config information, in particular we want the binary 
>+    // path of the service.
>+    nsAutoArrayPtr<char> serviceConfigBuffer = new char[bytesNeeded];
>+    if (!QueryServiceConfigW(schService, 
>+        reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), 
>+        bytesNeeded, &bytesNeeded)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could open service but could not query service config.  (%d)", 
>+         GetLastError()));
>+      return FALSE;
>+    }
>+    QUERY_SERVICE_CONFIGW &serviceConfig = 
>+      *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
>+
>+    // Obtain the version information of both files
>+    wchar_t *newServiceBinaryPath = NULL;
>+    DWORD existingMajor, existingMinor, existingAux, existingMaintenance;
>+    DWORD newMajor, newMinor, newAux, newMaintenance;    
>+    errno_t err = 0;
>+    if (err =_get_wpgmptr(&newServiceBinaryPath)) {
nit: space after =

Might be cleaner to use the path to the binary which is already in modulePath.

It isn't clear to me from my brief look on MSDN that _get_wpgmptr will return a long path and if it is at all like GetModuleFileNameW the path will be the path used to execute the binary which under some cases won't be a long path. Might be a good thing to always call GetLongPathNameW on paths so we are gauranteed to be working with a long path.

>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+        ("Could obtain service binary path, errno:  (%d)", 
>+        err));
>+      return FALSE;
>+    }
>+
>+    if(!GetVersionNumberFromPath(serviceConfig.lpBinaryPathName, 
>+                                 existingMajor, existingMinor, 
>+                                 existingAux, existingMaintenance) ||
>+       !GetVersionNumberFromPath(newServiceBinaryPath, newMajor, 
>+                                 newMinor, newAux, newMaintenance)) {
>+      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+             ("Could obtain version number from paths"));
>+      return FALSE;
>+    }
Per previous comment I don't think this should fail when an installed service is missing version info.

>+
>+    schService.reset(); //Explicitly close the handle so we can delete it
>+
>+    // Check if we need to replace the old binary with the new one
>+    if ((existingMajor < newMajor) ||
>+        (existingMajor == newMajor && existingMinor < newMinor) ||
>+        (existingMajor == newMajor && existingMinor == newMinor && 
>+         existingAux < newAux) ||
>+        (existingMajor == newMajor && existingMinor == newMinor && 
>+         existingAux == newAux && existingMaintenance < newMaintenance)) {
>+      if (!SvcUninstall()) {
>+        return FALSE;
>+      }
>+
>+      if (!DeleteFile(serviceConfig.lpBinaryPathName)) {
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Could not delete old service binary file.  (%d)", GetLastError()));
>+        return FALSE;
>+      }
>+
>+      if (!CopyFile(newServiceBinaryPath, 
>+                    serviceConfig.lpBinaryPathName, FALSE)) {
>+        PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+          ("Could not overwrite old service binary file.  (%d)", 
>+           GetLastError()));
>+        return FALSE;
The copy failing after a successful delete seems pretty bad. I don't think this would ever happen but please add some "run around with arms flailing" text to the PR_LOG to call this out if my assumption that this should never fail is true. If it isn't true please provide information of when this would happen.

Might be cleaner to do a rename of the old binary, a rename of the new binary, and then a delete of the old binary. Definitely more fallback options this way.

>+      }
>+
>+      // We made a copy of ourselves to the existing location.
>+      // The tmp file (the process of which we are executing right now) will be
>+      // left over.  Attempt to self delete ourselves in 5 seconds since we 
>+      // can't do it while we are running.  If it fails this is not a problem.
>+      DWORD err = DeferredDeletePath(newServiceBinaryPath);
>+      
>+      // Setup the new module path
>+      wcsncpy(modulePath, serviceConfig.lpBinaryPathName, MAX_PATH);
>+      // Fall through so we replace the service
>+    } else {
>+      // We don't need to copy ourselves to the existing location.
>+      // The tmp file (the process of which we are executing right now) will be
>+      // left over.  Attempt to self delete ourselves in 5 seconds since we 
>+      // can't do it while we are running.  If it fails this is not a problem.
>+      DWORD err = DeferredDeletePath(newServiceBinaryPath);
>+      
>+      return TRUE; // nothing to do, we already have a newer service installed
>+    }
>+  } else if (upgradeOnly) {
>+    // The service does not exist and we are upgrading, so don't install it
>+    return TRUE;
>+  }
>+
>+  // Create the service as on demand
>+  schService.own(CreateServiceW(schSCManager  , SVC_NAME, SVC_DISPLAY_NAME,
extra spaces after schSCManager

>+                              SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
>+                              SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
>+                              modulePath, NULL, NULL, NULL, NULL, NULL));
So, in the new install case you aren't renaming the binary prior to install?

>+  if (!schService) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not create Windows service.  (%d)", GetLastError()));
>+    return FALSE;
>+  } 
>+
>+  if (!SetUserAccessServiceDACL(schService)) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not set security ACE on service handle, the service will not be "
>+       "able to be started from unelevated processes.  (%d)", 
>+       GetLastError()));
>+  }
I think both of the above PR_LOG messages should denote that this is a failure we should never see.

>+
>+  return TRUE;
>+}
>+
Comment 321 Brian R. Bondy [:bbondy] 2011-11-10 09:33:25 PST
Created attachment 573542 [details] [diff] [review]
Patch 2 - Main service logic. v5.

Regarding patch 2:
- Session 0 comments are addressed in the follow up bug 701103
- Javadoc comments are addressed in patch 24
- Implemented various formatting fixes, some of them I left because they are from ode that is removed in bug 701103.
- Removed sharing access on the work item file, it's not needed, just force of habbit.
- Regarding the comment with a question in it "rs do you think via update status file?", this is removed in a future patch

> Might be a good thing to just have a counter in a pref that gets reset after success and incremented on failure. 
> When the failure count reach X number of failures disable using the service using the pref you added for this purpose.

This is a good idea but I think I prefer the status file.  
The new status in the status file seems safer to me especially since we do the update check post profile init.
Also it takes a bit of time to attempt an update, so if it fails we'd never want to try it a second time in succession.
When the maintenanceservice fails it relaunches the callback app, with a counter it would take a long time for 
any value > 1 going between the callback app and the maintenance service in succession without any user feedback.
So I'd like to keep that with the new status instead.


By the way, once every patch gets an r+, I'll provide a roll up patch of everything that you can look at for one final pass through.
Then I'll do a follow up patch for any final review comments.

I think whatever is left for patch 2 is addressed in future patches.
Comment 322 Brian R. Bondy [:bbondy] 2011-11-10 09:35:43 PST
Created attachment 573543 [details] [diff] [review]
Patch 16 - Support for launching callback app as unelevated. v5'.

rebased after patch 2 review comments
Comment 323 Brian R. Bondy [:bbondy] 2011-11-10 09:38:39 PST
Created attachment 573545 [details] [diff] [review]
Patch 18 - Write out update.status error code in maintenance service on failure. v2'''.

Rebased after patch 2 review comments
Comment 324 Brian R. Bondy [:bbondy] 2011-11-10 09:41:05 PST
Created attachment 573548 [details] [diff] [review]
Patch 21 - New service self update code. v1'.

Rebased after patch 2 review comments
Comment 325 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 09:48:42 PST
(In reply to Brian R. Bondy [:bbondy] from comment #321)
> Created attachment 573542 [details] [diff] [review] [diff] [details] [review]
> Patch 2 - Main service logic. v5.
> 
> Regarding patch 2:
> - Session 0 comments are addressed in the follow up bug 701103
> - Javadoc comments are addressed in patch 24
> - Implemented various formatting fixes, some of them I left because they are
> from ode that is removed in bug 701103.
> - Removed sharing access on the work item file, it's not needed, just force
> of habbit.
> - Regarding the comment with a question in it "rs do you think via update
> status file?", this is removed in a future patch
> 
> > Might be a good thing to just have a counter in a pref that gets reset after success and incremented on failure. 
> > When the failure count reach X number of failures disable using the service using the pref you added for this purpose.
> 
> This is a good idea but I think I prefer the status file.  
This is in conjunction with the status file. After x number of failures which would be reported by the status file the pref to disable the service would be set.
Comment 326 Brian R. Bondy [:bbondy] 2011-11-10 09:52:02 PST
> This is in conjunction with the status file. After x number of failures 
> which would be reported by the status file the pref to disable the service 
> would be set.

I guess I don't understand the pref then.  Is it to stop future upgrades from attempting to use the service forever? 

I think any failure that would happen (for example a bad certificate) would be auto fixed by some update in the future so we should always try to use the service once for each new update.
Comment 327 Brian R. Bondy [:bbondy] 2011-11-10 09:57:54 PST
> This is in conjunction with the status file. After x number of failures 
> which would be reported by the status file the pref to disable the service 
> would be set.

Also there is no negative side effect of attempting to use the service except maybe a second or two. The callback is launched with the new status right away which prompts.  Since the service is all silent work the user just thinks it works the same as before.
Comment 328 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 09:58:50 PST
It would only do this for specific error codes in the update status file, only after x number of failures, and only for the profile that experienced the failures.
Comment 329 Brian R. Bondy [:bbondy] 2011-11-10 10:37:23 PST
> It would only do this for specific error codes in the update status file, 
> only after x number of failures, and only for the profile that experienced 
> the failures.

OK fair enough, I posted a follow up task to do this to reduce traffic in this ticket: 
Bug 701436 - If maintenance service fails to apply an update X times, never use it again for updates
Comment 330 Brian R. Bondy [:bbondy] 2011-11-10 10:51:09 PST
Created attachment 573568 [details] [diff] [review]
Patch 25 - Global scope operator fix

Should be a quick review if you agree with it.
If not I'll just delete this patch.

I don't see any benefit from using the global scope operator on each Win32 API and I think it's much easier to stay consistent by not doing this convention.
Comment 331 Brian R. Bondy [:bbondy] 2011-11-10 12:06:58 PST
Created attachment 573590 [details] [diff] [review]
Patch 3 - Code for installing the service. v4.

- Implemented review comments.
- "> So, in the new install case you aren't renaming the binary prior to install?"
  Correct we only install the file from the nsis maintenanceservice script with maintenanceservice_tmp.exe if the file already exists.
  If the file does not exist, we just install it as maintenanceservice.exe
- Javadoc changes are in patch 24
- global scope :: changes are in patch 25
Comment 332 Brian R. Bondy [:bbondy] 2011-11-10 12:08:36 PST
Created attachment 573592 [details] [diff] [review]
Patch 24 - Fixed javadoc throughout. v1'.

Rebased after review comments on patch 3
Comment 333 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 12:16:33 PST
Comment on attachment 573590 [details] [diff] [review]
Patch 3 - Code for installing the service. v4.

rubber stamping this... I'll verify in the final patch that I do a final once over of.
Comment 334 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 12:35:54 PST
Comment on attachment 573590 [details] [diff] [review]
Patch 3 - Code for installing the service. v4.

I rubber stamped the wrong patch dag nabbit.

You obsoleted patch 3... are you working on a new version?
Comment 335 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-10 12:36:47 PST
Comment on attachment 573568 [details] [diff] [review]
Patch 25 - Global scope operator fix

Meant to rubber stamp this one... I'll verify this is correct in the final patch
Comment 336 Brian R. Bondy [:bbondy] 2011-11-10 12:44:13 PST
Comment on attachment 573590 [details] [diff] [review]
Patch 3 - Code for installing the service. v4.

> You obsoleted patch 3... are you working on a new version?

Sorry the correct name for this patch is Patch 3. Fixed and remarked r+. Patch 4 is the certificate check code.
Comment 337 Brian R. Bondy [:bbondy] 2011-11-10 12:45:29 PST
Comment on attachment 573590 [details] [diff] [review]
Patch 3 - Code for installing the service. v4.

Removing r+ just seen your note that you rubber stamped wrong one.
Comment 338 Brian R. Bondy [:bbondy] 2011-11-10 20:45:48 PST
Created attachment 573735 [details] [diff] [review]
All maintenance service and related code. v1.

- Consolidated all patches into one as per discussion with rs. 
- Although patch 7 RAII helpers is in the consolidated patch, I also left it here until it reaches sr+ from bsmedberg.
- If you want the NSPR logging to work better, you also need to apply the patch from "Bug 699567 - Add cross platform wide string formatting specifier for NSPR logging".  That will likely be pushed soon so didn't include it in this consolidated patch.  In particular if you don't push this any logging of filenames and cmd line strings will not show up in the log.
- Consolidated patch does not include the already pushed patches.

Happy reviewing :)
Comment 339 Brian R. Bondy [:bbondy] 2011-11-10 20:49:41 PST
Comment on attachment 573735 [details] [diff] [review]
All maintenance service and related code. v1.

Although the code will change some, I think it is mostly done. I think it would be a good time for a final code review from a security perspective.  Please re-assign the second review Ian if it will be someone else doing it.
Comment 340 Brian R. Bondy [:bbondy] 2011-11-10 20:59:24 PST
Created attachment 573736 [details] [diff] [review]
All maintenance service and related code. v1'.

Rebased for recent push to a makefile on mozilla-central tip.
Comment 341 Ian Melven :imelven 2011-11-11 15:42:45 PST
Comment on attachment 573736 [details] [diff] [review]
All maintenance service and related code. v1'.

Review of attachment 573736 [details] [diff] [review]:
-----------------------------------------------------------------

Just a thought that we probably want to make sure the binaries involved (namely the service DLL) are marked with all the appropriate security mechanisms
like /GS, /ASLR etc. so we opt into the OS level protections. 

Overall looks good from a security perspective minus the concern about passing arguments to the callback app

::: toolkit/components/maintenanceservice/maintenanceservice.cpp
@@ +218,5 @@
> +    gServiceStopping = TRUE;
> +    if (!PathAppendSafe(stopFilePath, L"stop")) {
> +      TerminateThread(thread, 2);
> +    }
> +    HANDLE stopFile = CreateFile(stopFilePath, GENERIC_READ, 0, 

i thought we were removing the need for the stop file ?

::: toolkit/components/maintenanceservice/registrycertificates.cpp
@@ +63,5 @@
> +  // We use KEY_WOW64_64KEY to always force 64-bit view.
> +  // The user may have both x86 and x64 applications installed
> +  // which each register information.  We need a consistent place
> +  // to put those certificate attributes in and hence why we always
> +  // force the non redirected registry under Wow6432Node.

you might want to add to the comment that the flag is ignored on 32 bit windows

::: toolkit/components/maintenanceservice/serviceinstall.cpp
@@ +211,5 @@
> +      // We made a copy of ourselves to the existing location.
> +      // The tmp file (the process of which we are executing right now) will be
> +      // left over.  Attempt to self delete ourselves in 5 seconds since we 
> +      // can't do it while we are running.  If it fails this is not a problem.
> +      MoveFileEx(newServiceBinaryPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);

where does 5 seconds come from ? the documentation says the flag does what it sounds like,
wait until an os reboot.

::: toolkit/components/maintenanceservice/uachelper.cpp
@@ +100,5 @@
> +  return hNewLinkedToken;
> +}
> +
> +/**
> + * Opens a linked token for the specified token.

looks like a c & p from above ?

::: toolkit/components/maintenanceservice/workmonitor.cpp
@@ +109,5 @@
> +  LPVOID environmentBlock = NULL;
> +  LPWSTR cmdLineMinusCallback = MakeCommandLine(min(argcTmp, 4), argvTmp);
> +
> +  // If we're about to start the update process from session 0 on vista
> +  // or later, then we should not show a GUI.

i don't understand how the updater.ini file related to showing a gui - can you elaborate please ?

@@ +130,5 @@
> +    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
> +      ("Process was started... waiting on result.")); 
> +
> +    // Wait for the updater process to finish
> +    WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);

it looks like if this times out, GetExitCodeProcess will return still active
and update was Successful will correctly return false - then we start the callback app. 
maybe we should consider killing the process if the time out happens ?

@@ +270,5 @@
> +       notifyInfo.FileName));
> +    return TRUE;
> +  }
> +  cmdlineBuffer[cmdLineBufferRead] = L'\0';
> +  cmdlineBuffer[cmdLineBufferRead + 1] = '\0';

L'\0' is 2 bytes so i think you don't want the 2nd one of these (cmdlineBuffer is a char buffer not wchar)

@@ +283,5 @@
> +  LPWSTR* argvTmp = CommandLineToArgvW(cmdlineBufferWide, &argcTmp);
> +
> +  // Validate the certificate of the app the user wants us to start.
> +  // Also check to make sure the certificate is trusted.
> +#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK

i assume this is for testing ?

@@ +319,5 @@
> +      PR_LOG(gServiceLog, PR_LOG_ALWAYS,
> +        ("Could not write update.status pending-no-service.  Last error: %d", 
> +         GetLastError()));
> +    }
> +    StartCallbackApp(argcTmp, argvTmp, sessionID);

this looks like the callback app takes a whole command line from the work item - so could i create a workitem (since the dir is world writeable) and a spoofed session ID of another user and inject arbitrary arguments to an existing app and have it run in their session ? i was under the impression the callback app was going to be a path to an executable and not allow specifying parameters, can you confirm ?

@@ +502,5 @@
> +    }
> +    return FALSE;
> +  }
> +
> +#ifdef ENABLE_CALLBACK_AUTHENTICODE_CHECK

i assume this is not defined, since we don't have a process for signing nightly builds yet - will it be defined for releases / betas ?

::: toolkit/xre/nsWindowsRestart.cpp
@@ +295,5 @@
> +
> +BOOL
> +PathAppendSafe(LPWSTR base, LPCWSTR extra)
> +{
> +  if (wcslen(base) + wcslen(extra) > MAX_PATH) {

according to some MS documentation i just found, MAX_PATH is supposed to include the terminating NULL, so maybe this should be if (wcslen(base) + wcslen(extra) >= MAX_PATH) ?

the buffers passed in all seem to be MAX_PATH + 1 so this is a bit of a nit, although later in the file MAX_PATH * sizeof(WCHAR) is used as the size to write to the file (i believe these are considered to not necc. be null terminated strings written to the file though IIRC)
Comment 342 Brian R. Bondy [:bbondy] 2011-11-14 07:13:43 PST
Created attachment 574292 [details] [diff] [review]
All maintenance service and related code. v2.

Thanks for the review comments Ian!

> i thought we were removing the need for the stop file ?

It's needed.  I use it to wake up the sync call to the directory watching.  
That way I can safely exit that thread withougt blindly killing it on service stop.  
It won't actually stop monitoring unless the flag is set though, so if someone just drops a file that says stop in it won't actually stop.  
Only if the stop service command is issued and the service is shutting down will gServiceStopping be true.   
We could alternatively use the async API for directory watching, but I see no benefit to do that.

> maybe we should consider killing the process if the time out happens ?

That's a good idea, I implemented killing updater.exe if it takes more than 45 minutes.  
It would have still been handled as an error before because the GetExitCodeProcess function call would have failed, but it's better to explicitly handle the timeout case and kill the process.  
We wouldn't want the operation to succeed right after this and then have the service write the pending-no-service status in the status file.


> +#ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK
> i assume this is for testing ?

Yup for testing only, and may be used for some automated tests if we can't get the try builds signed.


> +#ifdef ENABLE_CALLBACK_AUTHENTICODE_CHECK
> i assume this is not defined, since we don't have a process for 
> signing nightly builds yet - will it be defined for releases / betas ?

This is not defined currently and is planned to land on Nightly that way. Please advise if you think that is a big problem. Session ID spoofing would be possible here as you mentioned.  But the app will always be run unelevated.  The callback app is restarting firefox after the update.  If a user started firefox with -P profileName then we want to restart with those same command line params.   I plan to change that ifdef to #ifndef DISABLE_SERVICE_AUTHENTICODE_CHECK once we have nightly builds automatically signed.

> i assume this is not defined, since we don't have a process for signing 
> nightly builds yet - will it be defined for releases / betas ?

If acceptable I'd only like it this way only on Nightly.  As with the cert check on the udpater itself not even on Aurora.

> according to some MS documentation i just found, MAX_PATH is supposed 
> to include the terminating NULL...

I'm pretty sure that sometimes it is and sometimes it's not, so it's best to not make an assumption there.  I always allocated MAX_PATH + 1 to be safe when using any such API.  So yup I changed to "if (wcslen(base) + wcslen(extra) >= MAX_PATH)".  As for why I write only MAX_PATH * sizeof(WCHAR), this is just how I defined the file format of the work items.
Comment 343 Brian R. Bondy [:bbondy] 2011-11-14 07:42:58 PST
Created attachment 574298 [details] [diff] [review]
For security review only

As per email, I extracted the files I think are important for security review.
The full patch is left as is for Robert Strong to review.
Comment 344 Ian Melven :imelven 2011-11-14 11:40:33 PST
(In reply to Brian R. Bondy [:bbondy] from comment #342)
>
> Thanks for the review comments Ian!

you are very welcome :) 
 
> > i thought we were removing the need for the stop file ?
> 
> It's needed.  I use it to wake up the sync call to the directory watching.  
> That way I can safely exit that thread withougt blindly killing it on
> service stop.  
> It won't actually stop monitoring unless the flag is set though, so if
> someone just drops a file that says stop in it won't actually stop.  
> Only if the stop service command is issued and the service is shutting down
> will gServiceStopping be true.   
> We could alternatively use the async API for directory watching, but I see
> no benefit to do that.

that all sounds fine to me - even if the service is stopped, the next update will attempt to start it again, IIRC - so i don't see any risk here especially
with the points you made above

> > +#ifdef ENABLE_CALLBACK_AUTHENTICODE_CHECK
> > i assume this is not defined, since we don't have a process for 
> > signing nightly builds yet - will it be defined for releases / betas ?
> 
> This is not defined currently and is planned to land on Nightly that way.
> Please advise if you think that is a big problem. Session ID spoofing would
> be possible here as you mentioned.  But the app will always be run
> unelevated.  The callback app is restarting firefox after the update.  If a
> user started firefox with -P profileName then we want to restart with those
> same command line params.   I plan to change that ifdef to #ifndef
> DISABLE_SERVICE_AUTHENTICODE_CHECK once we have nightly builds automatically
> signed.

since we're always restarting firefox.exe (i assume the executable isn't often renamed ?) could we hardcode 'firefox.exe' and then have the path come from the work item - this would solve my concern with running arbitrary executables with an arbitrary command line without requiring signing (although signing is obv. better for non-nightly releases). also re session spoofing - since the callback app is being run unelevated, it may be that the unelevated user token wouldn't have access to the other user's session by default. we need to do some testing to see if the session injection is a practical attack (i'm tracking this). 

> > according to some MS documentation i just found, MAX_PATH is supposed 
> > to include the terminating NULL...
> 
> I'm pretty sure that sometimes it is and sometimes it's not, so it's best to
> not make an assumption there.  I always allocated MAX_PATH + 1 to be safe
> when using any such API.  So yup I changed to "if (wcslen(base) +
> wcslen(extra) >= MAX_PATH)".  As for why I write only MAX_PATH *
> sizeof(WCHAR), this is just how I defined the file format of the work items.

yeah, i agree that it's best to be defensive with this and not rely on the documentation.

thanks !
Comment 345 Brian R. Bondy [:bbondy] 2011-11-15 18:47:04 PST
Created attachment 574770 [details]
Prefs with and without the maint service installed

The preference screen already had a r+ from faaborg.
What wasn't approved yet was hiding this option when the maintenance service is not installed.  Also I wanted to make sure that it's ok that we show the option for both limited user accounts and admin accounts.  I think it is OK to show for both admin and limited user accounts as we will advise people to use lock_pref() if they are concerned about it.
Comment 346 Brian R. Bondy [:bbondy] 2011-11-15 22:09:13 PST
Created attachment 574798 [details] [diff] [review]
Various additions described in Comment 346

This patch is in addition to the consolidated patch.
I will merge them both into one once the reviews are done.

Changes in this patch:
- Now only show the preference if the maintenance service is installed.
- Fixed PostUpdate proces so it runs now for both admins and limited user acocunts
- Update check is reverted to pre xpcom init again (this change is in base consolidated patch)
- Now do updates via pending and pending-service states. pending-service is used if an update is pending and the service should be used to attempt it.
- Fixed up error handling a bit so updater.exe failed state actually survives and doesn't get overwritten by "pending" by the service.
- Added new pref for max fail count where service won't be reached if it fails too many times.
- Now hide updater.exe via CreateProcess flags and not by deleting updater.ini since it contains the PostUpdate info.
- Other minor cleanups and fixes.
Comment 347 Brian R. Bondy [:bbondy] 2011-11-15 22:24:38 PST
Created attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

This is the same consolidated patch as before (excluding the new patch which aslo has rs to review on).  The only difference is I removed the move of the update check to post XPCOM init diff file.

Please feel free to provide review comments on the old consolidated patch or this one, they are the same.
Comment 348 Brian Smith (:briansmith, :bsmith, use NEEDINFO?) 2011-11-16 15:49:21 PST
Comment on attachment 574298 [details] [diff] [review]
For security review only

Review of attachment 574298 [details] [diff] [review]:
-----------------------------------------------------------------

Where are the tests for this patch?
Comment 349 Brian R. Bondy [:bbondy] 2011-11-17 08:46:11 PST
There are no automated tests yet for this patch.
Comment 350 Brian R. Bondy [:bbondy] 2011-11-22 10:41:57 PST
Just wanted to give an update on automated tests.
Ehsan and I just had a call and here is the plan:

Update tests can be broken down into these 6 sections:
1. Old tests (These already exist)
2. Old tests copied and applied to background updates (Ehsan already did this)
3. Old tests copied and applied to background updates using the service (Ehsan volunteered to do these, in the context of his bug)
4. Old tests copied and applied to the service (Ehsan volunteered to do these, in the context of this bug)
5. New tests applied to background updates (Ehsan already did this, in the context of his bug)
6. New tests applied to the service itself (Brian (me) will do this)

So relating to this bug (bug 481815), Ehsan will attach a new patch for #4, and I will attach a new patch for #6 when done. 

How the service will be tested:
Testing through the service is hard because you need to have elevated permissions to install the service.  Also the service does things with user tokens that only the SYSTEM account is allowed to do.

Ehsan talked to RelEng about an idea he had to always have a base copy of the service installed on the machines that would be used to install the version of the service that needs to be tested.  This seems to be the way we will proceed with testing the service itself.
Comment 351 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-22 11:22:10 PST
There's also new tests for background updates in presence of the service.  I'll work on this as well.

I will update about our plans after I talk with RelEng again today.
Comment 352 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-22 12:22:04 PST
I talked with Brian and bhearsum from RelEng about this, and we're going to go with this proposal for automated testing infrastructure: <https://wiki.mozilla.org/Silent_Update_OS_Dialogs/Automated_testing>.

I filed bug 704578 for this.
Comment 353 Alex Limi (:limi) — Firefox UX Team 2011-11-22 20:52:08 PST
Comment on attachment 574770 [details]
Prefs with and without the maint service installed

Looks good to me.
Comment 354 Brian R. Bondy [:bbondy] 2011-11-22 21:19:31 PST
Created attachment 576402 [details] [diff] [review]
Incremental changes on the big base patch. v2.

Merged a couple of fixes from ehsan.
- Now using file share all flags again on the work item file, this was recently changed to no sharing. It causes problems with the rename not closing the handle before the service can open it.
- couple of instances of PENDING_NO_SERVICE needed to be changed to PENDING_SERVICE.
- About dialog had to check for "pending-service"
Comment 355 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:01:41 PST
Comment on attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

>diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
>--- a/browser/installer/package-manifest.in
>+++ b/browser/installer/package-manifest.in
>@@ -28,16 +28,17 @@
> @BINPATH@/defaults/profile/mimeTypes.rdf
> @BINPATH@/defaults/profile/chrome/*
> @BINPATH@/update.locale
> @BINPATH@/updater.ini
> @BINPATH@/dictionaries/*
> @BINPATH@/hyphenation/*
> #ifdef XP_WIN32
> @BINPATH@/uninstall/helper.exe
>+@BINPATH@/maintenanceservice_installer.exe
nit: Just place this after maintenanceservice.exe so they are all in one place

> #endif
> 
> [xpcom]
> @BINPATH@/dependentlibs.list
> #ifndef MOZ_STATIC_JS
> @BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
> #endif
> @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
>@@ -519,16 +520,22 @@ bin/libfreebl_32int64_3.so
> ; [Updater]
> ;
> #ifdef XP_MACOSX
> @BINPATH@/updater.app/
> #else
> @BINPATH@/updater@BIN_SUFFIX@
> #endif
> 
>+; [MaintenanceService]
>+;
>+#ifdef XP_WIN32
>+@BINPATH@/maintenanceservice.exe
>+#endif
>+

>diff --git a/toolkit/components/maintenanceservice/Makefile.in b/toolkit/components/maintenanceservice/Makefile.in
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/Makefile.in
>@@ -0,0 +1,103 @@
>+
>+DEPTH     = ../../..
>+topsrcdir = @top_srcdir@
>+srcdir    = @srcdir@
>+VPATH     = @srcdir@
>+
>+include $(DEPTH)/config/autoconf.mk
>+
>+CPPSRCS = \
>+  maintenanceservice.cpp \
>+  serviceinstall.cpp \
>+  workmonitor.cpp \
>+  certificatecheck.cpp \
>+  servicebase.cpp \
>+  registrycertificates.cpp \
>+  uachelper.cpp \
>+  pathhash.cpp \
>+  $(NULL)
>+
>+ifneq ($(OS_TARGET),Android)
nit: not necessary since this Makefile is already excluded for Android in the parent dir. I'd prefer to keep unnecessary cruft out so people don't scratch their heads as to why it is there. 

>+PROGRAM = maintenanceservice$(BIN_SUFFIX)
>+DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX)
>+
>+# Don't link the maintenanceservice against libmozutils. See bug 687139
>+MOZ_UTILS_LDFLAGS =
>+MOZ_UTILS_PROGRAM_LDFLAGS =
>+endif
>+
>+LIBS += \
>+  $(NSPR_LIBS) \
>+  $(NULL)
>+
>+ifeq ($(OS_ARCH),WINNT)
same here

>+USE_STATIC_LIBS = 1
>+HAVE_PROGRESSUI = 1
>+RCINCLUDE = maintenanceservice.rc
>+
>+OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
>+DEFINES += -DUNICODE -D_UNICODE
>+ifndef GNU_CC
>+RCFLAGS += -I$(srcdir)
>+else
>+RCFLAGS += --include-dir $(srcdir)
>+endif
>+
>+endif
>+
>+ifndef MOZ_WINCONSOLE
>+ifdef MOZ_DEBUG
>+MOZ_WINCONSOLE = 1
>+else
>+MOZ_WINCONSOLE = 0
>+endif
>+endif
>+
>+include $(topsrcdir)/config/rules.mk
>+
>+DEFINES += -DNS_NO_XPCOM
>+
>+ifdef _MSC_VER
>+WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
>+endif
>+
>+ifeq ($(OS_ARCH),WINNT)
same here

>+# Pick up nsWindowsRestart.cpp
>+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
>+endif

>diff --git a/toolkit/components/maintenanceservice/maintenanceservice.ico b/toolkit/components/maintenanceservice/maintenanceservice.ico
>new file mode 100644
>index 0000000000000000000000000000000000000000..48457029d6aa5f090e5964e22d2a580017e31b7b
Do we really care if the service has an icon? Many (most?) don't.

>diff --git a/toolkit/components/maintenanceservice/serviceinstall.cpp b/toolkit/components/maintenanceservice/serviceinstall.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/serviceinstall.cpp
>@@ -0,0 +1,430 @@

>+/**
>+ * Uninstalls the Maintenance service.
>+ *
>+ * @return TRUE if successful.
>+ */
>+BOOL
>+SvcUninstall()
>+{
>+  // Get a handle to the local computer SCM database with full access rights.
>+  nsAutoServiceHandle schSCManager(OpenSCManager(NULL, NULL, 
>+                                                 SC_MANAGER_ALL_ACCESS));
>+  if (!schSCManager) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not open service manager.  (%d)", GetLastError()));
>+    return FALSE;
>+  }
>+
>+  // Open the service
>+  nsAutoServiceHandle schService(OpenServiceW(schSCManager, SVC_NAME, 
>+                                              SERVICE_ALL_ACCESS));
>+  if (!schService) {
>+    PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+      ("Could not open service.  (%d)", GetLastError()));
>+    return FALSE;
>+  } 
>+
>+  //Stop the service so it deletes faster and so the uninstaller
>+  // can actually delete its EXE.
>+  DWORD totalWaitTime = 0;
>+  SERVICE_STATUS status;
>+  static const int maxWaitTime = 1000 * 60; // Never wait more than a minute
>+  if (ControlService(schService, SERVICE_CONTROL_STOP, &status)) {
>+    do {
>+      Sleep(status.dwWaitHint);
>+      totalWaitTime += (status.dwWaitHint + 10);
>+      if (status.dwCurrentState == SERVICE_STOPPED) {
>+        break;
>+      } else if (totalWaitTime > maxWaitTime) {
>+        break;
>+      }
>+    } while (QueryServiceStatus(schService, &status));
>+  }
>+
>+  // Delete the service or mark it for deletion
>+  BOOL deleted = DeleteService(schService);
>+  if(!deleted) {
>+    deleted = (GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE);
I recall ERROR_SERVICE_MARKED_FOR_DELETE preventing the install of a service with the same name back in Win2K days. Have you / can you verify what happens in this case? Might be a good thing to verify on Win XP that the behavior is the same.

On this same note what do you think about not installing the service on Win2K? I suggest this since support for Win2K will likely go away in the release immediately following the installation of the service.

>+  }
>+
>+  return deleted;
>+}
>+

>diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp b/toolkit/components/maintenanceservice/workmonitor.cpp
>new file mode 100644
>--- /dev/null
>+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
>@@ -0,0 +1,561 @@
>...
>+// Wait 45 minutes for an update operation to run at most.
>+// Updates usually take less than a minute so this seems like a 
>+// significantly large and safe amount of time to wait.
>+static const int TIME_TO_WAIT_ON_UPDATER = 45 * 60 * 1000;
This is crazy big. ;)

I can't even imagine an update that takes 5 minutes much less 45 minutes. What do you think?

>...
>+StartUpdateProcess(LPCWSTR appToStart, 
>+                   LPCWSTR workingDir, 
>+                   int argcTmp,
>+                   LPWSTR *argvTmp,
>+                   DWORD callbackSessionID = 0)
>+{
>+  BOOL processStarted = FALSE;
>+  DWORD myProcessID = GetCurrentProcessId();
>+  DWORD mySessionID = 0;
>+  ProcessIdToSessionId(myProcessID, &mySessionID);
>+
>+  STARTUPINFO si = {0};
>+  si.cb = sizeof(STARTUPINFO);
>+  si.lpDesktop = L"winsta0\\Default";
>+  PROCESS_INFORMATION pi = {0};
>+  nsAutoHandle elevatedToken, unelevatedToken;
>+
>+  PR_LOG(gServiceLog, PR_LOG_ALWAYS,
>+    ("Starting process in an elevated session.  Service "
>+     "session ID: %d; Requested callback session ID: %d", 
>+     mySessionID, callbackSessionID));
>+
>+  // The updater command line is of the form:
>+  // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
>+  // So update callback-dir is the 4th, callback-path is the 5th and its args 
>+  // are the 6th index.  So that we can execute the callback out of line we
>+  // won't call updater.exe with those callback args and we will manage the
>+  // callback ourselves.
>+  LPVOID environmentBlock = NULL;
>+  LPWSTR cmdLineMinusCallback = MakeCommandLine(min(argcTmp, 4), argvTmp);
>+
>+  // If we're about to start the update process from session 0 on Vista
>+  // or later, then we should not show a GUI.  One way to disable the
>+  // updater.exe GUI is to delete the updater.ini file.
>+  if (UACHelper::IsVistaOrLater() && argcTmp >= 2 ) {
>+    si.lpDesktop = L"";
>+    WCHAR updaterINI[MAX_PATH + 1];
>+    wcscpy(updaterINI, argvTmp[1]);
>+    if (PathAppendSafe(updaterINI, L"updater.ini")) {
>+      DeleteFileW(updaterINI);
>+    }
Though this has changed somewhat in the next patch but I don't think we want different behavior regarding showing or not showing the updater progress based on OS version.

I wonder if elevation type or if the user is admin is interesting / needed if we always use session 0 to apply the update? Could you provide some details?

>+  }
>+
>+  processStarted = CreateProcessW(appToStart, cmdLineMinusCallback, 
>+                                  NULL, NULL, FALSE, 
>+                                  CREATE_DEFAULT_ERROR_MODE | 
>+                                  CREATE_UNICODE_ENVIRONMENT, 
>+                                  NULL, workingDir, &si, &pi);
Comment 356 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:10:22 PST
Comment on attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

>diff --git a/other-licenses/nsis/Contrib/ServicesHelper/Services.cpp b/other-licenses/nsis/Contrib/ServicesHelper/Services.cpp
>new file mode 100644
Though this code still needs review if you publish it on the NSIS web site we don't have to take this code into the repo.

>--- /dev/null
>+++ b/other-licenses/nsis/Contrib/ServicesHelper/Services.cpp
>@@ -0,0 +1,272 @@
>...
>+/**
>+ * Determines a unique registry path from a file or directory path
>+ * 
>+ * @param  stacktop  A pointer to the top of the stack
>+ * @param  variables A pointer to the NSIS variables 
>+ * @return The unique registry path or an empty string on error
>+ */
>+extern "C" void __declspec(dllexport)
>+PathToUniqueRegistryPath(HWND hwndParent, int string_size, 
>+                         TCHAR *variables, stack_t **stacktop, 
>+                         void *extra)
>+{
>+  TCHAR tmp[MAX_PATH] = { L'\0' };
>+  WCHAR installBasePath[MAX_PATH] = { '\0' };
>+  popstring(stacktop, tmp, MAX_PATH);
>+
>+#if !defined(UNICODE)
>+    MultiByteToWideChar(CP_ACP, 0, tmp, -1, installBasePath, MAX_PATH);
>+#else
>+    wcscpy(installBasePath, tmp);
>+#endif
>+
>+  WCHAR registryPath[MAX_PATH + 1] = { '\0' };
>+  if (CalculateRegistryPathFromFilePath(installBasePath, registryPath)) {
This is kind of a bummer... guess we'll just have a Mozilla only plugin.

btw: thanks for compiling this down to a small size... every bit helps.
Comment 357 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:15:24 PST
Though it looks fine I'm still slightly concerned about locale packaging... it tends to bite us in the butt whenever we make changes. Have you tried local repackaging yet? If not, I can over the U.S. holiday.
Comment 358 Brian R. Bondy [:bbondy] 2011-11-23 17:29:45 PST
> Though it looks fine I'm still slightly concerned about locale 
> packaging... it tends to bite us in the butt whenever we make 
> changes. Have you tried local repackaging yet? If not, I 
> can over the U.S. holiday.

Is this a normal part of try builds? If so last time I tried try builds they were passing.  If not please let me know the command to run and I can make sure it works. I don't want to eat up any of your holiday.
Comment 359 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-23 17:30:40 PST
Created attachment 576663 [details] [diff] [review]
WIP - Adding tests using the maintenance service

This is the first step towards having tests which can use the maintenance service to test the behavior of the updater.  This patch adds one test equivalent to test_0110_general.js which uses the maintenance service to launch the updater, exactly what happens when an update is initiated from Firefox with the presence of the service.  It also includes all of the test infrastructure service necessary to make this work.

The basic idea is that we stage the update root dir and the app dir, set update.status to "pending-service", then launch firefox.exe -process-updates, with two environment variables set to override the update root dir and the app dir from their normal values.  Firefox will then get in touch with the service, and the service will launch the updater.exe process.  We'll keep watching update.status for a success code, and as soon as we get that code, we proceed to verify the results of the update the same way that test_0110_general.js does.

This patch should apply on top of Brian's patches.  Brian, I'd appreciate if you can take a look at this and let me know what you think.  For those who are curious to follow the development of more tests in this category can watch this branch: <https://github.com/ehsan/mozilla-central/tree/nouac-tests>.
Comment 360 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:33:39 PST
No, it isn't. The steps are a tad convoluted and I tend to get them wrong a few times before I get them right so I'll just do this over the weekend unless Axel or someone provide the details. If no one does provide details I'll post the process I used in this bug after I get them right.
Comment 361 Brian R. Bondy [:bbondy] 2011-11-23 17:37:11 PST
Ehsan: Great update, thanks!  I'll take a look as soon as possible and post

Robert: Thanks, I think this can wait until next week if you're busy.  Once you post the steps I will verify them and I can create a wiki page about it.
Comment 362 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:38:43 PST
I think there might already be a wiki page for it where I got the details from originally though I haven't found it for quite some time.

>diff --git a/toolkit/xre/nsWindowsRestart.cpp b/toolkit/xre/nsWindowsRestart.cpp
>--- a/toolkit/xre/nsWindowsRestart.cpp
>+++ b/toolkit/xre/nsWindowsRestart.cpp
>...
>+BOOL
>+GetUpdateDirectoryPath(WCHAR *path) 
>+{
>+  HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 
>+    SHGFP_TYPE_CURRENT, path);
>+  if (FAILED(hr)) {
>+    return FALSE;
>+  }
>+  if (!PathAppendSafe(path, L"Mozilla")) {
>+    return FALSE;
>+  }
>+  // The directory should already be created from the installer, but
>+  // just to be safe in case someone deletes.
>+  CreateDirectoryW(path, NULL);
The installer creates this directory so all users can write to it while this doesn't. :(

>+/**
>+ * Sets update.status to pending-no-service so that the next startup will
>+ * not use the service and attempt an update the old way.
>+ *
>+ * @param  updateDirPath    The path of the update directory
>+ * @return TRUE if successful
>+ */
>+BOOL
>+WriteStatusPendingNoService(LPCWSTR updateDirPath)
Could this be moved to nsUpdateDriver.cpp?

>+{
>+  WCHAR updateStatusFilePath[MAX_PATH + 1];
>+  wcscpy(updateStatusFilePath, updateDirPath);
>+  if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
>+    return FALSE;
>+  }
>+
>+  const char pendingNoService[] = "pending-no-service";
>+  nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, 
>+                                      NULL, CREATE_ALWAYS, 0, NULL));
>+  if (statusFile == INVALID_HANDLE_VALUE) {
>+    return FALSE;
>+  }
>+
>+  DWORD wrote;
>+  BOOL ok = WriteFile(statusFile, pendingNoService, 
>+                      sizeof(pendingNoService) - 1, &wrote, NULL); 
>+  return ok && (wrote == sizeof(pendingNoService) - 1);
>+}
Comment 363 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:47:31 PST
Comment on attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

>diff --git a/browser/installer/windows/Makefile.in b/browser/installer/windows/Makefile.in
>--- a/browser/installer/windows/Makefile.in
>+++ b/browser/installer/windows/Makefile.in
>diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi
>--- a/browser/installer/windows/nsis/installer.nsi
>+++ b/browser/installer/windows/nsis/installer.nsi
>@@ -35,16 +36,17 @@
> # ***** END LICENSE BLOCK *****
> 
> # Required Plugins:
> # AppAssocReg   http://nsis.sourceforge.net/Application_Association_Registration_plug-in
> # ApplicationID http://nsis.sourceforge.net/ApplicationID_plug-in
> # CityHash      http://mxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
> # ShellLink     http://nsis.sourceforge.net/ShellLink_plug-in
> # UAC           http://nsis.sourceforge.net/UAC_plug-in
>+# ServicesHelper
Realign the above and add a note about this being a Mozilla specific plugin that is located in other-licenses.

>@@ -477,16 +506,30 @@ Section "-Application" APP_IDX
>         ; add the log entry without the path since there is no simple way to
>         ; know the correct full path.
>         ${LogMsg} "Added Quick Launch Shortcut: ${BrandFullName}.lnk"
>         GetFunctionAddress $0 AddQuickLaunchShortcut
>         UAC::ExecCodeSegment $0
>       ${EndIf}
>     ${EndUnless}
>   ${EndIf}
>+

Add a comment that this has to be done at the end so the correct registry view is maintained during the installation
>+  ; Allow main Mozilla cert information for updates
>+  ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
>+  Pop $MaintCertKey
>+  ${If} $MaintCertKey != ""
>+    ; We always use the 64bit registry for certs
>+    ; This call is ignored on 32-bit systems.
>+    SetRegView 64
>+    WriteRegStr HKLM "$MaintCertKey\0" "name" "Mozilla Corporation"
>+    WriteRegStr HKLM "$MaintCertKey\0" "issuer" "Thawte Code Signing CA - G2"
>+    WriteRegStr HKLM "$MaintCertKey\0" "programName" ""
>+    WriteRegStr HKLM "$MaintCertKey\0" "publisherLink" ""
>+    WriteRegStr HKLM "$MaintCertKey\0" "moreInfoLink" "http://www.mozilla.com"
>+  ${EndIf} 
Make a note that we can't have a fallback certificate in this instance here and in other places as appropriate.


> SectionEnd
> 
> ; Cleanup operations to perform at the end of the installation.
> Section "-InstallEndCleanup"
>   SetDetailsPrint both
>   DetailPrint "$(STATUS_CLEANUP)"
>   SetDetailsPrint none
> 
>@@ -794,16 +837,52 @@ Function leaveShortcuts
>     ${MUI_INSTALLOPTIONS_READ} $AddQuickLaunchSC "shortcuts.ini" "Field 4" "State"
>   ${EndUnless}
> 
>   ${If} $InstallType == ${INSTALLTYPE_CUSTOM}
>     Call CheckExistingInstall
>   ${EndIf}
> FunctionEnd
> 
>+Function preComponents
>+  ; Don't show the custom components page if the
>+  ; user is not an admin
>+  Call IsUserAdmin
>+  Pop $R9
>+  ${If} $R9 != "true"
>+    Abort
>+  ${EndIf}
>+
>+  ; If the service already exists, don't show this page
>+  ; We will always install again (which will upgrade)
>+  ; as long as the user is admin
>+  ServicesHelper::IsInstalled "MozillaMaintenance"
>+  Pop $R9
>+  ${If} $R9 == 1
>+    ; The service already exists so don't show this page.
>+    Abort
>+  ${EndIf}
>+
>+  StrCpy $PageName "Components"
Sent an email to Blake regarding this and cc'd you.
Comment 364 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:51:50 PST
Comment on attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

>diff --git a/browser/installer/windows/nsis/maintenanceservice_installer.nsi b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
>new file mode 100644
>--- /dev/null
>+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
>@@ -0,0 +1,264 @@
>...
>+# Required Plugins:
>+# ServicesHelper
>+
>+; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
>+!verbose 3
>+
>+; 7-Zip provides better compression than the lzma from NSIS so we add the files
>+; uncompressed and use 7-Zip to create a SFX archive of it
>+SetDatablockOptimize on
>+SetCompress off
>+CRCCheck on
>+
>+RequestExecutionLevel admin
>+!addplugindir ./
>+
>+; Variables
>+Var PageName
>+Var TempMaintServiceName
>+
>+; Modenr UI
>+!include "MUI2.nsh"
>+
>+
>+; Other included files may depend upon these includes!
>+; The following includes are provided by NSIS.
>+!include FileFunc.nsh
>+!include LogicLib.nsh
>+!include MUI.nsh
>+!include WinMessages.nsh
>+!include WinVer.nsh
>+!include WordFunc.nsh
>+
>+!insertmacro GetOptions
>+!insertmacro GetParameters
>+!insertmacro GetSize
>+!insertmacro StrFilter
>+!insertmacro WordFind
>+!insertmacro WordReplace
>+
>+!define CompanyName "Mozilla Corporation"
We need to think about how 3rd parties can use this.

>+
>+; The following includes are custom.
>+!include defines.nsi
>+; We keep defines.nsi defined so that we get other things like 
>+; the version number, but we redefine BrandFullName
>+!define MaintFullName "Mozilla Maintenance Service"
>+!undef BrandFullName
>+!define BrandFullName "${MaintFullName}"
>+
>+!include common.nsh
>+!include locales.nsi
>+
>+; Must be inserted before other macros that use logging
>+!insertmacro _LoggingCommon
>+
>+!insertmacro AddDDEHandlerValues
>+!insertmacro ChangeMUIHeaderImage
>+!insertmacro CheckForFilesInUse
>+!insertmacro CleanUpdatesDir
>+!insertmacro CopyFilesFromDir
>+!insertmacro CreateRegKey
>+!insertmacro GetPathFromString
>+!insertmacro GetParent
>+!insertmacro IsHandlerForInstallDir
>+!insertmacro IsPinnedToTaskBar
>+!insertmacro LogDesktopShortcut
>+!insertmacro LogQuickLaunchShortcut
>+!insertmacro LogStartMenuShortcut
>+!insertmacro ManualCloseAppPrompt
>+!insertmacro PinnedToStartMenuLnkCount
>+!insertmacro RegCleanAppHandler
>+!insertmacro RegCleanMain
>+!insertmacro RegCleanUninstall
>+!insertmacro SetAppLSPCategories
>+!insertmacro SetBrandNameVars
>+!insertmacro UpdateShortcutAppModelIDs
>+!insertmacro WriteRegStr2
>+!insertmacro WriteRegDWORD2
>+!insertmacro CheckIfRegistryKeyExists
Looks like you have a lot of cruft if as I thought the maintenance service installer is going to be silent.

>+Name "${MaintFullName}"
>+OutFile "maintenanceservice_installer.exe"
>+InstallDir "$PROGRAMFILES\${MaintFullName}"
>+
>+; Get installation folder from registry if available
>+InstallDirRegKey HKCU "Software\Mozilla\MaintenanceService" ""
>+
>+SetOverwrite on
>+
>+!define MaintUninstallKey \
>+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
>+
>+!ifdef HAVE_64BIT_OS
>+  InstallDir "$PROGRAMFILES64\${MaintFullName}\"
>+!else
>+  InstallDir "$PROGRAMFILES32\${MaintFullName}\"
>+!endif
>+ShowInstDetails nevershow
>+
>+################################################################################
>+# Modern User Interface - MUI
>+
>+!define MUI_ICON setup.ico
>+!define MUI_UNICON setup.ico
>+!define MUI_WELCOMEPAGE_TITLE_3LINES
>+!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
>+
>+;Interface Settings
>+!define MUI_ABORTWARNING
>+
>+; Uninstaller Pages
>+!define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome
>+!insertmacro MUI_UNPAGE_WELCOME
>+!insertmacro MUI_UNPAGE_CONFIRM
>+!insertmacro MUI_UNPAGE_INSTFILES
>+!insertmacro MUI_UNPAGE_FINISH
>+
>+################################################################################
>+# Language
>+
>+!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
>+!verbose push
>+!verbose 3
>+!include "overrideLocale.nsh"
>+!include "customLocale.nsh"
>+!verbose pop
>+
>+Function .onInit
>+  SetSilent silent
>+FunctionEnd
>+
>+Function un.onInit
>+  StrCpy $BrandFullNameDA "${MaintFullName}"
>+FunctionEnd
>+
>+Function un.preWelcome
>+  StrCpy $PageName "Welcome"
>+  ${If} ${FileExists} "$EXEDIR\core\distribution\modern-wizard.bmp"
>+    Delete "$PLUGINSDIR\modern-wizard.bmp"
>+    CopyFiles /SILENT "$EXEDIR\core\distribution\modern-wizard.bmp" \
>+              "$PLUGINSDIR\modern-wizard.bmp"
>+  ${EndIf}
>+FunctionEnd
>+
>+Section "MaintenanceService"
>+  AllowSkipFiles off
>+
>+  ${LogHeader} "Installing Main Files"
>+
>+  CreateDirectory $INSTDIR
>+  SetOutPath $INSTDIR
>+
>+  ; Stop the maintenance service so we can overwrite any
>+  ; binaries that it uses.
>+  ; 1 for wait for file release, and 30 second timeout
>+  ServicesHelper::Stop "MozillaMaintenance"
>+
>+  ; If we don't have maintenanceservice.exe already installed
>+  ; then keep that name.  If we do use maintenanceservice_tmp.exe
>+  ; which will auto install itself when you call it with the install parameter.
>+  StrCpy $TempMaintServiceName "maintenanceservice.exe"
>+  IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
>+    StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
>+  skipAlreadyExists:
>+
>+  ; We always write out a copy and then decide whether to install it or 
>+  ; not via calling its 'install' cmdline which works by version comparison.
>+  CopyFiles "$EXEDIR\nspr4.dll" "$INSTDIR"
>+  CopyFiles "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
>+
>+  ; Install the application updater service.
>+  ; If a service already exists, the command line parameter will stop the
>+  ; service and only install itself if it is newer than the already installed
>+  ; service.  If successful it will remove the old maintenanceservice.exe
>+  ; and replace it with maintenanceservice_tmp.exe.
>+  ClearErrors
>+  ${GetParameters} $0
>+  ${GetOptions} "$0" "/Upgrade" $0
>+  ${Unless} ${Errors}
>+    ; The upgrade cmdline is the same as install except
>+    ; It will fail if the service isn't already installed.
>+    nsExec::Exec '"$INSTDIR\$TempMaintServiceName" upgrade'
>+  ${Else}
>+    nsExec::Exec '"$INSTDIR\$TempMaintServiceName" install'
>+  ${EndIf}
>+
>+  ${GetLongPath} "$INSTDIR" $8
>+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayName" "${MaintFullName}"
>+  WriteRegStr HKLM "${MaintUninstallKey}" "UninstallString" \
>+                   '"$INSTDIR\uninstall.exe"'
>+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayIcon" "$INSTDIR\maintenanceservice.exe,0"
>+  WriteRegStr HKLM "${MaintUninstallKey}" "DisplayVersion" "${AppVersion}"
>+  WriteRegStr HKLM "${MaintUninstallKey}" "Publisher" "Mozilla"
>+  WriteRegStr HKLM "${MaintUninstallKey}" "Comments" \
>+                   "${BrandFullName} ${AppVersion} (${ARCH} ${AB_CD})"
>+  ${GetSize} "$8" "/S=0K" $R2 $R3 $R4
>+  WriteRegDWORD HKLM "${MaintUninstallKey}" "EstimatedSize" $R2 
>+
>+  WriteUninstaller "$INSTDIR\Uninstall.exe"
>+
>+  ; Write out that a maintenance service was attempted.
>+  ; We do this because on upgrades we will check this value and we only
>+  ; want to install once on the first upgrade to maintenance service.
>+  ; Since the Maintenance service can be installed either x86 or x64,
>+  ; always use the 64-bit registry for checking if an attempt was made.
>+  SetRegView 64
Add a comment that this has to be done at the end so the correct registry view is maintained during the installation

>+  WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Attempted" 1
>+
>+  # Make the update directory read/write for all users.
>+  # This is to avoid permission problems from multiple users doing updates.
>+  SetShellVarContext all
>+  CreateDirectory "$APPDATA\Mozilla\updates"
>+  AccessControl::GrantOnFile \
>+    "$APPDATA\Mozilla\updates" "(BU)" "FullAccess"
>+SectionEnd
>+
>+Section "Uninstall"
>+  ; Delete the service so that no updates will be attempted
>+  nsExec::Exec '"$INSTDIR\maintenanceservice.exe" uninstall'
>+
>+  Delete "$INSTDIR\maintenanceservice.exe"
>+  Delete "$INSTDIR\maintenanceservice_tmp.exe"
>+  Delete "$INSTDIR\nspr4.dll"
>+  Delete "$INSTDIR\Uninstall.exe"
>+  RMDir "$INSTDIR"
Perhaps /REBOOTOK?

>diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh
>--- a/browser/installer/windows/nsis/shared.nsh
>+++ b/browser/installer/windows/nsis/shared.nsh
>@@ -100,16 +101,33 @@
>   ${FixClassKeys}
>   ${SetUninstallKeys}
> 
>   ; Remove files that may be left behind by the application in the
>   ; VirtualStore directory.
>   ${CleanVirtualStore}
> 
>   ${RemoveDeprecatedFiles}
>+
>+  ; We check to see if the maintenance service install was already attempted.
>+  ; Since the Maintenance service can be installed either x86 or x64,
>+  ; always use the 64-bit registry for checking if an attempt was made.
>+  SetRegView 64
Add a comment that this has to be done at the end so the correct registry view is maintained during the installation

I suspect a is admin check is needed here

>+  ReadRegDWORD $5 HKLM "Software\Mozilla\MaintenanceService" "Attempted"
>+  ${If} $5 == ""
>+    ; An install of maintenance service was never attempted.
>+    ; We call ExecShell (which is ShellExecute) with the verb "runas"
>+    ; to ask for elevation if the user isn't already elevated.  If the user 
>+    ; is already elevated it will just launch the program.
>+    ExecShell "runas" "$INSTDIR\maintenanceservice_installer.exe"
>+  ${Else}
>+    ; The maintenance service is already installed.
>+    ; Do nothing, the maintenance service will launch the 
>+    ; maintenanceservice_installer.exe /Upgrade itself to do the self update. 
>+  ${EndIf}
Comment 365 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:56:20 PST
Comment on attachment 574803 [details] [diff] [review]
All maintenance service and related code. v2'.

The certificatecheck.cpp code looks good but I definitely want bsmith and / or imelven to review that as well if they haven't already.

One more look I think... I'm going to review the incremental change patch right now. Please include both in one final patch.
Comment 366 Brian R. Bondy [:bbondy] 2011-11-23 17:57:13 PST
> Make a note that we can't have a fallback certificate 
> in this instance here and in other places as appropriate.

We could add a second certificate info to $MaintCertKey\1 or any other subfolder name and it would work btw.
Comment 367 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 17:59:50 PST
I thought that was for the signed binary files.
Comment 368 Brian R. Bondy [:bbondy] 2011-11-23 18:04:15 PST
> I'm going to review the incremental change patch right now. Please include both in one final patch.

Awesome! Yup I'll include both in a merged view.

> One more look I think... I'm going to review the incremental 
> change patch right now. Please include both in one final patch.

Ian has done one pass on it already but one of them will review it again before we're ready to push.


> I suspect a is admin check is needed here

The maintenance service installer will always be run as admin and never as a limited user or non elevated user.

> I thought that was for the signed binary files.

I'm not sure if you can have more than one authenticode sign on an exe.  In any case it allows diff certificates currently but we can disable that to only allow 1 certificate if it matters.
Comment 369 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 18:07:22 PST
(In reply to Brian R. Bondy [:bbondy] from comment #368)
> > I suspect a is admin check is needed here
> 
> The maintenance service installer will always be run as admin and never as a
> limited user or non elevated user.
PostUpdate won't always run as admin though. Is it going to prompt to elevate in that case? I think I'm ok with that.

> 
> > I thought that was for the signed binary files.
> 
> I'm not sure if you can have more than one authenticode sign on an exe.  In
> any case it allows diff certificates currently but we can disable that to
> only allow 1 certificate if it matters.
No need to change it but I wanted the comment to call it out so anyone not familiar would know.
Comment 370 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 18:27:14 PST
Comment on attachment 576402 [details] [diff] [review]
Incremental changes on the big base patch. v2.

>diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js
>--- a/browser/components/preferences/advanced.js
>+++ b/browser/components/preferences/advanced.js
>@@ -507,16 +507,34 @@ var gAdvancedPane = {
>     // A locked pref is sufficient to disable the radiogroup.
>     radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
> 
>     var modePref = document.getElementById("app.update.mode");
>     var warnIncompatible = document.getElementById("warnIncompatible");
>     // the warnIncompatible checkbox value is set by readAddonWarn
>     warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
>                                 !enabledPref.value || !autoPref.value;
>+
>+    // Check to see if the maintenance service is installed.
>+    // If it is don't show the preference at all.
>+    var installed = 0;
Should be able to just use var installed;

>+    try {
>+      Components.utils.reportError("0");
?

>+      var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
>+                .createInstance(Components.interfaces.nsIWindowsRegKey);
>+      wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
>+               "SOFTWARE\\Mozilla\\MaintenanceService",
>+               wrk.ACCESS_READ | wrk.WOW64_64);
>+      installed = wrk.readIntValue("Installed");


>diff --git a/toolkit/components/maintenanceservice/workmonitor.cpp b/toolkit/components/maintenanceservice/workmonitor.cpp
>--- a/toolkit/components/maintenanceservice/workmonitor.cpp
>+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
>@@ -56,76 +56,97 @@
> 
> extern BOOL gServiceStopping;
> 
> // Wait 45 minutes for an update operation to run at most.
> // Updates usually take less than a minute so this seems like a 
> // significantly large and safe amount of time to wait.
> static const int TIME_TO_WAIT_ON_UPDATER = 45 * 60 * 1000;
> PRUnichar* MakeCommandLine(int argc, PRUnichar **argv);
>-BOOL WriteStatusPendingNoService(LPCWSTR updateDirPath);
>+BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
>+BOOL WriteStatusPending(LPCWSTR updateDirPath);
> BOOL StartCallbackApp(int argcTmp, LPWSTR *argvTmp, DWORD callbackSessionID);
> BOOL StartSelfUpdate(int argcTmp, LPWSTR *argvTmp);
>+void LaunchWinPostProcess(const WCHAR *appExe, HANDLE userToken = NULL);
>+BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,  LPCWSTR siblingFilePath, 
>+                            LPCWSTR newFileName);
>+
>+// Error codes is 16000 since Windows system error codes only go up to 15999.
nit: s/codes/code/

>+const int SERVICE_UPDATE_ERROR = 16000;

>...
>   // If we're about to start the update process from session 0 on Vista
>-  // or later, then we should not show a GUI.  One way to disable the
>-  // updater.exe GUI is to delete the updater.ini file.
>+  // or later, then we should not show a GUI.
>   if (UACHelper::IsVistaOrLater() && argcTmp >= 2 ) {
>+    // Setting the desktop to blank will ensure no GUI is displayed
>     si.lpDesktop = L"";
>-    WCHAR updaterINI[MAX_PATH + 1];
>-    wcscpy(updaterINI, argvTmp[1]);
>-    if (PathAppendSafe(updaterINI, L"updater.ini")) {
>-      DeleteFileW(updaterINI);
>-    }
>+    si.dwFlags |= STARTF_USESHOWWINDOW;
>+    si.wShowWindow = SW_HIDE;
mentioned in previous review
Comment 371 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-23 18:41:42 PST
Comment on attachment 576402 [details] [diff] [review]
Incremental changes on the big base patch. v2.

>diff --git a/toolkit/xre/nsWindowsRestart.cpp b/toolkit/xre/nsWindowsRestart.cpp
>--- a/toolkit/xre/nsWindowsRestart.cpp
>+++ b/toolkit/xre/nsWindowsRestart.cpp
>...
>@@ -552,8 +602,114 @@ WinLaunchChild(const PRUnichar *exePath,
>     if (lpMsgBuf)
>       LocalFree(lpMsgBuf);
>   }
> 
>   free(cl);
> 
>   return ok;
> }
>+
>+void
>+LaunchWinPostProcess(const WCHAR *appExe, HANDLE userToken = NULL)
Please add a javadoc comment.

I'd prefer to keep code that isn't used by the application out of application code. Is there a different file this could be added to?

Same goes for the set status code.
Comment 372 Axel Hecht [:Pike] 2011-11-23 22:54:56 PST
(In reply to Robert Strong [:rstrong] (do not email) from comment #360)
> No, it isn't. The steps are a tad convoluted and I tend to get them wrong a
> few times before I get them right so I'll just do this over the weekend
> unless Axel or someone provide the details. If no one does provide details
> I'll post the process I used in this bug after I get them right.

Here's the techie version of it:

Test with http://hg.mozilla.org/l10n-central/fr/.
pip install compare-locales
Add 
ac_add_options --with-l10n-base=full/path/to/parent/of/fr
to your mozconfig. 
reconfigure.
make package
cd browser/locales
make unpack
make merge-fr LOCALE_MERGEDIR=$PWD/merge
make installers-fr LOCALE_MERGEDIR=$PWD/merge

Install the french build and give it a quick test spin so that you know it's actually french. Given that the strings landed, your service installer should be in French now, too. (They're quick)
Comment 373 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-24 06:00:32 PST
(In reply to Ehsan Akhgari [:ehsan] from comment #359)
> Created attachment 576663 [details] [diff] [review] [diff] [details] [review]
> WIP - Adding tests using the maintenance service

So, here's a problem.  I pushed a try job yesterday with this patch, and I expected the test to fail on Windows, but it passed.  Upon closer investigation, it should have been obvious to me that in case that the service cannot be started, we just fall back to not using the service and launching updater.exe directly.  While that's definitely what we want, it hurts our ability to test that the service was really used.  I've looked through the service code again, but there doesn't seem to be any traces left from the execution of the service (except for the log, which we can't use since we won't have admin privileges required to set system environment variables for NSPR logging.  :(

Should I add some sort of a trace (for example, writing a file somewhere, etc) and check for it in the test to make sure that it was really the service which initiated the update process?  If yes, what do you suggest that trace to be?

Thanks!
Comment 374 Brian R. Bondy [:bbondy] 2011-11-24 06:20:36 PST
> expected the test to fail on Windows.
> Upon closer investigation, it should have been obvious 
> to me that in case that the service cannot be started, 
> we just fall back to not using the service and launching 
> updater.exe directly. 

That makes me feel better at least in general :)

> except for the log, which we can't use since we won't have admin 
> privileges required to set system environment variables for NSPR logging. 

I spoke to rs about this earlier (and as we discussed as well). I'll be removing NSPR logging in the next patch I submit (hopefully today). Instead I'll add normal logging.  I htink the best bet would be to add a function that reads in that log and searches for some finalized success code in the log after running ecah test.

> Should I add some sort of a trace (for example, writing a file 
> somewhere, etc) and check for it in the test to make sure that 
> it was really the service which initiated the update process? 

I think just using the log would be best and would automatically test that logging is working correctly.  And I'll handle that code to create the log.
In the test code you can just read in the entire log (should only be about 10 lines at any given time) and then search for the string "updater.exe was launched and run successfully" inside of it.  I'm open to suggestions on where to keep the log.  I was planning to put it to all users program data under Mozilla/logs, but let me know if you have a better suggestion.   I plan to keep at least the last 1 old log as well by the way, but perhaps up to the last 10 old logs.   We can make the newest log always have a consistent name though.
Comment 375 Brian R. Bondy [:bbondy] 2011-11-24 06:23:00 PST
Thanks for the info Pike.
Comment 376 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-24 07:28:46 PST
Using the log makes sense to me.  We put the current update.log file in the update root directory.  I don't have any strong preferences on where the log file from the service should live, as long as its location and name are consistent.
Comment 377 Brian R. Bondy [:bbondy] 2011-11-25 06:30:13 PST
Just wanted to give an update:
- Pretty much all review comments are implemented modulo a couple questions I sent to rs via email.
- I'm making a shared library right now to be used between udpater and maintenanceservice to avoid having duplicated code (for logging), and so that code only used by updater and maintenanceservice doesn't need to be in toolkit/xre/
- Pending still, I need to implement callback app being called by updater.exe instead of maintenanceservice for various reasons that ehsan pointed out.
Comment 378 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-25 09:13:20 PST
Created attachment 576955 [details] [diff] [review]
Automated tests (v1)

OK, this patch adds automated tests for all of the tests invoking the updater application using the service to run updater.exe.
Comment 379 Brian R. Bondy [:bbondy] 2011-11-27 21:08:53 PST
Created attachment 577191 [details] [diff] [review]
All maintenance service and related code. v3.

Review feedback and other things changed/fixed:

- Added code so Windows 2000 will not install the service nor show the option
- Since elevation type and IsUserAdmin were no longer used from UACHelper I removed it.   
  - It's very possible we'll use this for future maintenance service code, but if so I'll just regrab the code from an existing patch.
- Removed special handling from the installer for the all users application data work item folder, since there is an inherited write file attribute already set which is all that is needed.
  - Adding more permissions than we need can be problematic because limited user accounts can for example delete the folder.
- Added ifdef for Firefox so no other applications try to use the service that use the code.
- > +!define CompanyName "Mozilla Corporation" 
  > We need to think about how 3rd parties can use this.
  Do we need to worry about this initially, or is this just a comment to keep in mind for the future?
  If I can post a bug instead of handling now please let me know.
- > I suspect a is admin check is needed here
  Yup fixed.
- > I recall ERROR_SERVICE_MARKED_FOR_DELETE preventing the install of a service with 
  > the same name back in Win2K days. Have you / can you verify what happens in this case? 
  > Might be a good thing to verify on Win XP that the behavior is the same.
  The uninstall will work (eventually), if we are re-installing though that will fail. 
  I don't think there's anything we can do in that case though.  The user wouldn't have 
  a service temporarily.  I don't know of any case this will ever happen though since we always properly stop and delete.
- I lowercased the path before calculating the registry hash path to be safer.  Makes deployments for build servers easier.
- I now let updater.exe run its own callback application for various reasons that ehsan pointed out.  I still have to run it from the service in certain error conditions though.
- Added code inside updater.exe that checks for an env var set with the sessionID for the callback.  This can only be set on the system account env vars so is not a security risk.
- Added ifdef in toolkit/xre and in js so that service stuff isn't checked nor set initially for other products.
- CreateProcess had a static memory string used for cmdline which you shouldn't do that I meant to fix for a while, fixed now.
- Fixed bug with slogFile updater log path for refactored LaunchWinPostProcess function
- Remove structured exception handling code from CheckCertificateForPEFile since mingw doens't support it.
- Added logging to better identify if update files are left in directory (old bug)
- Removed icon and use uninstall icon instead. This saves 91KB on the installer and full MAR updates.
Comment 380 Brian R. Bondy [:bbondy] 2011-11-28 10:10:54 PST
Created attachment 577302 [details] [diff] [review]
All maintenance service and related code. v4.

- Removed signer info checks since that can be easily spoofed by anyone, and leaves more room for error from RelEng side for when signing binaries.  Now check just the name and the issuer.
- Fixed race condition when starting PostUpdate under session 0 and the callback app.  If both are async sometimes the HKCU one could be set first leading to 2 uninstall icons.
Comment 381 Brian R. Bondy [:bbondy] 2011-11-28 10:18:21 PST
Comment on attachment 577302 [details] [diff] [review]
All maintenance service and related code. v4.

I obsoleted old patch for security review from bsmith since this one is more up to date.

In case you can't review the entire patch, I think the most important files for security review are:
maintenanceservice_installer.nsi | registrycertificates.cpp | registrycertificates.h | uachelper.cpp | uachelper.h | workmonitor.cpp | workmonitor.h
Comment 382 Brian R. Bondy [:bbondy] 2011-11-29 10:46:12 PST
Created attachment 577670 [details] [diff] [review]
All maintenance service and related code. v5.

2 minor changes:
- Fixed Uninstall icon, it was a generic file icon since the maintenanceservice.exe icon was removed. Now it the Uninstall.exe icon.
- Fixed build problem with updatedefines.h and a different one on non-windows platforms.

If you already started reviewing the old patch that is fine, as the changes are very minor.
Comment 383 Ian Melven :imelven 2011-11-29 15:04:51 PST
Comment on attachment 577670 [details] [diff] [review]
All maintenance service and related code. v5.

Review of attachment 577670 [details] [diff] [review]:
-----------------------------------------------------------------

a general comment: we should use all the available OS protections for the new binaries (ASLR, DEP, etc.). The new executables
should pass a run of Microsoft's SDL binscope tool as Firefox itself does. 

overall looks good, with some nits and there's a couple of questions i would like clarification on around the post process
and the callback.

::: toolkit/components/maintenanceservice/certificatecheck.cpp
@@ +329,5 @@
> +    case TRUST_E_NOSIGNATURE:
> +      // The file was not signed or had a signature that was not valid.
> +      // Get the reason for no signature.
> +      if (TRUST_E_TIME_STAMP == dwLastError) {
> +        // The file was not signed.

this comment looks incorrect ?

::: toolkit/components/maintenanceservice/maintenanceservice.cpp
@@ +182,5 @@
> +  wcscpy(path, basePath);
> +  if (logNumber <= 0) {
> +    swprintf(logName, L"maintenanceservice.log");
> +  } else {
> +    swprintf(logName, L"maintenanceservice-%d.log", logNumber);

this is pretty picky, but %d can be 10 chars long (the check <= 0 means it cant have a - in front of it making it 11)
the rest of the hardcoded chars are 23 chars long, could be a possible off by one in the
pathological case - this can't happen though since we only keep 5 logs based on the #define above

::: toolkit/components/maintenanceservice/pathhash.cpp
@@ +141,5 @@
> +    filePathLen--;
> +  }
> +
> +  WCHAR *lowercasePath = new WCHAR[filePathLen + 1];
> +  wcscpy(lowercasePath, filePath);

doesn't this copy the slash we wanted to ignore above ?

::: toolkit/components/maintenanceservice/workmonitor.cpp
@@ +211,5 @@
> +      // to figure out where.
> +      // The directory containing the update information.
> +      LaunchWinPostProcess(callbackApplication, updateInfoDir, NULL);
> +      nsAutoHandle userToken(UACHelper::OpenUserToken(callbackSessionID));
> +      LaunchWinPostProcess(callbackApplication, updateInfoDir, userToken);

i'm a little confused here. it seems like the service will now always execute the callback app (instead of updater.exe launching the callback app)

why do we launch the post process twice, once elevated and once as the user ? i thought the post update process and the callback were the same thing ? this is probably
my misunderstanding. 

is there a case where updater.exe will launch the callback app ever ?

@@ +343,5 @@
> +  int argcTmp = 0;
> +  LPWSTR* argvTmp = CommandLineToArgvW(cmdlineBufferWide, &argcTmp);
> +
> +  // Check for callback application sign problems
> +  BOOL callbakSignProblem = FALSE;

nit: typo

@@ +414,5 @@
> +         sessionID, GetLastError()));
> +
> +    // When there is a certificate error we just want to write pending.
> +    // That is because a future update will probably fix the certificate
> +    // problem, and we don't want to update  app.update.service.failcount.

this won't be true for a malicious work item pointing to a bogus callback.. but we're
not planning to enable signed callbacks any time soon right ?

@@ +435,5 @@
> +    }
> +
> +    // On certificate check errors on updater.exe, updater.exe won't run at all
> +    // so we need to manually start the callback application ourselves.
> +    // This condition will only be hit when the callback app has no sign errors

except we don't check the callback app and don't plan to - maybe we should
consider not running the callback in this case ? 

i still have concerns about injecting a callback into another user's sessions and being able to specify
args, this must be researched during security testing of the feature IMO

@@ +548,5 @@
> +  wcscpy(maintserviceInstallerPath, argvTmp[2]);
> +  PathAppendSafe(maintserviceInstallerPath, 
> +                 L"maintenanceservice_installer.exe");
> +  WCHAR cmdLine[64];
> +  wcscpy(cmdLine, L"app.exe /Upgrade");

what's the hardcoded app.exe here ?

::: toolkit/mozapps/update/common/updatedefines.h
@@ +70,5 @@
> +# define snprintf(dest, count, fmt, ...) \
> +  PR_BEGIN_MACRO \
> +  int _count = count - 1; \
> +  _snprintf(dest, _count, fmt, ##__VA_ARGS__); \
> +  dest[_count] = '\0'; \

if dest is WCHAR this should be L'\0' to terminate with a 2 byte NULL i think ? 

making this a macro to ensure null termination is awesome, thank you.

::: toolkit/mozapps/update/nsUpdateService.js
@@ +611,5 @@
> +                          PREF_APP_UPDATE_SERVICE_FAILCOUNT, 0);
> +  var maxFail = getPref("getIntPref", 
> +                        PREF_APP_UPDATE_SERVICE_MAXFAIL, 
> +                        DEFAULT_MAX_FAIL_COUNT);
> +  return useService && failCount < maxFail;

do we ever reset the failCount ? or if it fails 10 times, we just never try the service ever again ?
Comment 384 Brian R. Bondy [:bbondy] 2011-11-29 18:19:38 PST
Created attachment 577824 [details] [diff] [review]
All maintenance service and related code. v6.

Thanks for the review Ian!
I implemented the comments and attached a new patch.
If you want to see only the changes you can see them here: 
http://hg.mozilla.org/projects/elm/rev/8f67123eaebe

> +  wcscpy(lowercasePath, filePath);
> doesn't this copy the slash we wanted to ignore above ?

It does but that's OK because we calculate the hash based only on filePathLen.
I updated with a comment to clarify this.



> i'm a little confused here. it seems like the service will now always execute the callback app (instead of updater.exe 
> launching the callback app)
> ...
> is there a case where updater.exe will launch the callback app ever ?

updater.exe will always execute the callback app itself because of various dependencies on these parameters that updater.exe needs.
The maintenance service will execute the callback app only if updater.exe couldn't be started and the callback app's signature is verified.
 


> why do we launch the post process twice, once elevated and once as the user ? 
> i thought the post update process and the callback were the same thing ? 
> this is probably my misunderstanding. 

We do this because some fields need to be set in HKLM so we run it as session 0.
Then we need to set some per user stuff so we launch it with the user token (unelevated)


> this won't be true for a malicious work item pointing to a bogus callback.. but we're
> not planning to enable signed callbacks any time soon right ?
> ...
> except we don't check the callback app and don't plan to - mayrsbe we should
> consider not running the callback in this case ? >
> ...
> i still have concerns about injecting a callback into another user's sessions and being able to specify
> args, this must be researched during security testing of the feature IMO

I'm not sure if RelEng will be done the automated signing by the time we would like to land this on Nightly or not.
It is my understanding that they are close.
But the plan is definitely to check the signature on the callback before executing it. 
So you can only execute a callback that is signed by us.
Please advise on if the callback sign check can be disabled for Nightly or not. 
I think previously the understanding was that it was OK on Nightly but not Aurora, but I could be wrong so please confirm.

If you want before we land we can disable the service starting the callback app if RelEng isn't ready.  
But I think it's just as much of a problem for updater.exe to execute the callback app.


> wcscpy(cmdLine, L"app.exe /Upgrade");
> what's the hardcoded app.exe here ?

Just a dummy parameter that is unused, I renamed it to dummyparam.exe



> if dest is WCHAR this should be L'\0' to terminate with a 2 byte NULL i think ? 
> making this a macro to ensure null termination is awesome, thank you.

That is just code moved from updater so I can't take credit.
I changed L'\0' as I think that's correct.



> do we ever reset the failCount ? or if it fails 10 times, 
> we just never try the service ever again ?

Ya we never reset it currently and if it reaches that it will never be used again.  
I think we may eventually reset it via add on update or disable it completely.
rs in Comment 325 asked that it be added, I think as a safety measure.
Comment 385 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-30 07:27:57 PST
Created attachment 577952 [details] [diff] [review]
Automated tests (v2)

This is a better version of the automated tests which uses the maintenanceservice.log file to verify that the service was indeed used in order to perform the update in the new tests.
Comment 386 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 13:37:24 PST
Couple of notes

The pref associated with using the service should be set to disable the service when the failCount reaches the threshold and failCount should reset after the pref associated with using the service is set to disable using the service.

I haven't been able to compile with pymake with this patch applied. I don't know if it is a pymake problem or due to the patch so I am building using pymake without the patch. Have you been using pymake?

I tried to repackage without success. I will also test repackaging without the patch at the same time I check if pymake is working.
Comment 387 Brian R. Bondy [:bbondy] 2011-11-30 13:44:14 PST
> The pref associated with using the service should be set to disable 
> the service when the failCount reaches the threshold 
> and failCount should reset after the pref associated with using the 
> service is set to disable using the service.

That sounds better, then someone can just turn it on via manually going to preferences again.  Thanks.  I'll make that change on the next patch.

> I haven't been able to compile with pymake with this patch applied.

I've only tried building with make -f client.mk (and incremental builds).  Also it passes try tests. 
I haven't tested the localization repackaging yet given in comment 372.
Comment 388 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 13:45:35 PST
I have used the following to repackage on Windows using an l10n dir alongside my source dir without compare-locales

Create an l10n dir alongside the source dir

For the French locale
cd into the l10n dir
hg clone http://hg.mozilla.org/l10n-central/fr/ fr

In my mozconfig:
mk_add_options MOZ_LOCALE_DIRS=@TOPSRCDIR@/../l10n
ac_add_options --with-l10n-base=../l10n

reconfigure

make package

cd browser/locales
make installers-fr
Comment 389 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 13:46:57 PST
(In reply to Brian R. Bondy [:bbondy] from comment #387)
> > The pref associated with using the service should be set to disable 
> > the service when the failCount reaches the threshold 
> > and failCount should reset after the pref associated with using the 
> > service is set to disable using the service.
> 
> That sounds better, then someone can just turn it on via manually going to
> preferences again.  Thanks.  I'll make that change on the next patch.
Also reset it if the service successfully updates and it has a value
Comment 390 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 13:52:59 PST
btw: iirc two or three versions previous to the current patch did compile with pymake... still compiling and will report back whether the current patch breaks pymake
Comment 391 Axel Hecht [:Pike] 2011-11-30 14:25:36 PST
(In reply to Robert Strong [:rstrong] (do not email) from comment #388)
> I have used the following to repackage on Windows using an l10n dir
> alongside my source dir without compare-locales
> 
> Create an l10n dir alongside the source dir
> 
> For the French locale
> cd into the l10n dir
> hg clone http://hg.mozilla.org/l10n-central/fr/ fr
> 
> In my mozconfig:
> mk_add_options MOZ_LOCALE_DIRS=@TOPSRCDIR@/../l10n
> ac_add_options --with-l10n-base=../l10n
> 
> reconfigure
> 
> make package
> 
> cd browser/locales
> make installers-fr

I'd use a full path to l10n base, and I miss the merge step here, and passing the full path to LOCALE_MERGEDIR in both merge-fr and installers-fr. Beyond that, if you can pastebin the exact error, I should be able to help.
Comment 392 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 14:33:25 PST
Using the steps from comment #388 (en-GB with missing files added) without the patch repackaging works.

Using the steps from comment #388 (en-GB with missing files added) with the patch I get the following:
updating: chrome.manifest (172 bytes security) (deflated 76%)
make[1]: Entering directory `/c/moz/_1_mozilla-central/ff-x86-opt/browser/locale
s'
make[1]: *** No rule to make target `/c/moz/_1_mozilla-central/ff-x86-opt/browse
r/locales/../../dist/install/sea/firefox-11.0a1.en-US.win32.installer.exe', need
ed by `repackage-win32-installer'.  Stop.
make[1]: Leaving directory `/c/moz/_1_mozilla-central/ff-x86-opt/browser/locales
'
make: *** [repackage-win32-installer-en-GB] Error 2
Comment 393 :Ehsan Akhgari (busy, don't ask for review please) 2011-11-30 15:09:38 PST
Created attachment 578107 [details] [diff] [review]
Automated tests (v3)

Added a bootstrapping test for getting the built version of the service in place, and also made a few robustness improvements to the test.
Comment 394 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 15:24:53 PST
building with pymake

processing ../../../../mozilla/toolkit/mozapps/extensions/jar.mn
make.py[4]: Leaving directory 'c:\moz\_1_mozilla-central\ff-x86-opt\toolkit\moz
pps/extensions'
c:\moz\_1_mozilla-central\mozilla\config\makefiles\target_libs.mk:54:0: command
'c:/moz/mozilla-build/python/python.exe c:/moz/_1_mozilla-central/mozilla/build
pymake/pymake/../make.py -C toolkit libs' failed, return code 2
c:\moz\_1_mozilla-central\mozilla\config\rules.mk:744:0: command 'c:/moz/mozill
-build/python/python.exe c:/moz/_1_mozilla-central/mozilla/build/pymake/pymake/
./make.py libs_tier_platform' failed, return code 2
c:\moz\_1_mozilla-central\mozilla\config\rules.mk:709:0: command 'c:/moz/mozill
-build/python/python.exe c:/moz/_1_mozilla-central/mozilla/build/pymake/pymake/
./make.py  tier_platform' failed, return code 2
Comment 395 Ian Melven :imelven 2011-11-30 16:11:35 PST
(In reply to Brian R. Bondy [:bbondy] from comment #384)

> updater.exe will always execute the callback app itself because of various
> dependencies on these parameters that updater.exe needs.
> The maintenance service will execute the callback app only if updater.exe
> couldn't be started and the callback app's signature is verified.

ok, thanks for the clarification.

> We do this because some fields need to be set in HKLM so we run it as
> session 0.
> Then we need to set some per user stuff so we launch it with the user token
> (unelevated)

are these the same executable ? i assume it just looks at its session id to work out which things to do if so. is this different at all from the update process prior to the service work ? 
 
> I'm not sure if RelEng will be done the automated signing by the time we
> would like to land this on Nightly or not.
> It is my understanding that they are close.
> But the plan is definitely to check the signature on the callback before
> executing it. 
> So you can only execute a callback that is signed by us.
> Please advise on if the callback sign check can be disabled for Nightly or
> not. 
> I think previously the understanding was that it was OK on Nightly but not
> Aurora, but I could be wrong so please confirm.

that's my understanding of the discussion previously too - nightly is ok, other channels are not

> If you want before we land we can disable the service starting the callback
> app if RelEng isn't ready.  
> But I think it's just as much of a problem for updater.exe to execute the
> callback app.

yeah, they're both elevated so it seems equivalent
Comment 396 Brian R. Bondy [:bbondy] 2011-11-30 16:48:31 PST
> are these the same executable ? i assume it just looks at 
> its session id to work out which things to do if so. 
> is this different at all from the update process prior to the service work ? 

The code branches off on if you have access or not. The HKCU is executed secondly and has special handling to not add an uninstall icon if it is already added.  rs and I discussed running 2 post processes previously on a phone call.  I'm not sure if that'll cause problems with other apps or not, so we might be changing this eventually.  For now the service is only ifdef'ed to work with Firefox though.  I'd like to eventually have 2 different callback parameters for people who want to use the service but that'll probaly be in another bug ID.
Comment 397 Robert Strong [:rstrong] (use needinfo to contact me) 2011-11-30 19:32:31 PST
(In reply to Robert Strong [:rstrong] (do not email) from comment #392)
> Using the steps from comment #388 (en-GB with missing files added) without
> the patch repackaging works.
> 
> Using the steps from comment #388 (en-GB with missing files added) with the
> patch I get the following:
> updating: chrome.manifest (172 bytes security) (deflated 76%)
> make[1]: Entering directory
> `/c/moz/_1_mozilla-central/ff-x86-opt/browser/locale
> s'
> make[1]: *** No rule to make target
> `/c/moz/_1_mozilla-central/ff-x86-opt/browse
> r/locales/../../dist/install/sea/firefox-11.0a1.en-US.win32.installer.exe',
> need
> ed by `repackage-win32-installer'.  Stop.
> make[1]: Leaving directory
> `/c/moz/_1_mozilla-central/ff-x86-opt/browser/locales
> '
> make: *** [repackage-win32-installer-en-GB] Error 2
I repackaged using the fixed pymake build and it worked. Not sure what caused the initial failures.
Comment 398 Robert Strong [:rstrong] (use needinfo to contact me) 2011-12-01 02:00:07 PST
>diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
>--- a/browser/base/content/aboutDialog.js
>+++ b/browser/base/content/aboutDialog.js
>@@ -180,18 +180,22 @@ function appUpdater()
> appUpdater.prototype =
> {
>   // true when there is an update check in progress.
>   isChecking: false,
> 
>   // true when there is an update already staged / ready to be applied.
>   get isPending() {
>     if (this.update)
>-      return this.update.state == "pending";
>-    return this.um.activeUpdate && this.um.activeUpdate.state == "pending";
>+      return this.update.state == "pending" || 
>+             this.update.state == "pending-service";
nit: add braces since it is on two lines now.

>diff --git a/browser/components/preferences/advanced.js b/browser/components/preferences/advanced.js
>--- a/browser/components/preferences/advanced.js
>+++ b/browser/components/preferences/advanced.js
>@@ -507,16 +507,34 @@ var gAdvancedPane = {
>     // A locked pref is sufficient to disable the radiogroup.
>     radiogroup.disabled = !canCheck || enabledPref.locked || autoPref.locked;
> 
>     var modePref = document.getElementById("app.update.mode");
>     var warnIncompatible = document.getElementById("warnIncompatible");
>     // the warnIncompatible checkbox value is set by readAddonWarn
>     warnIncompatible.disabled = radiogroup.disabled || modePref.locked ||
>                                 !enabledPref.value || !autoPref.value;
>+
>+    // Check to see if the maintenance service is installed.
>+    // If it is don't show the preference at all.
>+    var installed;
>+    try {
>+      Components.utils.reportError("0");
>+      var wrk = Components.classes["@mozilla.org/windows-registry-key;1"]
>+                .createInstance(Components.interfaces.nsIWindowsRegKey);
>+      wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE,
>+               "SOFTWARE\\Mozilla\\MaintenanceService",
>+               wrk.ACCESS_READ | wrk.WOW64_64);
>+      installed = wrk.readIntValue("Installed");
>+      wrk.close();
>+    } catch(e) {
>+    }
>+    if (!installed) {
So, any value that evaluates to not true will hide the control. Might be better to check for the explicit value.

>+      document.getElementById("useService").hidden = true;
>+    }
>   },
>diff --git a/toolkit/Makefile.in b/toolkit/Makefile.in
>--- a/toolkit/Makefile.in
>+++ b/toolkit/Makefile.in
>@@ -41,27 +41,27 @@
> DEPTH     = ..
> topsrcdir = @top_srcdir@
> srcdir    = @srcdir@
> VPATH     = @srcdir@
> 
> include $(DEPTH)/config/autoconf.mk
> 
> PARALLEL_DIRS = \
>-  components \
>   content \
>   locales \
>   mozapps/downloads \
>   mozapps/extensions \
>   mozapps/handling \
>   mozapps/preferences \
>   mozapps/plugins \
>   mozapps/shared \
>   mozapps/update \
>   mozapps/webapps \
>+  components \
If this change isn't necessary just leave it like it was

>   obsolete \
>   profile \
>   themes \
>   $(NULL)
> 
> ifneq (,$(filter gtk2 qt,$(MOZ_WIDGET_TOOLKIT)))
> PARALLEL_DIRS += system/unixproxy
> endif
>diff --git a/toolkit/components/Makefile.in b/toolkit/components/Makefile.in
>--- a/toolkit/components/Makefile.in
>+++ b/toolkit/components/Makefile.in
>@@ -72,16 +72,20 @@ PARALLEL_DIRS += \
>   statusfilter \
>   telemetry \
>   typeaheadfind \
>   urlformatter \
>   viewconfig \
>   viewsource \
>   $(NULL)
> 
>+ifeq ($(OS_ARCH),WINNT)
>+PARALLEL_DIRS += maintenanceservice
>+endif
This being parallel along with toolkit/mozapps/update/common appears to be what is breaking pymake.
Comment 399 Robert Strong [:rstrong] (use needinfo to contact me) 2011-12-01 02:19:39 PST
>diff --git a/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.rc b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.rc
>new file mode 100644
>--- /dev/null
>+++ b/other-licenses/nsis/Contrib/ServicesHelper/ServicesHelper.rc
>@@ -0,0 +1,99 @@
>...
>+BEGIN
>+    BLOCK "StringFileInfo"
>+    BEGIN
>+        BLOCK "040904b0"
>+        BEGIN
>+            VALUE "FileDescription", "NSIS Plug-in for managing Windows services"
>+            VALUE "FileVersion", "1, 0, 0, 0"
>+            VALUE "InternalName", "ServicesHelper"
>+            VALUE "LegalCopyright", "Original code Copyright (c) 2011 Mozilla Corporation"
I think we want "License: MPL 1.1/GPL 2.0/LGPL 2.1"

Please double check with Gerv.
Comment 400 Brian R. Bondy [:bbondy] 2011-12-01 03:05:38 PST
Created attachment 578218 [details] [diff] [review]
File info / licensing info rc file

Please see Comment 399 regarding the file info properties in the .rc file.
Comment 401 Brian R. Bondy [:bbondy] 2011-12-01 03:25:42 PST
Created attachment 578222 [details] [diff] [review]
Alternate file info / licensing info rc file as per :rs' suggestion
Comment 402 Brian R. Bondy [:bbondy] 2011-12-01 04:23:45 PST
Created attachment 578225 [details] [diff] [review]
All maintenance service and related code. v7.

Implemented review comments from rs.
Also built successfully with pymake.

> include $(DEPTH)/config/autoconf.mk
> ... move of components ...
> If this change isn't necessary just leave it like it was

I was getting a build error with make -f client.mk without changing this.
Comment 403 Gervase Markham [:gerv] 2011-12-01 04:29:00 PST
Comment on attachment 578222 [details] [diff] [review]
Alternate file info / licensing info rc file as per :rs' suggestion

Go with this one, but with + signs:

VALUE "LegalCopyright", "MPL 1.1+/GPL 2.0+/LGPL 2.1+"

Gerv
Comment 404 Brian R. Bondy [:bbondy] 2011-12-01 04:42:18 PST
Created attachment 578230 [details] [diff] [review]
All maintenance service and related code. v7'.

Updated license info on ServicesHelper.rc as per gerv's comment.
Comment 405 Brian R. Bondy [:bbondy] 2011-12-01 06:24:19 PST
In addition to the existing tests that ehsan ported over, I'd like to also test as many of these as is possible:

- Test that the service can be started from the current unelevated user
- Test that the service can be stopped from the current unelevated user
- Test that installed reg value is present @ HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\MaintenanceService and has value of 1
- Test that attempted reg value is present @ HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\MaintenanceService and has value of 1
- Test an invalid mar file
- Mar file is missing
- Invalid work item file
- Work item file locked
- Work item file doens't specify enough command line args (< 2)
- Should we be checking specific error codes?
- Cert check failure on updater
- Cert check failure on callback
- Cert check failure on helper.exe
- Cert check failure specifically that timestamp is expired
- Cert check failure specifically that it is not trusted
- Cert check failure specifically that it is trusted but the name and issuer do not match
- Mar sign checks:
 - Mar sign verify failure 
 - Signed mar verify success
- Disabled pref when update is available should write out pending not pending-svc
- Test max fail count should auto-set disabled pref and reset max fail count
- updater.exe can't be started, callback should be started
- Maybe make callback app with dummy args return a failurecode if run in session 0 on Vista or higher.
- Ensure updater is run as session 0 always if run through the service.
- The following tests maybe have a special command line arg to emulate the action but not actually do since they require admin?  Then it would write log?
 - Test that service gets updated to a newer version if you call maintenanceservice_tmp.exe update where maintenanceservice_tmp.exe is newer
 - Test that service gets updated to a newer version if you call maintenanceservice_tmp.exe install where maintenanceservice_tmp.exe is newer
 - Test that service does not get updated to an older version if you call maintenanceservice_tmp.exe update where maintenanceservice_tmp.exe is older
 - Test that service does not get updated to an older version if you call maintenanceservice_tmp.exe install where maintenanceservice_tmp.exe is older
 - Test that service gets removed if you call maintenanceservice_tmp.exe uninstall
 - Test that service gets installed if it doesn't exist if you call maintenanceservice_tmp.exe install
 - Test that service does not get installed if it doesn't exist and you call maintenanceservice_tmp.exe install
- Test to make sure uninstall screen has only 1 uninstall icon and not 2 (should exist only in HKCU or HKLM but not both), I don't think we can test this because we don't actually use the installer in tests right?
- Verify exactly 5 backup logs are kept if you do 6 updates.
Comment 406 Brian R. Bondy [:bbondy] 2011-12-01 06:41:15 PST
Comment on attachment 578107 [details] [diff] [review]
Automated tests (v3)

Review of attachment 578107 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good so far. 

I think there was a copy error