Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication against Active Directory with C++ on Linux

I'm surprised there's so few examples on this out there. I basically need to do a user/pass authentication against Active Directory. I'm able to initialize a connection to active directory, but it always gives me an "Invalid Credentials" error. I'm wondering if I'm passing it something wrong. This is my first attempt with LDAP. For that matter, I'm open to another (well documented perhaps) solution.

#include <iostream>
#include <stdio.h>

#define LDAP_SERVER "ldap://hq.mydomain.local/"

#include <ldap.h>

int main( int argc, char** argv )
{
    LDAP    *ld;
    int     version( LDAP_VERSION3 );
    int     rc;
    char    bind_dn[100];
    berval  creds;
    berval* serverCreds;

    if( ldap_initialize( &ld, LDAP_SERVER ) ) {
        std::cerr << "Error initializing ldap..." << std::endl;
        return 1;
    }

    ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );

    creds.bv_val = "password";
    creds.bv_len = strlen("password");

    rc = ldap_sasl_bind_s( ld, "sAMAccountName=MYDOMAIN\\UserName,dc=mydomain,dc=local", "GSSAPI", &creds, NULL, NULL, &serverCreds );

    if ( rc != LDAP_SUCCESS ) {
        std::cerr << "ERROR: " << ldap_err2string( rc ) << std::endl; 
        return 1;
    } else {
        std::cout << "Success." << std::endl;
    }

    return 0;

}

EDIT:

I wanted to make sure everything is okay on the server side, so did some tests with ldapsearch. It did not work at first, but I finally got it (with ldapsearch, anyway).

ldapsearch -D [email protected] -H "ldap://hq.mydomain.local:389" -b "ou=Development,ou=Domain Users,dc=mydomain,dc=local" -W "sAMAccountName=first.last"

Maybe not the best way. For starters, the key is the -D argument, and passing sAMAccountName at the end. I'm not going to have the common name - only the windows login name, and their password. The above command will show a user his info, if the password passes.

The caveat (I think) is that ldap_sasl_bind_s() has no equivalent of setting the -D (binddn) flag. Looking at this question/answer it looks like ldap_interactive_bind_s() might, but it's a bit more involved, as I have to pass a call back.

With the example above where I set a password, but no binddn/username of any kind, who does it assume I'm trying to authenticate as?

like image 619
kiss-o-matic Avatar asked Jul 29 '15 15:07

kiss-o-matic


1 Answers

sAMAccountName=MYDOMAIN\UserName,dc=mydomain,dc=local

This format of username is incorrect. You do not need to specify sAMAccountName in your username and don't need to specify dc unless you're using Distinguished Name. You have few options for username.

  1. Distinguished Name

CN=Jeff Smith,OU=Sales,DC=Fabrikam,DC=Com

  1. sAMaccountName

jsmith

  1. The user path from a previous version of Windows

"Fabrikam\jeffsmith".

  1. User Principal Name (UPN)

[email protected]

Having said that, I'm not certain if the username is the only issue you're experiencing. I have not run your code locally.

Although this answer may not directly answer your question, since I have not tested this code in Linux machine, it could give you an idea or put you in a right direction. I will not be surprised if this method is Windows specific only.

According to MSDN there're few methods you can use to authenticate a user.

The ADsOpenObject function binds to an ADSI object using explicit user name and password credentials.

This method is accepting the following parameters:

HRESULT ADsOpenObject(
  _In_  LPCWSTR lpszPathName,
  _In_  LPCWSTR lpszUserName,
  _In_  LPCWSTR lpszPassword,
  _In_  DWORD   dwReserved,
  _In_  REFIID  riid,
  _Out_ VOID    **ppObject
);

Using this method you can bind to object in Active Directory by specifying username and password.

If the bind is successful, the return code is S_OK, otherwise you'll get different error messages.

I don't write programs in C++ on a daily basis. I typically work with Active Directory and Active Directory Lightweight Services in a C# world. But this sample code I wrote, shows you how to call ADsOpenObject method to bind to an ADSI object using specified credentials. In your case, just authenticate.

#include <iostream>
#include "activeds.h"

using namespace std;

int main(int argc, char* argv[])
{
   HRESULT hr;
   IADsContainer *pCont;
   IDispatch *pDisp = NULL;
   IADs *pUser;

   CoInitialize(NULL);

   hr = ADsOpenObject( L"LDAP://yourserver",
               L"username",
               L"password",
               ADS_FAST_BIND, //authentication option     
               IID_IADs,
               (void**) &pUser);


   if (SUCCEEDED(hr))
   {
      cout << "Successfully authenticated";
   }
   else
      cout << "Incorrect username or password";
   return hr;
}

Depending on your setup, you might have to tweak ADS_AUTHENTICATION_ENUM. I suggest you install SSL Certificate and use ADS_USE_SSL binding. Dealing with passwords without SSL in AD, can be nightmare.

like image 66
smr5 Avatar answered Sep 28 '22 08:09

smr5