Tuesday, April 24, 2007

Google Surpasses Microsoft as the World's Most Valuable Brands

Google has surpassed Microsoft as the world's top-ranked brand, with General Electric, Coca-Cola, and others following. Factors like corporate responsibility and expansion into emerging markets contributed to brand recognition. China Mobile topped the list for non-U.S. brands. The rankings were based on financial data and interviews with a million consumers. Google's rise highlights its word-of-mouth promotion approach, while Microsoft slid despite a massive marketing campaign for Windows Vista. Apple and Starbucks saw significant improvements, while Intel, Home Depot, and Dell declined in the brand rankings. A Reuters article is available here.


Tuesday, April 17, 2007

Call to WTSQueryUserToken() gives ERROR_PRIVILEGE_NOT_HELD in Windows Vista

2007-04-17 Here I will describe an error called ERROR_PRIVILEGE_NOT_HELD that I faced from a call to WTSQueryUserToken() using Visual C++ on Windows Vista and Windows XP SP2.

I have used the WTSQueryUserToken() function to obtain the primary access token of the logged-on user whose session-id I had obtained from a call to WTSGetActiveConsoleSessionId().

Now, here is my problem. After the call to WTSQueryUserToken() from inside a DLL that is running under the Print Spooler service, GetLastError() returns error number 1314 which means: A required privilege is not held by the client. I am logged in to Windows using an administrator account. The Notepad.exe application that is invoked at the end of the code also doesn't start.

In MSDN I see that what I'm getting is the error ERROR_PRIVILEGE_NOT_HELD which means: The caller does not have the SE_TCB_NAME privilege. How should I go about getting that privilege now?

Print Spooler (SPOOLSV.EXE) runs under the SYSTEM username account, i.e., the LocalSystem account. A quick look up in the Windows Task Manager shows that the Image Name SPOOLSV.EXE is running under the SYSTEM username.

My DLL is loaded by SPOOLSV.EXE under its own context. My intent is to start the Notepad.exe application under the context of the user who is currently logged in to Windows.

Note: As an aside, note that the SPOOLSV.EXE runs from location C:\WINDOWS\system32\spoolsv.exe. It is the Windows Print Spooler service that loads files to memory for later printing.

Here is my source code snippet:

static STARTUPINFO si;
static PROCESS_INFORMATION pi;
HANDLE hTokenNew = NULL, hTokenDup = NULL;

DWORD dwSessionId = WTSGetActiveConsoleSessionId();

WTSQueryUserToken(dwSessionId, &hTokenNew);

DuplicateTokenEx(hTokenNew, MAXIMUM_ALLOWED, NULL,
                 SecurityIdentification, TokenPrimary,
&hTokenDup);

ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = _T("winsta0\\default");

LPVOID  pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

CreateEnvironmentBlock(&pEnv, hTokenDup, FALSE));

dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;

pEnv = NULL;

ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

CreateProcessAsUser(hTokenDup, NULL, _T("c:\\windows\\notepad.exe"),
                    NULL, NULL, FALSE, dwCreationFlag,
                    pEnv, NULL, &si, &pi);

CloseHandle(hTokenDup);


Update 2007-04-25: So, this is how I solved it. There was no need for the WTSGetActiveConsoleSessionId() and WTSQueryUserToken(). Just the CreateEnvironmentBlock() must work properly so that the environment for the process you are going to create is set correctly.

Here is the source that worked for me. The code snippet below shows the Notepad application being launched. The DLL in which I used this code runs under the Print Spooler service - basically it is a Print Monitor. You need to add your own validity checks - what's listed below is bare-bones.

#include "userenv.h"
// Global Typedefs for function pointers in USERENV.DLL
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNCREATEENVIRONMENTBLOCK)
             (LPVOID  *lpEnvironment,
              HANDLE  hToken,
              BOOL    bInherit);
typedef BOOL (STDMETHODCALLTYPE FAR * LPFNDESTROYENVIRONMENTBLOCK)
             (LPVOID lpEnvironment);

void InvokeApp()
{
    // Local Variable Declarations
    HANDLE hToken    = NULL;
    HANDLE hTokenDup = NULL;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
   
    si.cb = sizeof(STARTUPINFO);
    si.lpDesktop = _T("Winsta0\\Default");
   
    DWORD  dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
    LPVOID pEnvironment = NULL;
    LPFNCREATEENVIRONMENTBLOCK lpfnCreateEnvironmentBlock = NULL;
    LPFNDESTROYENVIRONMENTBLOCK lpfnDestroyEnvironmentBlock = NULL;
    HMODULE hUserEnvLib = NULL;
    hUserEnvLib = LoadLibrary(_T("userenv.dll"));
    if ( NULL != hUserEnvLib ) {
        lpfnCreateEnvironmentBlock = (LPFNCREATEENVIRONMENTBLOCK)
        GetProcAddress(hUserEnvLib, "CreateEnvironmentBlock");
       
        lpfnDestroyEnvironmentBlock = (LPFNDESTROYENVIRONMENTBLOCK)
        GetProcAddress(hUserEnvLib, "DestroyEnvironmentBlock");
    }

    OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE, TRUE, &hToken);
    DuplicateTokenEx(hToken,
                     TOKEN_IMPERSONATE|TOKEN_READ|
                     TOKEN_ASSIGN_PRIMARY|TOKEN_DUPLICATE,
                     NULL,
                     SecurityImpersonation,
                     TokenPrimary,
                     &hTokenDup);
    RevertToSelf();
    CloseHandle(hToken);

    if (NULL != lpfnCreateEnvironmentBlock)
{
        if (lpfnCreateEnvironmentBlock(&pEnvironment, hTokenDup, FALSE))
{
            dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT; // must specify
        }
        else
{
            pEnvironment = NULL;
            OutputDebugString(_T("CreateEnvironmentBlock() -- FAILED"));
        }
    }
    else
{
        OutputDebugString(_T("FAILED - GetProcAddress"));
    }

    CreateProcessAsUser(hTokenDup, NULL, _T("c:\\windows\\notepad.exe"),
                        NULL, NULL, FALSE, dwCreationFlag,
                        pEnvironment, NULL, &si, &pi);
   
    if (NULL != lpfnDestroyEnvironmentBlock)
        lpfnDestroyEnvironmentBlock(pEnvironment);
 
    if (NULL != hUserEnvLib)
        FreeLibrary(hUserEnvLib);
   
    CloseHandle(hTokenDup);
}

What did I fix? The issue I was facing was that the application I wanted my Print Monitor to launch using CreateProcessAsUser() was not getting the logged-on user's environment. The after-effect was that, because of this reason, when my application used to show the File Open common dialog box, it would behave strange while trying to browse to the Desktop in it. This was in Vista.

In Windows XP, the File Open dialog would let you browse to the Desktop folder but the object icons on the Desktop would not appear right in it.

Note that if you don't use CreateEnvironmentBlock(), and the application you launch uses things like the Windows Common Dialog boxes, you may find file dialog boxes working erratically.

Refer this and this.

Saturday, April 14, 2007

Google pays $3.1bn for DoubleClick

Google acquired DoubleClick, a pioneer in online advertising, for $3.1 billion in cash, making Google a major player in online display advertising. Despite DoubleClick's revenues being estimated at $300m-$400m, the high price reflects intense competition in the online advertising market. Google aims to cross-sell DoubleClick's services to its existing search advertising customers. The deal allows Google to expand its advertising market as its core search advertising business slows down. The integration of search and display advertising is a significant benefit of the acquisition. DoubleClick's private equity owners profit handsomely from the sale. Here is an article from the Financial Times.