Skip to content

Instantly share code, notes, and snippets.

@richardschoen
Forked from NicolasSchindler/README.md
Created August 13, 2024 13:24
Show Gist options
  • Save richardschoen/9e71f2dbfe24d6ff4ee7ead70cb0effc to your computer and use it in GitHub Desktop.
Save richardschoen/9e71f2dbfe24d6ff4ee7ead70cb0effc to your computer and use it in GitHub Desktop.
How to connect IBM i to LDAP Server

How to connect a LDAP Server to IBM i

In this Project i provide a simple example how to connect to a LDAP Server from IBM i. I added some Constants and Functions from the LDAP Headerfile provided by IBM, not all Functions are implemented in this example. The Serviceprogramm is written in Fixed Format RPGLE and uses the provided LDAP API written in C, which is included in the OS. The Documentation of the LDAP API can be found here

Thanks to @ScottKlement, who has published an example of a copybook on his site, which I have used as a guide. You can find his example here. Also thanks to @m1h43l for helping me with handling the null-terminated strings in the RPG Code.

How to use this example

You can install the Serviceprogramm, by copying the sources provided in the Files below to your IBM i and compile it.

Examples

  1. Getting DN for Entry where Attribute fullname "Nicolas Schindler"
Dcl-S filter char(256);
Dcl-S base char(256);
Dcl-Ds Connection likeds(ConnectionDS);

base = 'OU=ACME,DC=internal';
filter = '(&(objectclass=person)(fullname=Nicolas Schindler))';
Connection.Host = '<YOUR-LDAP-SERVER>';
Connection.Port = LDAP_PORT; // or LDAPS_PORT for SSL
Connection.User = '<YOUR-LDAP_USER>';
Connection.Password = 'SUPER_SECRET_PASSWORD';

DN = LDAPSRV_GET_DN(Connection:base:filter);

snd-msg 'DN for Nicolas Schindler is: ' + DN;
  1. Getting Entry with Attribute memberOf for DN "CN=Nicolas Schindler,OU=ACME,DC=internal"
Dcl-S filter char(256);
Dcl-S base char(256);
Dcl-S attributes char(256) dim(32) inz;
Dcl-S value char(256);                     
Dcl-Ds Attr likeds(AttributeDS);
Dcl-Ds Connection likeds(ConnectionDS);
Dcl-Ds Entry likeds(DNDS) inz;

attributes(1) = 'memberOf';
base = 'OU=ACME,DC=internal';
filter = '(&(objectclass=person)(distinguishedName=CN=Nicolas Schindler,OU=ACME,DC=internal))';
Connection.Host = '<YOUR-LDAP-SERVER>';
Connection.Port = LDAP_PORT; // or LDAPS_PORT for SSL
Connection.User = '<YOUR-LDAP_USER>';
Connection.Password = 'SUPER_SECRET_PASSWORD';

Entry = LDAPSRV_GET_DN_WITH_ATTRIBUTES(Connection:base:filter:attributes);

For-Each Attr in Entry.Attributes;
    If Attr.Attribute <> *blank;
        snd-msg 'Attribute: ' + Attr.Attribute;
        For-Each value in Attr.Values;
            If value <> *blank;
                snd-msg 'Value: ' + value;
            EndIf;
        EndFor;
    EndIf;
EndFor;  
  1. Check if DN Entry have the Attribute memberOf with Value "CN=Admins,OU=ACME,DC=internal"
Dcl-S DN char(256);
Dcl-S attribute char(256);
Dcl-S value char(256);              
Dcl-Ds Connection likeds(ConnectionDS);

attribute = 'memberOf';
value = 'CN=Admins,OU=ACME,DC=internal';
DN = 'distinguishedName=CN=Nicolas Schindler,OU=ACME,DC=internal';
Connection.Host = '<YOUR-LDAP-SERVER>';
Connection.Port = LDAP_PORT; // or LDAPS_PORT for SSL
Connection.User = '<YOUR-LDAP_USER>';
Connection.Password = 'SUPER_SECRET_PASSWORD';

If LDAPSRV_HAS_DN_ATTRIBUTE_WITH_VALUE(Connection:DN:attribute:value);
    snd-msg 'DN has Attribute memberOf with Value CN=Admins,OU=ACME,DC=internal';
Else;
    snd-msg 'DN has not Attribute memberOf with Value CN=Admins,OU=ACME,DC=internal';
