Back to index

lightning-sunbird  0.9+nobinonly
nsWindowsRestart.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla XULRunner bootstrap.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Benjamin Smedberg <benjamin@smedbergs.us>.
00018  *
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Mozilla Foundation. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 // This file is not build directly. Instead, it is included in multiple
00039 // shared objects.
00040 
00041 #ifdef nsWindowsRestart_cpp
00042 #error "nsWindowsRestart.cpp is not a header file, and must only be included once."
00043 #else
00044 #define nsWindowsRestart_cpp
00045 #endif
00046 
00047 #include <shellapi.h>
00048 
00049 #ifndef ERROR_ELEVATION_REQUIRED
00050 #define ERROR_ELEVATION_REQUIRED 740L
00051 #endif
00052 
00053 BOOL (WINAPI *pCreateProcessWithTokenW)(HANDLE,
00054                                         DWORD,
00055                                         LPCWSTR,
00056                                         LPWSTR,
00057                                         DWORD,
00058                                         LPVOID,
00059                                         LPCWSTR,
00060                                         LPSTARTUPINFOW,
00061                                         LPPROCESS_INFORMATION);
00062 
00063 BOOL (WINAPI *pIsUserAnAdmin)(VOID);
00064 
00065 BOOL (WINAPI *pDuplicateTokenEx)(HANDLE,
00066                                  DWORD,
00067                                  LPSECURITY_ATTRIBUTES,
00068                                  SECURITY_IMPERSONATION_LEVEL,
00069                                  TOKEN_TYPE,
00070                                  PHANDLE);
00071 
00075 static int QuotedStrLen(const char *s)
00076 {
00077   int i = 2; // initial and final quote
00078   while (*s) {
00079     if (*s == '"') {
00080       ++i;
00081     }
00082 
00083     ++i; ++s;
00084   }
00085   return i;
00086 }
00087 
00095 static char* QuoteString(char *d, const char *s)
00096 {
00097   *d = '"';
00098   ++d;
00099 
00100   while (*s) {
00101     *d = *s;
00102     if (*s == '"') {
00103       ++d;
00104       *d = '"';
00105     }
00106 
00107     ++d; ++s;
00108   }
00109 
00110   *d = '"';
00111   ++d;
00112 
00113   return d;
00114 }
00115 
00120 static char*
00121 MakeCommandLine(int argc, char **argv)
00122 {
00123   int i;
00124   int len = 1; // null-termination
00125 
00126   for (i = 0; i < argc; ++i)
00127     len += QuotedStrLen(argv[i]) + 1;
00128 
00129   char *s = (char*) malloc(len);
00130   if (!s)
00131     return NULL;
00132 
00133   char *c = s;
00134   for (i = 0; i < argc; ++i) {
00135     c = QuoteString(c, argv[i]);
00136     *c = ' ';
00137     ++c;
00138   }
00139 
00140   *c = '\0';
00141 
00142   return s;
00143 }
00144 
00148 static PRUnichar *
00149 AllocConvertAToW(const char *buf)
00150 {
00151   PRUint32 inputLen = strlen(buf) + 1;
00152   int n = MultiByteToWideChar(CP_ACP, 0, buf, inputLen, NULL, 0);
00153   if (n <= 0)
00154     return NULL;
00155   PRUnichar *result = (PRUnichar *)malloc(n * sizeof(PRUnichar));
00156   if (!result)
00157     return NULL;
00158   MultiByteToWideChar(CP_ACP, 0, buf, inputLen, result, n);
00159   return result;
00160 }
00161 
00165 static BOOL
00166 LaunchAsNormalUser(const char *exePath, char *cl)
00167 {
00168   if (!pCreateProcessWithTokenW) {
00169     // IsUserAnAdmin is not present on Win9x and not exported by name on Win2k
00170     *(FARPROC *)&pIsUserAnAdmin =
00171         GetProcAddress(GetModuleHandle("shell32.dll"), "IsUserAnAdmin");
00172 
00173     // CreateProcessWithTokenW is not present on WinXP or earlier
00174     *(FARPROC *)&pCreateProcessWithTokenW =
00175         GetProcAddress(GetModuleHandle("advapi32.dll"),
00176                        "CreateProcessWithTokenW");
00177 
00178     if (!pCreateProcessWithTokenW)
00179       return FALSE;
00180 
00181     // DuplicateTokenEx is not present on WinME and Win9x.
00182     *(FARPROC *)&pDuplicateTokenEx =
00183         GetProcAddress(GetModuleHandle("advapi32.dll"), "DuplicateTokenEx");
00184 
00185     if (!pDuplicateTokenEx)
00186       return FALSE;
00187   }
00188 
00189   // do nothing here if we are not elevated or IsUserAnAdmin is not present.
00190   if (!pIsUserAnAdmin || pIsUserAnAdmin && !pIsUserAnAdmin())
00191     return FALSE;
00192 
00193   // borrow the shell token to drop the privilege
00194   HWND hwndShell = FindWindow("Progman", NULL);
00195   DWORD dwProcessId;
00196   GetWindowThreadProcessId(hwndShell, &dwProcessId);
00197 
00198   HANDLE hProcessShell = OpenProcess(MAXIMUM_ALLOWED, FALSE, dwProcessId);
00199   if (!hProcessShell)
00200     return FALSE;
00201 
00202   HANDLE hTokenShell;
00203   BOOL ok = OpenProcessToken(hProcessShell, MAXIMUM_ALLOWED, &hTokenShell);
00204   CloseHandle(hProcessShell);
00205   if (!ok)
00206     return FALSE;
00207 
00208   HANDLE hNewToken;
00209   ok = pDuplicateTokenEx(hTokenShell,
00210                          MAXIMUM_ALLOWED,
00211                          NULL,
00212                          SecurityDelegation,
00213                          TokenPrimary,
00214                          &hNewToken);
00215   CloseHandle(hTokenShell);
00216   if (!ok)
00217     return FALSE;
00218 
00219   STARTUPINFOW si = {sizeof(si), 0};
00220   PROCESS_INFORMATION pi = {0};
00221 
00222   PRUnichar *exePathW = AllocConvertAToW(exePath);
00223   PRUnichar *clW = AllocConvertAToW(cl);
00224   ok = exePathW && clW;
00225   if (ok) {
00226     ok = pCreateProcessWithTokenW(hNewToken,
00227                                   0,    // profile is already loaded
00228                                   exePathW,
00229                                   clW,
00230                                   0,    // No special process creation flags
00231                                   NULL, // inherit my environment
00232                                   NULL, // use my current directory
00233                                   &si,
00234                                   &pi);
00235   }
00236   free(exePathW);
00237   free(clW);
00238   CloseHandle(hNewToken);
00239   if (!ok)
00240     return FALSE;
00241 
00242   CloseHandle(pi.hProcess);
00243   CloseHandle(pi.hThread);
00244 
00245   return TRUE;
00246 }
00247 
00253 BOOL
00254 WinLaunchChild(const char *exePath, int argc, char **argv, int needElevation)
00255 {
00256   char *cl;
00257   BOOL ok;
00258   if (needElevation > 0) {
00259     cl = MakeCommandLine(argc - 1, argv + 1);
00260     if (!cl)
00261       return FALSE;
00262     ok = ShellExecute(NULL, // no special UI window
00263                       NULL, // use default verb
00264                       exePath,
00265                       cl,
00266                       NULL, // use my current directory
00267                       SW_SHOWDEFAULT) > (HINSTANCE)32;
00268     free(cl);
00269     return ok;
00270   }
00271 
00272   cl = MakeCommandLine(argc, argv);
00273   if (!cl)
00274     return FALSE;
00275 
00276   if (needElevation < 0) {
00277     // try to launch as a normal user first
00278     ok = LaunchAsNormalUser(exePath, cl);
00279     // if it fails, fallback to normal launching
00280     if (!ok)
00281       needElevation = 0;
00282   }
00283   if (needElevation == 0) {
00284     STARTUPINFO si = {sizeof(si), 0};
00285     PROCESS_INFORMATION pi = {0};
00286 
00287     ok = CreateProcess(exePath,
00288                        cl,
00289                        NULL,  // no special security attributes
00290                        NULL,  // no special thread attributes
00291                        FALSE, // don't inherit filehandles
00292                        0,     // No special process creation flags
00293                        NULL,  // inherit my environment
00294                        NULL,  // use my current directory
00295                        &si,
00296                        &pi);
00297 
00298     if (ok) {
00299       CloseHandle(pi.hProcess);
00300       CloseHandle(pi.hThread);
00301     }
00302   }
00303 
00304   free(cl);
00305 
00306   return ok;
00307 }