0

I have C++ code that tries to authenticate a local user to windows:

BOOL result = ::LogonUserW(localAdminUserName_.c_str(), L".", localAdminPassword_.c_str(), 
    LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT);

This works fine for ASCII character-set based usernames.

But doesn't work for a user named, say userああ

If I print the variable localAdminUserName_ in the log, It's printing the username just fine.

Is .c_str() messing it up somehow?

Should I encode the username/password in someway before making this API call?

The following console application I made to test this scenario, is working fine!

_declspec(dllimport)
BOOL
__stdcall
LogonUserW(
    __in        LPCWSTR lpszUsername,
    __in_opt    LPCWSTR lpszDomain,
    __in        LPCWSTR lpszPassword,
    __in        DWORD dwLogonType,
    __in        DWORD dwLogonProvider,
    __deref_out PHANDLE phToken
    );

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hToken = NULL;

    BOOL returnValue = LogonUserW(
                            L"userああ",
                            L".",
                            L"pa$$w0rd",
                            LOGON32_LOGON_NETWORK,
                            LOGON32_PROVIDER_DEFAULT,
                            &hToken); 

    if (returnValue == false) {
        std::cout<<"Error!";
    } else {
        std::cout<<"Success!";
    }
    return 0;
}
gkns
  • 697
  • 2
  • 12
  • 32
  • 2
    Most likely, `localAdminUserName_` is simply not being encoded correctly for Unicode. Or its encoding does not match how the username is encoded inside of Windows. How is `localAdminUserName_` being populated? User input? File input? Hard-coded? How is it being logged? Please show that code. – Remy Lebeau Mar 18 '16 at 20:10
  • Also, what error code is being returned? – Harry Johnston Mar 18 '16 at 23:02
  • @HarryJohnston The error code is 1326 – gkns Mar 21 '16 at 05:59
  • @RemyLebeau It's retrieved from a server using a SOAP call and the retrieved value is printed correctly in the logs. -The reason why I feel some encoding is required before sending it to the logon API. – gkns Mar 21 '16 at 12:16
  • Please show the actual SOAP, the code that is extracting the values from the SOAP, and the code that is logging the values. You are clearly missing something. Maybe the values are escaped in the SOAP and you are not removing the escaping before calling `LogonUser()`. Or maybe the values are using a different Unicode normalization than Windows is using. – Remy Lebeau Mar 21 '16 at 14:09
  • 1
    Or show the actual bytes in the string. – Adrian McCarthy Mar 21 '16 at 16:30
  • Since this is a local account, you can use [NetUserEnum](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370652(v=vs.85).aspx) to find the actual username and compare it byte by byte to the string the server gave you. (The fact that it looks the same in the logs is inconclusive, two Unicode strings can look the same without being the same.) – Harry Johnston Mar 21 '16 at 20:16
  • Problem solved after I converted the original strign to MbString and then used the windows.h method MultiByteToWideChar() – gkns Mar 22 '16 at 09:51

1 Answers1

0

Problem got solved after the original string is converted to multi byte string and then used the MultiByteToWideChar method in windows.h to convert it to wide char:

//convert the credentials to a multi-byte string first
std::string MbLocalAdminUserName = MbString(localAdminUserName_.c_str());
std::string MbLocalAdminPassowrd = MbString(localAdminPassowrd_.c_str());

//convert this multi-byte format to wide char that windows is expecting
//username
int len_MbLocalAdminUserName = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminUserName = new wchar_t[len_MbLocalAdminUserName + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str(), -1, WcLocalAdminUserName, len_MbLocalAdminUserName + 1);
//password
int len_MbLocalAdminPassowrd = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminPassowrd = new wchar_t[len_MbLocalAdminPassowrd + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str(), -1, WcLocalAdminPassowrd, len_MbLocalAdminPassowrd + 1);

BOOL result = ::LogonUser(WcLocalAdminUserName, L".", WcLocalAdminPassowrd, 
    LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken);
delete[] WcLocalAdminUserName;
delete[] WcLocalAdminPassowrd;

Where MbString is :

MbString::MbString(const wchar_t* src)
: buf_(0) {
const size_t count = 1 + sizeof(wchar_t) * wcslen(src);
buf_ = new char[count];

// The 3rd parameter specifies the size of multi-bytes string buffer.
wcstombs(buf_, src, count);
}
gkns
  • 697
  • 2
  • 12
  • 32
  • Can you show the code for MbString? It looks as if the SOAP call is giving you a 16-bit string encoded in UTF-8 (which is awfully strange) but it's hard to tell without seeing the rest of the code. Also, if MbString does anything more complicated than throwing away each character's upper byte, this process may malfunction for some characters and/or language settings. (It would likely be better to fix the underlying problem than to try to patch the string back up after it has been mangled.) – Harry Johnston Mar 22 '16 at 21:19
  • Yeah, the behaviour of `wcstombs` depends on the current locale, so the conversion will likely fail if the client computer is configured to use any locale other than the simplest ones. I think you need to figure out why the string is `wchar_t` in the first place, looks like it should have been just plain `char`. (But you first need to compare the original string to the string that works, to confirm that the original string really is 16-bit UTF-8.) – Harry Johnston Mar 23 '16 at 20:17