EndIf;
**FREE
// Ports
Dcl-C LDAP_Port 389;
Dcl-C LDAPS_Port 636;
// Error Codes
Dcl-C LDAP_SUCCESS x'00';
Dcl-C LDAP_OPERATIONS_ERROR x'01';
Dcl-C LDAP_PROTOCOL_ERROR x'02';
Dcl-C LDAP_TIMELIMIT_EXCEEDED x'03';
Dcl-C LDAP_SIZELIMIT_EXCEEDED x'04';
Dcl-C LDAP_COMPARE_FALSE x'05';
Dcl-C LDAP_COMPARE_TRUE x'06';
Dcl-C LDAP_STRONG_AUTH_NOT_SUPPORTED x'07';
Dcl-C LDAP_STRONG_AUTH_REQUIRED x'08';
Dcl-C LDAP_PARTIAL_RESULTS x'09';
Dcl-C LDAP_REFERRAL x'0A';
Dcl-C LDAP_ADMIN_LIMIT_EXCEEDED x'0B';
Dcl-C LDAP_UNAVAILABLE_CRITICAL_EXTENSION x'0C';
Dcl-C LDAP_NO_SUCH_ATTRIBUTE x'10';
Dcl-C LDAP_UNDEFINED_TYPE x'11';
Dcl-C LDAP_INAPPROPRIATE_MATCHING x'12';
Dcl-C LDAP_CONSTRAINT_VIOLATION x'13';
Dcl-C LDAP_TYPE_OR_VALUE_EXISTS x'14';
Dcl-C LDAP_INVALID_SYNTAX x'15';
Dcl-C LDAP_NO_SUCH_OBJECT x'20';
Dcl-C LDAP_ALIAS_PROBLEM x'21';
Dcl-C LDAP_INVALID_DN_SYNTAX x'22';
Dcl-C LDAP_IS_LEAF x'23';
Dcl-C LDAP_ALIAS_DEREF_PROBLEM x'24';
Dcl-C LDAP_INAPPROPRIATE_AUTH x'30';
Dcl-C LDAP_INVALID_CREDENTIALS x'31';
Dcl-C LDAP_INSUFFICIENT_ACCESS x'32';
Dcl-C LDAP_BUSY x'33';
Dcl-C LDAP_UNAVAILABLE x'34';
Dcl-C LDAP_UNWILLING_TO_PERFORM x'35';
Dcl-C LDAP_LOOP_DETECT x'36';
Dcl-C LDAP_NAMING_VIOLATION x'40';
Dcl-C LDAP_OBJECT_CLASS_VIOLATION x'41';
Dcl-C LDAP_NOT_ALLOWED_ON_NONLEAF x'42';
Dcl-C LDAP_NOT_ALLOWED_ON_RDN x'43';
Dcl-C LDAP_ALREADY_EXISTS x'44';
Dcl-C LDAP_NO_OBJECT_CLASS_MODS x'45';
Dcl-C LDAP_RESULTS_TOO_LARGE x'46';
Dcl-C LDAP_AFFECTS_MULTIPLE_DSAS x'47';
Dcl-C LDAP_OTHER x'50';
Dcl-C LDAP_SERVER_DOWN x'51';
Dcl-C LDAP_LOCAL_ERROR x'52';
Dcl-C LDAP_ENCODING_ERROR x'53';
Dcl-C LDAP_DECODING_ERROR x'54';
Dcl-C LDAP_TIMEOUT x'55';
Dcl-C LDAP_AUTH_UNKNOWN x'56';
Dcl-C LDAP_FILTER_ERROR x'57';
Dcl-C LDAP_USER_CANCELLED x'58';
Dcl-C LDAP_PARAM_ERROR x'59';
Dcl-C LDAP_NO_MEMORY x'5A';
Dcl-C LDAP_CONNECT_ERROR x'5b';
Dcl-C LDAP_NOT_SUPPORTED x'5c';
Dcl-C LDAP_CONTROL_NOT_FOUND x'5d';
Dcl-C LDAP_NO_RESULTS_RETURNED x'5e';
Dcl-C LDAP_MORE_RESULTS_TO_RETURN x'5f';
Dcl-C LDAP_URL_ERR_NOTLDAP x'60';
Dcl-C LDAP_URL_ERR_NODN x'61';
Dcl-C LDAP_URL_ERR_BADSCOPE x'62';
Dcl-C LDAP_URL_ERR_MEM x'63';
Dcl-C LDAP_CLIENT_LOOP x'64';
Dcl-C LDAP_REFERRAL_LIMIT_EXCEEDED x'65';
Dcl-C LDAP_SSL_ALREADY_INITIALIZED x'70';
Dcl-C LDAP_SSL_INITIALIZE_FAILED x'71';
Dcl-C LDAP_SSL_INITIALIZE_NOT_CALLED x'72';
Dcl-C LDAP_SSL_PARAM_ERROR x'73';
Dcl-C LDAP_SSL_HANDSHAKE_FAILED x'74';
Dcl-C LDAP_SSL_GET_CIPHER_FAILED x'75';
Dcl-C LDAP_SSL_NOT_AVAILABLE x'76';
Dcl-C LDAP_SSL_KEYRING_NOT_FOUND x'77';
Dcl-C LDAP_SSL_PASSWORD_NOT_SPECIFIED x'78';
Dcl-C LDAP_NO_EXPLICIT_OWNER x'80';
Dcl-C LDAP_NO_LOCK x'81';
Dcl-C LDAP_DNS_NO_SERVERS x'85';
Dcl-C LDAP_DNS_TRUNCATED x'86';
Dcl-C LDAP_DNS_INVALID_DATA x'87';
Dcl-C LDAP_DNS_RESOLVE_ERROR x'88';
Dcl-C LDAP_DNS_CONF_FILE_ERROR x'89';
Dcl-C LDAP_XLATE_E2BIG x'A0';
Dcl-C LDAP_XLATE_EINVAL x'A1';
Dcl-C LDAP_XLATE_EILSEQ x'A2';
Dcl-C LDAP_XLATE_NO_ENTRY x'A3';
Dcl-C LDAP_REG_FILE_NOT_FOUND x'B0';
Dcl-C LDAP_REG_CANNOT_OPEN x'B1';
Dcl-C LDAP_REG_ENTRY_NOT_FOUND x'B2';
Dcl-C LDAP_CONF_FILE_NOT_OPENED x'C0';
Dcl-C LDAP_PLUGIN_NOT_LOADED x'C1';
Dcl-C LDAP_PLUGIN_FUNCTION_NOT_RESOLVED x'C2';
Dcl-C LDAP_PLUGIN_NOT_INITIALIZED x'C3';
Dcl-C LDAP_PLUGIN_COULD_NOT_BIND x'C4';
Dcl-C LDAP_SASL_GSS_NO_SEC_CONTEXT x'D0';
// Scopes
Dcl-C LDAP_SCOPE_BASE x'00';
Dcl-C LDAP_SCOPE_ONELEVEL x'01';
Dcl-C LDAP_SCOPE_SUBTREE x'02';
// Data Structures
Dcl-S p_Timeval pointer;
Dcl-Ds timeval qualified based(p_Timeval) align;
tv_sec int(10);
tv_usec int(10);
End-Ds;
Dcl-S p_Berval pointer;
Dcl-Ds berval qualified based(p_Berval) align;
bv_len uns(10);
bv_val pointer;
End-Ds;
Dcl-S p_LDAPMOD pointer;
Dcl-Ds LdapMod based(p_LDAPMOD) align;
mod_op int(10);
mod_type pointer;
modv_strvals pointer;
modv_bvals pointer overlay(modv_strvals);
End-Ds;
// Prototypes
Dcl-Pr ldap_init pointer extproc('ldap_init');
host pointer value options(*string);
port int(10) value;
End-Pr;
Dcl-Pr ldap_simple_bind_s int(10) extproc('ldap_simple_bind_s');
ld pointer value;
dn pointer value options(*string);
pw pointer value options(*string);
End-Pr;
Dcl-Pr ldap_unbind int(10) extproc('ldap_unbind');
ld pointer value;
End-Pr;
Dcl-Pr ldap_err2string pointer extproc('ldap_err2string');
error int(10) value;
End-Pr;
Dcl-Pr ldap_search_s int(10) extproc('ldap_search_s');
ld pointer value;
base pointer value options(*string);
scope int(10) value;
filter pointer value options(*string);
attrs pointer value;
attrsonly int(10) value;
res pointer;
End-Pr;
Dcl-Pr ldap_first_entry pointer extproc('ldap_first_entry');
ld pointer value;
result pointer value;
End-Pr;
Dcl-Pr ldap_next_entry pointer extproc('ldap_next_entry');
ld pointer value;
entry pointer value;
End-Pr;
Dcl-Pr ldap_first_attribute pointer extproc('ldap_first_attribute');
ld pointer value;
entry pointer value;
berptr pointer;
End-Pr;
Dcl-Pr ldap_next_attribute pointer extproc('ldap_next_attribute');
ld pointer value;
entry pointer value;
berptr pointer value;
End-Pr;
Dcl-Pr ldap_get_dn pointer extproc('ldap_get_dn');
ld pointer value;
entry pointer value;
End-Pr;
Dcl-Pr ldap_msgfree int(10) extproc('ldap_msgfree');
msg pointer value;
End-Pr;
Dcl-Pr ldap_memfree extproc('ldap_memfree');
msg pointer value;
End-Pr;
Dcl-Pr ldap_value_free extproc('ldap_value_free');
mem pointer value;
End-Pr;
Dcl-Pr ldap_get_errno int(10) extproc('ldap_get_errno');
ld pointer value;
End-Pr;
Dcl-Pr ldap_count_entries int(10) extproc('ldap_count_entries');
ld pointer value;
result pointer value;
End-Pr;
Dcl-Pr ldap_count_attributes int(10) extproc('ldap_count_attributes');
ld pointer value;
result pointer value;
End-Pr;
Dcl-Pr ldap_count_values int(10) extproc('ldap_count_values');
values pointer value;
End-Pr;
Dcl-Pr ldap_get_values pointer extproc('ldap_get_values');
ld pointer value;
entry pointer value;
attr pointer value options(*string);
End-Pr;
Dcl-Pr ldap_compare_s int(10) extproc('ldap_compare_s');
ld pointer value;
dn pointer value options(*string);
attr pointer value options(*string);
value pointer value options(*string);
End-Pr;
STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('V1.0')
EXPORT SYMBOL('LDAPSRV_GET_DN')
EXPORT SYMBOL('LDAPSRV_GET_DN_WITH_ATTRIBUTES')
EXPORT SYMBOL('LDAPSRV_HAS_DN_ATTRIBUTE_WITH_VALUE')
ENDPGMEXP
**FREE
Ctl-Opt NoMain;
/copy qcpysrc,LDAPSRV
/copy qcpysrc,LDAP_H
//-----------------------------------------------------------------------------
//\brief Get DN from LDAP which matches the filter
//-----------------------------------------------------------------------------
Dcl-Proc LDAPSRV_GET_DN Export;
Dcl-Pi *N char(256);
pConnection likeds(ConnectionDS);
pBase char(256) value;
pFilter char(256) value;
End-Pi;
Dcl-S returnDN char(256);
Dcl-S LDAP pointer;
Dcl-S result pointer;
Dcl-S entry pointer;
Dcl-S dn pointer;
Dcl-S rc int(10);
LDAP = ldap_init(%trim(pConnection.Host):pConnection.Port);
rc = ldap_get_errno(LDAP);
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return *BLANKS;
EndIf;
rc = ldap_simple_bind_s(LDAP:%trim(pConnection.User):%trim(pConnection.Password));
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return *BLANKS;
EndIf;
If LDAP = *NULL;
return *BLANKS;
EndIf;
rc = ldap_search_s(LDAP:pBase:LDAP_SCOPE_SUBTREE:pFilter:*NULL:1:result);
If rc = LDAP_SUCCESS;
entry = ldap_first_entry(LDAP:result);
If entry <> *NULL;
dn = ldap_get_dn(LDAP:entry);
returnDN = %str(dn:256);
ldap_memfree(dn);
EndIf;
EndIf;
ldap_unbind(LDAP);
return returnDN;
End-Proc;
//-----------------------------------------------------------------------------
//\brief Get DN with attributes from LDAP which matches the filter
// returns a datastructure with the DN and the attributes
//-----------------------------------------------------------------------------
Dcl-Proc LDAPSRV_GET_DN_WITH_ATTRIBUTES Export;
Dcl-Pi *N likeds(DNDS);
pConnection likeds(ConnectionDS);
pBase char(256) value;
pFilter char(256) value;
pAttributes char(256) dim(32);
End-Pi;
Dcl-Ds returnDS likeds(DNDS) inz;
Dcl-S LDAP pointer;
Dcl-S rc int(10);
Dcl-S x int(10) inz(1);
Dcl-S y int(10) inz(1);
Dcl-S z int(10) inz(1);
Dcl-S dn pointer;
Dcl-S result pointer;
Dcl-S entry pointer;
Dcl-S berptr pointer;
Dcl-S attribute pointer;
Dcl-S values pointer;
Dcl-S valuesArray pointer based(values) dim(256);
Dcl-S strPtr pointer;
Dcl-S AttributeChar char(256);
Dcl-S attrArray pointer dim(32);
For-each AttributeChar in pAttributes;
If AttributeChar <> *BLANK;
strPtr = %alloc(%len(AttributeChar));
%str(strPtr:%len(%trim(AttributeChar)) + 1) = AttributeChar;
attrArray(y) = strPtr;
Else;
attrArray(y) = *NULL;
EndIf;
y += 1;
EndFor;
LDAP = ldap_init(%trim(pConnection.Host):pConnection.Port);
rc = ldap_get_errno(LDAP);
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return returnDS;
EndIf;
rc = ldap_simple_bind_s(LDAP:%trim(pConnection.User):%trim(pConnection.Password));
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return returnDS;
EndIf;
rc = ldap_search_s(LDAP:pBase:LDAP_SCOPE_SUBTREE:pFilter:%addr(attrArray):0:result);
If rc = LDAP_SUCCESS;
entry = ldap_first_entry(LDAP:result);
If entry <> *NULL;
dn = ldap_get_dn(LDAP:entry);
returnDS.Name = %str(dn:256);
attribute = ldap_first_attribute(LDAP:entry:berptr);
z = 1;
Dow attribute <> *NULL and z <= 64;
returnDS.Attributes(z).Attribute = %str(attribute:256);
values = ldap_get_values(LDAP:entry:attribute);
If values <> *NULL;
x = 1;
Dow x <= 256 and valuesArray(x) <> *NULL;
returnDS.Attributes(z).Values(x) = %str(valuesArray(x):256);
x += 1;
EndDo;
EndIf;
ldap_value_free(values);
attribute = ldap_next_attribute(LDAP:entry:berptr);
z += 1;
EndDo;
ldap_memfree(dn);
EndIf;
EndIf;
ldap_unbind(LDAP);
return returnDS;
End-Proc;
//-----------------------------------------------------------------------------
//\brief Checks whether the LDAP DN has an attribute with a specific value
//-----------------------------------------------------------------------------
Dcl-Proc LDAPSRV_HAS_DN_ATTRIBUTE_WITH_VALUE Export;
Dcl-Pi *N ind;
pConnection likeds(ConnectionDS);
pDN char(256) value;
pAttributes char(256) value;
pValue char(256) value;
End-Pi;
Dcl-S rc int(10);
Dcl-S LDAP pointer;
LDAP = ldap_init(%trim(pConnection.Host):pConnection.Port);
rc = ldap_get_errno(LDAP);
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return *Off;
EndIf;
rc = ldap_simple_bind_s(LDAP:%trim(pConnection.User):%trim(pConnection.Password));
If rc <> LDAP_SUCCESS;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return *Off;
EndIf;
rc = ldap_compare_s(LDAP:pDN:pAttributes:pValue);
ldap_unbind(LDAP);
If rc = LDAP_COMPARE_TRUE;
return *On;
ElseIf rc = LDAP_COMPARE_FALSE;
return *Off;
EndIf;
snd-msg 'Error: ' + %str(ldap_err2string(rc):1024);
return *Off;
End-Proc;
**FREE
//---------------------------------------------------------------
// Datastructure
//---------------------------------------------------------------
Dcl-DS ConnectionDS qualified;
Host char(256);
Port int(5);
User char(256);
Password char(256);
End-DS;
Dcl-DS AttributeDS qualified;
Attribute char(256);
Values char(256) dim(256);
End-DS;
Dcl-DS DNDS qualified;
Name char(256);
Attributes likeds(AttributeDS) dim(64);
End-DS;
//---------------------------------------------------------------
// Prototypes
//---------------------------------------------------------------
Dcl-Pr LDAPSRV_GET_DN char(256);
pConnection likeds(ConnectionDS);
pBase char(256) value;
pFilter char(256) value;
End-Pr;
Dcl-Pr LDAPSRV_GET_DN_WITH_ATTRIBUTES likeds(DNDS);
pConnection likeds(ConnectionDS);
pBase char(256) value;
pFilter char(256) value;
pAttributes char(256) dim(32);
End-Pr;
Dcl-Pr LDAPSRV_HAS_DN_ATTRIBUTE_WITH_VALUE ind;
pConnection likeds(ConnectionDS);
pDN char(256) value;
pAttributes char(256) value;
pValue char(256) value;
End-Pr;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment