Search code examples
c++winapiunicodewstringlocal-security-authority

How do I convert a std::wstring to an LSA_UNICODE_STRING


Today I was able to write a simple C++ program that granted a user the "Log on as a service" privilege. Part of this involved converting between a LPCWSTR and an LSA_UNICODE_STRING. The code to do that is here:

LSA_UNICODE_STRING StringToLsaUnicodeString(LPCWSTR string) {
    LSA_UNICODE_STRING lsaString;
    DWORD dwLen = 0;

    dwLen = wcslen(string);
    lsaString.Buffer = (LPWSTR) string;
    lsaString.Length = (USHORT)((dwLen) * sizeof(WCHAR));
    lsaString.MaximumLength = (USHORT)((dwLen + 1) * sizeof(WCHAR));
    return lsaString;
}

When I had some small errors in this function, my call to LsaLookupNames2() failed with a code 87(hex 0x57) "The parameter is incorrect." I am trying to make this call in a C++ app that uses std::wstring and it is failing. My current function there is as follows:

#if defined(_UNICODE)
    LSA_UNICODE_STRING toLsaUnicodeString (std::wstring str) {
        LSA_UNICODE_STRING lsaWStr;
        DWORD len = 0;

        LPWSTR cstr = (LPWSTR)str.c_str();
        len = wcslen(cstr);
        lsaWStr.Buffer = cstr;
        lsaWStr.Length = (USHORT)((len) * sizeof(WCHAR));
        lsaWStr.MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR));
        return lsaWStr;
    } 
#endif

What am I doing wrong?


Solution

  • You're likely encountering a lifetime issue with the wchar_t* returned from str.c_str(). str.c_str() will return a pointer to an underlying string whose lifetime is governed by str. Since str is passed by-value, it will be destroyed at the end of the toLsaUnicodeString function, resulting in the returned LSA_UNICODE_STRING pointing at memory that has been deallocated. In order to avoid this, you'll need to make a copy of the underlying string in the toLsaUnicodeString function, and associate the copy with the returned LSA_UNICODE_STRING, something like:

    LSA_UNICODE_STRING toLsaUnicodeString (const std::wstring& str) {
        LSA_UNICODE_STRING lsaWStr;
        DWORD len = 0;
    
        len = str.length(); 
        LPWSTR cstr = new WCHAR[len + 1];
        memcpy(cstr, str.c_str(), (len + 1) * sizeof(WCHAR));
        lsaWStr.Buffer = cstr;
        lsaWStr.Length = (USHORT)((len) * sizeof(WCHAR));
        lsaWStr.MaximumLength = (USHORT)((len + 1) * sizeof(WCHAR));
        return lsaWStr;
    }
    

    Since the memory is now allocated on the heap, you are responsible for making sure it is deallocated. You can use a function like the following to take care of this.

    void freeLsaUnicodeString(LSA_UNICODE_STRING& str) {
        delete [] str.Buffer;
        str.Buffer = 0;
        str.Length = 0;
        str.MaximumLength = 0;
    }
    

    Even better would be to use RAII to manage the memory and guarantee that it is released when the variable is no longer in use. See Mr_C64's answer for details on this approach.