Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Logon task schedule Error: No Mapping between account names and security ids was done

I am trying to write a windows Logon trigger task using C++ on Windows 7.

I am following this microsoft tutorial.

But I am facing problem in saving the task to root folder. Here:

//  ------------------------------------------------------
    //  Save the task in the root folder.
    IRegisteredTask *pRegisteredTask = NULL;

    hr = pRootFolder->RegisterTaskDefinition(
            _bstr_t( wszTaskName ),
            pTask,
            TASK_CREATE_OR_UPDATE, 
            _variant_t(L"Builtin\\Administrators"), 
            _variant_t(), 
            TASK_LOGON_GROUP,
            _variant_t(L""),
            &pRegisteredTask);

Where the hr is getting error : No Mapping between account names and security ids was done

I also tried replacing _variant_t(L"Builtin\\Administrators") with _variant_t(L"S-1-5-32-544") to NULL out language hard coding issue, still No luck.

How can I make it work?

like image 570
foobar Avatar asked May 28 '14 11:05

foobar


2 Answers

A definitive solution to creation of a TaskScheduler task on Windows startup

(with Administor privileges, working for Windows 7, 8, etc. Note that this won't display an UAC popup on Windows startup "Are you sure to run this software with Admin rights?", that's why the TaskScheduler method is more interesting in this case than the good old HKEY_LOCAL_MACHINE\...\CurrentVersion\Run solution)

There are a few things to update in this tutorial to make it work:

  • _variant_t(L"S-1-5-32-544") instead of _variant_t(L"Builtin\\Administrators")
  • _CRT_SECURE_NO_WARNINGS
  • In VC++, Project Properties > Configuration Properties > Linker > Manifest file > UAC Execution Level > requireAdministrator
  • Remove the date boundaries which are now outdated !
  • Replace hr = pLogonTrigger->put_UserId(_bstr_t(L"DOMAIN\\UserName")); by either a hardcoded Domain\Username, or by some Domain\Username detection code (I couldn't make it work), or just comment this line, it worked for me!
  • Add some code for TASK_RUNLEVEL_HIGHEST
  • Add some code to enable the task even if running from a laptop on batteries (default would be "don't run task if on batteries"!), and some code to prevent the .exe to be killed after some time (By default, a task will be stopped 72 hours after it starts to run), etc.

Then you'll get the famous:

Success! Task successfully registered.


Phew! After a few hours per day and some edits, now here is a working full main.cpp:

#define SECURITY_WIN32
#include <windows.h>
#include <iostream>
#include <stdio.h>
#include <comdef.h>
#include <Security.h>
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")

using namespace std;

#define TASKNAME L"Logon Trigger Test Task"

int __cdecl wmain()
{
    //  Get the windows directory and set the path to notepad.exe.
    wstring wstrExecutablePath = _wgetenv(L"WINDIR");
    wstrExecutablePath += L"\\SYSTEM32\\NOTEPAD.EXE";

    //  Initialize COM  
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr)) return 1;

    //  Set general COM security levels.
    hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
    if (FAILED(hr)) goto cleanup0;

    //  Create an instance of the Task Service.     
    ITaskService *pService = NULL;
    hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService);
    if (FAILED(hr)) goto cleanup0;

    //  Connect to the task service.    
    hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
    if (FAILED(hr)) goto cleanup1;

    //  Get the pointer to the root task folder.  This folder will hold the new task that is registered.
    ITaskFolder *pRootFolder = NULL;
    hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
    if (FAILED(hr)) goto cleanup1;

    //  If the same task exists, remove it. 
    pRootFolder->DeleteTask(_bstr_t(TASKNAME), 0);

    //  Create the task builder object to create the task.  
    ITaskDefinition *pTask = NULL;
    hr = pService->NewTask(0, &pTask);

    // COM clean up.  Pointer is no longer used.    
    pService->Release();
    if (FAILED(hr)) { pRootFolder->Release(); CoUninitialize(); return 1; }

    //  Get the registration info for setting the identification.   
    IRegistrationInfo *pRegInfo = NULL;
    hr = pTask->get_RegistrationInfo(&pRegInfo);
    if (FAILED(hr)) goto cleanup2;

    hr = pRegInfo->put_Author(L"Author Name");
    pRegInfo->Release();
    if (FAILED(hr)) goto cleanup2;

    //  Create the settings for the task
    ITaskSettings *pSettings = NULL;
    hr = pTask->get_Settings(&pSettings);
    if (FAILED(hr)) goto cleanup2;
    
    //  Set setting values for the task. 
    pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
    pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
    pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S"));
    pSettings->Release();
    if (FAILED(hr)) goto cleanup2;

    //  Get the trigger collection to insert the logon trigger.
    ITriggerCollection *pTriggerCollection = NULL;
    hr = pTask->get_Triggers(&pTriggerCollection);
    if (FAILED(hr)) goto cleanup2;

    //  Add the logon trigger to the task.
    ITrigger *pTrigger = NULL;
    hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
    pTriggerCollection->Release();
    if (FAILED(hr)) goto cleanup2;

    ILogonTrigger *pLogonTrigger = NULL;
    hr = pTrigger->QueryInterface(IID_ILogonTrigger, (void**)&pLogonTrigger);
    pTrigger->Release();
    if (FAILED(hr)) goto cleanup2;

    hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
    if (FAILED(hr)) goto cleanup2;

    //  Define the user.  The task will execute when the user logs on. The specified user must be a user on this computer.  
    //hr = pLogonTrigger->put_UserId(_bstr_t(L"DOMAIN\\UserName"));

    pLogonTrigger->Release();
    if (FAILED(hr)) goto cleanup2;

    IPrincipal *pPrincipal;
    hr = pTask->get_Principal(&pPrincipal);
    if (FAILED(hr)) goto cleanup2;

    hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
    if (FAILED(hr)) goto cleanup2;


    //  Add an Action to the task. This task will execute .exe
    IActionCollection *pActionCollection = NULL;

    //  Get the task action collection pointer.
    hr = pTask->get_Actions(&pActionCollection);
    if (FAILED(hr)) goto cleanup2;

    //  Create the action, specifying that it is an executable action.
    IAction *pAction = NULL;
    hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
    pActionCollection->Release();
    if (FAILED(hr)) goto cleanup2;

    //  QI for the executable task pointer.
    IExecAction *pExecAction = NULL;
    hr = pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction);
    pAction->Release();
    if (FAILED(hr)) goto cleanup2;

    //  Set the path of the executable.
    hr = pExecAction->put_Path(_bstr_t(wstrExecutablePath.c_str()));
    pExecAction->Release();
    if (FAILED(hr)) goto cleanup2;

    //  Save the task in the root folder.
    IRegisteredTask *pRegisteredTask = NULL;
    hr = pRootFolder->RegisterTaskDefinition(_bstr_t(TASKNAME), pTask, TASK_CREATE_OR_UPDATE, _variant_t(L"S-1-5-32-544"), _variant_t(), TASK_LOGON_GROUP, _variant_t(L""), &pRegisteredTask);      //_variant_t(L"Builtin\\Administrators"),
    if (FAILED(hr)) goto cleanup2;

    printf("Success! Task successfully registered.");
    getchar();

    pRootFolder->Release();
    pTask->Release();
    pRegisteredTask->Release();
    CoUninitialize();
    return 0;

