How to pull from Active Directory and enter into
Openldap
My goal was to setup an opensource email server that would authenticate
via Active Directory. I also wanted a Ldap address book located on the
local server that gathered it's information from Active Directory. I did quite
a bit of searching and was finally able to get all of this
working, only because others had laid the ground work. I decided to use CentOS so the information is based upon
this distribution. I also want to point out that I'm not a programmer, the best I can do is hack someone elses work. Also I'm sure this is not the best way to do
this. It does however work, and that's what I wanted....I hope this helps someone.
Well deserved Credits:
Postfix SMTP Server Setup Howto for RHEL/CentOS 5
YoLinux LDAP Tutorial: Support scripts
and software tools for OpenLDAP directories
Basically we do things in several steps:
Query Active Directory and redirect output to a file:
ldapsearch -x -L -E pr=200/noprompt -h 192.168.1.1 -b'DC=example,DC=com' -D 'administrator@example.com' -w admin-passwd "mail=*" >/opt/ldap-conversion/data/ad_out.ldif
Please note I only want to search the Active Directory for accounts that have an email address field filled out.
This file is later copied to ad_out_last.ldif as the last line in the script, we later modify it so we can delete users as well after they have been removed from Active
Directory.
Convert output file format to csv:
/bin/awk -F ': ' -f /opt/ldap-conversion/script-2-csv.awk < /opt/ldap-conversion/data/ad_out.ldif > /opt/ldap-conversion/data/ad_out.csv
Convert output (last) file format to csv:
/bin/awk -F ': ' -f /opt/ldap-conversion/script-2-csv.awk < /opt/ldap-conversion/data/ad_out_last.ldif > /opt/ldap-conversion/data/ad_out_last.csv
Contents of script-2-csv.awk:
# Create csv dump for whole database
#
BEGIN {
last = ""
first = ""
name = ""
mail = ""
# printf(" last,first,full name,e-mail,\n");
}
/^sn: / {last=$2}
/^givenName: / {first=$2}
/^cn: / {name=$2}
/^mail: / {mail=$2}
/^dn/ {
if(last != "" && first != "" && last != "StoogeAdmin") printf("%s,%s,%s,%s\n",last,first,name,mail)
last = ""
first = ""
name = ""
mail = ""
}
# Capture last dn
END { if(last != "" && first != "" && last != "StoogeAdmin") printf("%s,%s,%s,%s\n",last,first,name,mail)}
Please note: this has been tailored to JUST pull last,first,fullname,and email. It could be used to parse through the entire output.
I feel it's easy enough to interpret the needed changes.
Create a csv using diff to find any users that have been deleted from Active Directory:
diff /opt/ldap-conversion/data/ad_out.csv /opt/ldap-conversion/data/ad_out_last.csv > /opt/ldap-conversion/data/temp.csv
Create two ldif files from the csv files (temp.csv & ad_out.csv), this takes two different programs as the ldif for ldapdelete has to be different from ldapadd and
ldapmodify:
This is done with a C program, this is how I call it:
./create_ldif < ad_out.csv > outfile.ldif
Contents of program for processing ad_out.csv before compiled:
/* File: csv-2-ldif.c
Author: Greg Ippolito
Version: 1.0
Useage: csvDump2ldf < inputfile.csv > outfile.ldif
Load into LDAP:
#ldapadd -f stooges.ldif -cxv -D "cn=StoogeAdmin,o=stooges" -W
*/
#include
#include
#define MAX_NUMBER_OF_FIELDS 17
#define MAX_FIELD_LENGTH 132
#define TOTAL_SIZE (MAX_FIELD_LENGTH * MAX_NUMBER_OF_FIELDS)
#define SERVER_ROOT "dc=example,dc=com"
int main(int argv, char *argc[])
{
int i, c;
int ifield; /* Field count */
int iline=0; /* Line count */
int ignoreCommaFlag = 0;
char field[MAX_NUMBER_OF_FIELDS][MAX_FIELD_LENGTH]; /* 17 fields */
char *server_root = SERVER_ROOT;
int ii, kk, fComma;
/* for( ii=1910; ii<2100; ii++ )
{
printf("dn: ou=%d,o=stooges\n",ii);
printf("ou: %d\n",ii);
printf("objectclass: top\n");
printf("objectclass: organizationalUnit\n");
printf("\n");
}
*/
bzero((char *)field, (size_t) TOTAL_SIZE);
i = 0;
ifield = 0;
fComma = 0;
while ((c = getchar()) != EOF)
{
if( c == '"' && ignoreCommaFlag ) ignoreCommaFlag = 0;
else if( c == '"' && !ignoreCommaFlag ) ignoreCommaFlag = 1;
else if( c == ',' && ignoreCommaFlag )
{
field[ifield][i] = ' ';
i++;
}
else if( c == ',' && !ignoreCommaFlag )
{
fComma = 1; /* Set comma flag */
field[ifield][i] = '\0'; /* NULL terminate */
i = 0;
ifield++; /* Found line with bogus number of fields. Keep repeating last field.*/
if( ifield == MAX_NUMBER_OF_FIELDS ) ifield--;
}
else if( c == '\n' )
{ /* First field number begins count at 0 */
iline++;
field[ifield][i] = '\0'; /* NULL terminate */
fComma = 0; /* Set comma flag */
if( field[2][0] == '\0' )
fprintf(stderr,"Error line %d: Blank field 3 - no cn\n", iline);
else
{
printf("dn: cn=%s,", field[2]);
printf("ou=faculty,dc=example,dc=com\n");
printf("cn: %s\n", field[2]);
printf("sn: %s\n", field[0]);
printf("objectClass: person\n");
printf("mail: %s\n", field[3]);
printf("objectClass: organizationalPerson\n");
printf("objectClass: inetOrgPerson\n");
printf("\n"); /* Clear variables */
bzero((char *)field, (size_t) TOTAL_SIZE);
}
i = 0;
ifield = 0;
}
else
{
//if( i==0 && c==' ' )
field[ifield][i] = c;
i++;
fComma = 0; /* Set comma flag */
} }}
Please Note: this file has been tailored to provide proper output for insertion in my ldap system
It was compiled as follows:
gcc -o create_ldif csv-2-ldif.c
input was csv-2-ldif.c
program now called create_ldif
Contents of program for processing temp.csv before compiled:
/* File: csv-2-ldif.c
Author: Greg Ippolito
Version: 1.0
Useage: csvDump2ldf < inputfile.csv > outfile.ldif
Load into LDAP:
#ldapadd -f stooges.ldif -cxv -D "cn=StoogeAdmin,o=stooges" -W
*/
#include
#include
#define MAX_NUMBER_OF_FIELDS 17
#define MAX_FIELD_LENGTH 132
#define TOTAL_SIZE (MAX_FIELD_LENGTH * MAX_NUMBER_OF_FIELDS)
#define SERVER_ROOT "dc=example,dc=com"
int main(int argv, char *argc[])
{
int i, c;
int ifield; /* Field count */
int iline=0; /* Line count */
int ignoreCommaFlag = 0;
char field[MAX_NUMBER_OF_FIELDS][MAX_FIELD_LENGTH]; /* 17 fields */
char *server_root = SERVER_ROOT;
int ii, kk, fComma;
bzero((char *)field, (size_t) TOTAL_SIZE);
i = 0;
ifield = 0;
fComma = 0;
while ((c = getchar()) != EOF)
{
if( c == '"' && ignoreCommaFlag ) ignoreCommaFlag = 0;
else if( c == '"' && !ignoreCommaFlag ) ignoreCommaFlag = 1;
else if( c == ',' && ignoreCommaFlag )
{
field[ifield][i] = ' ';
i++;
}
else if( c == ',' && !ignoreCommaFlag )
{
fComma = 1; /* Set comma flag */
field[ifield][i] = '\0'; /* NULL terminate */
i = 0;
ifield++; /* Found line with bogus number of fields. Keep repeating last field.*/
if( ifield == MAX_NUMBER_OF_FIELDS ) ifield--;
}
else if( c == '\n' )
{ /* First field number begins count at 0 */
iline++;
field[ifield][i] = '\0'; /* NULL terminate */
fComma = 0; /* Set comma flag */
if( field[2][0] == '\0' )
fprintf(stderr,"\n", iline);
else
{
printf("cn=%s,", field[2]);
printf("ou=faculty,dc=example,dc=com\n");
printf("\n"); /* Clear variables */
bzero((char *)field, (size_t) TOTAL_SIZE);
}
i = 0;
ifield = 0;
}
else
{
//if( i==0 && c==' ' )
field[ifield][i] = c;
i++;
fComma = 0; /* Set comma flag */
} }}
Compile this the same way as the one above
Add/remove ldif contents to ldap system:
ldapdelete -c -x -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/delete.ldif
ldapadd -c -x -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/outfile.ldif
ldapmodify -c -x -F -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/outfile.ldif
I created the following script to be run by crond:
ldapsearch -x -L -E pr=200/noprompt -h 192.168.1.1 -b'DC=example,DC=com' -D 'administrator@example.com' -w admin-passwd "mail=*" >//opt/ldap-conversion/data/ad_out.ldif
/bin/awk -F ': ' -f /home/scott/ldap-conversion/script-2-csv.awk < /opt/ldap-conversion/data/ad_out.ldif > /opt/ldap-conversion/data/ad_out.csv
/bin/awk -F ': ' -f /home/scott/ldap-conversion/script-2-csv.awk < /opt/ldap-conversion/data/ad_out_last.ldif > /opt/ldap-conversion/data/ad_out_last.csv
diff /opt/ldap-conversion/data/ad_out.csv /opt/ldap-conversion/data/ad_out_last.csv > /opt/ldap-conversion/data/temp.csv
./create_ldif < /opt/ldap-conversion/data/ad_out.csv > /opt/ldap-conversion/data/outfile.ldif
./create_del_ldif < /opt/ldap-conversion/data/temp.csv > /opt/ldap-conversion/data/delete.ldif
ldapdelete -c -x -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/delete.ldif
ldapadd -c -x -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/outfile.ldif
ldapmodify -c -x -F -D 'cn=manager,dc=example,dc=com' -w secret -f /opt/ldap-conversion/data/outfile.ldif
cp /opt/ldap-conversion/data/ad_out.ldif /opt/ldap-conversion/data/ad_out_last.ldif