The "rfb_win32" library merged with VNC 4.1.1 code.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@523 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
index 1a24485..7562d29 100644
--- a/rfb_win32/CurrentUser.cxx
+++ b/rfb_win32/CurrentUser.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -18,48 +18,98 @@
 
 // -=- Currentuser.cxx
 
-#include <windows.h>
-#include <lmcons.h>
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
 #include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb_win32/Service.h>
 #include <rfb_win32/OSVersion.h>
+#include <lmcons.h>
 
 using namespace rfb;
 using namespace win32;
 
+static LogWriter vlog("CurrentUser");
 
-CurrentUserToken::CurrentUserToken() : isValid_(false) {
-  // - If the OS doesn't support security, ignore it
-  if (!isServiceProcess()) {
-    // - Running in User-Mode - just get our current token
-    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
-      DWORD err = GetLastError();
-      if (err != ERROR_CALL_NOT_IMPLEMENTED)
-       throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+
+const TCHAR* shellIconClass = _T("Shell_TrayWnd");
+
+BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) {
+  TCHAR className[16];
+  if (GetClassName(hwnd, className, sizeof(className)) &&
+      (_tcscmp(className, shellIconClass) == 0)) {
+    vlog.debug("located tray icon window (%s)", (const char*)CStr(className));
+    DWORD processId = 0;
+    GetWindowThreadProcessId(hwnd, &processId);
+    if (!processId)
+      return TRUE;
+    Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+    if (!process.h)
+      return TRUE;
+    if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam))
+      return TRUE;
+    vlog.debug("obtained user token");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) {
+  HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);
+  vlog.debug("opening \"%s\"", lpszDesktop);
+  if (!desktop) {
+    vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop));
+    return TRUE;
+  }
+  BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam);
+  if (!CloseDesktop(desktop))
+    vlog.info("unable to close desktop: %ld", GetLastError());
+  return result;
+}
+
+
+CurrentUserToken::CurrentUserToken() : isSafe_(false) {
+  if (isServiceProcess()) {
+    // If the platform is Windows 95/98/Me then we must fake the token's presence
+    if (osVersion.isPlatformWindows) {
+      try {
+        UserName un;
+        h = INVALID_HANDLE_VALUE;
+      } catch (rdr::SystemException& e) {
+        if (e.err != ERROR_NOT_LOGGED_ON)
+          throw;
+        if (FindWindow(shellIconClass, 0))
+          h = INVALID_HANDLE_VALUE;
+      }
+      isSafe_ = (h != 0);
+      return;
     }
-    isValid_ = true;
-  } else {
-    // - Under XP/2003 and above, we can just ask the operating system
+
+    // Try to get the user token using the Terminal Services APIs
+    //   NB: This will only work under XP/2003 and later
     typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
     DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
     if (_WTSQueryUserToken.isValid()) {
       (*_WTSQueryUserToken)(-1, &h);
-      isValid_ = true;
-    } else {
-      // - Under NT/2K we have to resort to a nasty hack...
-      HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
-      if (!tray)
-        return;
-      DWORD processId = 0;
-      GetWindowThreadProcessId(tray, &processId);
-      if (!processId)
-        return;
-      Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
-      if (!process.h)
-        return;
-      OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
-      isValid_ = true;
+      isSafe_ = true;
+      return;
     }
+
+    // Try to find the Shell Tray Icon window and take its token
+    //   NB: This will only work under NT/2K (and later, but they're dealt with above)
+    //   NB: If the shell is not running then this will return an Unsafe Null token.
+    EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h);
+    isSafe_ = (h != 0);
+  } else {
+    // Try to open the security token for the User-Mode process
+    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+      DWORD err = GetLastError();
+      if (err != ERROR_CALL_NOT_IMPLEMENTED)
+        throw rdr::SystemException("OpenProcessToken failed", err);
+      // Under Windows 95/98/Me, we fake the handle value...
+      h = INVALID_HANDLE_VALUE;
+    }
+    isSafe_ = true;
   }
 }
 
@@ -68,8 +118,8 @@
   RegCloseKey(HKEY_CURRENT_USER);
   if (!isServiceProcess())
     return;
-  if (!token.isValid())
-    throw rdr::Exception("CurrentUserToken is not valid");
+  if (!token.canImpersonate())
+    throw rdr::Exception("Cannot impersonate unsafe or null token");
   if (!ImpersonateLoggedOnUser(token)) {
     DWORD err = GetLastError();
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
@@ -83,6 +133,7 @@
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
       exit(err);
   }
+  RegCloseKey(HKEY_CURRENT_USER);
 }
 
 
@@ -91,3 +142,11 @@
   if (!GetUserName(buf, &len))
     throw rdr::SystemException("GetUserName failed", GetLastError());
 }
+
+
+UserSID::UserSID() {
+  CurrentUserToken token;
+  if (!token.canImpersonate())
+    return;
+  setSID(Sid::FromToken(token.h));
+}