Search code examples
c++windowscredentialssspi

SEC_WINNT_AUTH_IDENTITY creation (SSPI)


i'm trying to create an SEC_WINNT_AUTH_IDENTITY to use into a call to AcquireCredentialsHandle in order to get credentials for this identity. However i struggle creating the Authenfication date.

Here is the code used to create the struct :

#define SECURITY_WIN32
#include <Windows.h>
#include <sspi.h>


int main(int argc, char* argv[]) {

SEC_WINNT_AUTH_IDENTITY AuthId;
char* login = "login";
char* domain = "mydomain.com";
char* password = "pass";

AuthId.User = login;
AuthId.UserLength = strlen(login);
AuthId.Domain = domain;
AuthId.DomainLength = strlen(domain);
AuthId.Password = password;
AuthId.PasswordLength = strlen(password);
AuthId.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

//Here the call to AcquireCredentialHandle and the end of the program
}

The problem is that the struct want unsigned short __RPC_FAR *User but i can't find any info on this and how i'm supposed to pass it strings. The examples i found ont Internet are all using strings directly.


Solution

  • Some experience in ODBC helped me in this situation. However, it won't work for Unicode passwords, and I do not need it for now. But it would be nice if someone will give a hint.

    Well, here is my solution. I've created helper class:

    Header:

    class StringParam
    {
    public:
        StringParam();
        StringParam(const std::wstring& val);
        StringParam(const std::string& val);
        virtual ~StringParam();
    
        unsigned short* GetBuffer() const;
        unsigned long   GetBufferLength() const;
        unsigned long   GetLength() const;
    private:
        unsigned short* buffer;
        unsigned long   bufferLength;
    };
    

    Implementation:

    StringParam::StringParam():
        buffer(NULL),
        bufferLength(0)
    {
    }
    
    StringParam::StringParam(const std::wstring& val):
        StringParam()
    {
        size_t len = val.size(); // .length() is the same
        bufferLength = sizeof(unsigned short) * (len + 1);
        unsigned short* buf = new unsigned short[len + 1];
        memset(buf, 0, bufferLength);
        int i = 0;
        for (wchar_t c: val)
            buf[i++] = (unsigned short)c;
    
        buffer = reinterpret_cast<unsigned short*>(buf);
    }
    
    StringParam::StringParam(const std::string& val):
        StringParam()
    {
        size_t len = val.size(); // .length() is the same
        bufferLength = sizeof(unsigned short) * (len + 1);
        unsigned short* buf = new unsigned short[len + 1];
        memset(buf, 0, bufferLength);
        int i = 0;
        for (char c: val)
            buf[i++] = (unsigned short)c;
    
        buffer = reinterpret_cast<unsigned short*>(buf);
    }
    
    StringParam::~StringParam()
    {
        if (buffer)
        {
            delete[] buffer;
            buffer = NULL;
            bufferLength = 0;
        }
    }
    
    unsigned short* StringParam::GetBuffer() const
    {
        return buffer;
    }
    
    unsigned long StringParam::GetBufferLength() const
    {
        return bufferLength;
    }
    
    unsigned long StringParam::GetLength() const
    {
        return bufferLength / sizeof(unsigned short);
    }
    

    Usage:

    SECURITY_STATUS res;
    
    // Aquire credentials
    CredHandle              credentials;
    TimeStamp               credentialsExpiryTime;
    
    SEC_WINNT_AUTH_IDENTITY userIdentity;
    StringParam prmDomain("domain.com");
    StringParam prmUser("username");
    StringParam prmPassword("password#1");
    // wstring version will also work:
    //StringParam prmDomain(L"domain.com");
    //StringParam prmUser(L"username");
    //StringParam prmPassword(L"password#1");
    
    userIdentity.Domain = prmDomain.GetBuffer();
    userIdentity.DomainLength = prmDomain.GetLength();
    userIdentity.User = prmUser.GetBuffer();
    userIdentity.UserLength = prmUser.GetLength();
    userIdentity.Password = prmPassword.GetBuffer();
    userIdentity.PasswordLength = prmPassword.GetLength();
    userIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
    res = AcquireCredentialsHandle(NULL, L"Kerberos", SECPKG_CRED_INBOUND, NULL, &userIdentity, NULL, NULL, &credentials, &credentialsExpiryTime);
    

    Works just fine for me. The trick is that wchar_t is 4 bytes long. But SSPI uses UTF-16. In ODBC Unicode characters converted from wstring worked, but here something is different.