cleanup0:
    CoUninitialize();
    return 1;

cleanup1:
    pService->Release();
    CoUninitialize();
    return 1;

cleanup2:
    pRootFolder->Release();
    pTask->Release();
    CoUninitialize();
    return 1;
}
like image 181
Basj Avatar answered Nov 19 '22 20:11

Basj


I suspect the demo code you have is XP-era, and hasn't been updated to match the Vista/Win7 rules.

I updated the sample to set the LUA settings after setting the logon trigger, and it seems to work:

    hr = pLogonTrigger->put_UserId(_bstr_t(L"DOMAIN\username"));
    if (FAILED(hr))
    {
        printf("\nCannot add user ID to logon trigger: %x", hr);
        CoUninitialize();
        return 1;
    }


    //*** NEW**** Set the LUA settings
    CComPtr<IPrincipal>         pPrincipal;

    hr = pTask->get_Principal(&pPrincipal);
    if (SUCCEEDED(hr))
    {
        hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA);
    }
    if (SUCCEEDED(hr))
    {
        hr = pPrincipal->put_GroupId(_bstr_t(L"Builtin\\Administrators"));
    }
    if (FAILED(hr))
    {
        printf("\nCannot set runlevel/groupid: %x", hr);
        CoUninitialize();
        return 1;
    }

If you need it to run on XP, then it's likely that the get_Principal call will fail, so let that failure through.

like image 43
Eric Brown Avatar answered Nov 19 '22 21:11

Eric Brown