centralized authentication and user information

now after you install the packages you start by editing slapd.conf


#
# See slapd.conf(5) for details on configuration options.
# This file should NOT be world readable.
#
include		/etc/openldap/schema/core.schema
include		/etc/openldap/schema/cosine.schema
include		/etc/openldap/schema/inetorgperson.schema
include		/etc/openldap/schema/nis.schema

pidfile		/var/run/slapd.pid
argsfile	/var/run/slapd.args

## ACL ##

access to * by * read

#######################################################################
# ldbm and/or bdb database definitions
#######################################################################

database	bdb
suffix		"dc=lab,dc=local"
rootdn		"cn=admin,dc=lab,dc=local"
rootpw		{crypt}

defaultaccess   read

directory	/var/lib/ldap

# Indices to maintain for this database
index objectClass                       eq,pres
index ou,cn,mail,surname,givenname      eq,pres,sub
#index uidNumber,gidNumber,loginShell    eq,pres
index uid,memberUid                     eq,pres,sub
#index nisMapName,nisMapEntry            eq,pres,sub

now some concepts (don't rely on me for concepts), these are my 2cents notes on understanding this issue

example of an entry for a user (see how it has DN object to identify it and the a set of attributes for details)


dn: uid=john,ou=people,dc=example,dc=com
cn: John Doe
uid: john
uidNumber: 1001
gidNumber: 100
homeDirectory: /home/john
loginShell: /bin/bash
objectClass: top
objectClass: posixAccount

now the most important thing to do thetrick is to figure out what to put in that ldif file (that took a lot of time and reading, lucky for you i did my homework and you can bypass that time, i hope) here is my sample file


dn: dc=lab, dc=local
objectClass: top
objectclass: organization
objectclass: dcObject
o: Opencraft labs
dc: lab

dn: ou=users,dc=lab, dc=local
ou: users
objectClass: top
objectClass: organizationalUnit

dn: ou=groups,dc=lab, dc=local
ou: groups
objectClass: top
objectClass: organizationalUnit

dn: cn=engineering, ou=groups, dc=lab, dc=local
objectclass: top
objectclass: posixGroup
cn: engineering
gidnumber: 500
memberuid: foo

dn: cn=ramez hanna,ou=users,dc=lab, dc=local
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: ramez
userpassword: {crypt}
uidnumber: 500
gidnumber: 500
gecos:ramez zoheir hanna
loginShell:/bin/bash
homeDirectory: /home/rhanna
shadowLastChange:10877
shadowMin: 0
shadowMax: 999999
shadowWarning: 7
shadowInactive: -1
shadowExpire: -1
shadowFlag: 0
cn: ramez hanna
givenname: ramez
sn: hanna       
mail: me@domain.com
title: CEO
StreetAddress: somewhere 
l: cairo
postalCode: huh
telephoneNumber: 54545454
homephone: 454545454
mobile: 555555555
facsimileTelephoneNumber: 555555

references : scripts:


srand (time());
my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
my $plaintext = <STDIN>;
my $crypttext = "{CRYPT}".crypt ($plaintext, $salt);

#!/usr/bin/perl

use Net::LDAP;

# configuration section
# edit the next lines to meet your environment
# 
$server = "ldap.opencraft.local";
$suffix = "dc=opencraft,dc=local";
$rootdn = "cn=admin,$suffix"; #if not present the script will ask for it
$ou = "ou=People";
$gou = "ou=Group";
$homeDir = "/home/";
# end of configuration

$action = shift;
if ($action eq "useradd") {
	$username = shift;
	$options = shift;
	$groupname = $username;
	$_ = $options;
	if (/^-g/) {
		$groupname = $';
	}		
	ifexist($username,"user","exit");
	ifexist($groupname,"group","exit");
	adduser();
	addgroup($groupname,"auto");
}
elsif ($action eq "groupadd") {
	$group = shift;
	ifexist($group,"group","exit");
	$idNumber = generate_uid_num();
	addgroup($group,"user");
}
elsif ($action eq "passwd") {
	$username = shift;
	print "changing password for user $username";
	ifexist($username,"user","continue");
	`ldappasswd -x -v -S -w secret -Dcn=admin,dc=opencraft,dc=local uid=$username,ou=People,dc=opencraft,dc=local`;
}
elsif ($action eq "usermod") {
	$username = shift;
	$changes = shift;
	ifexist($username,"user","continue");
	usermod();
}
else {
	die "you have to specify an action";
}

################################ sub routines ##############################
#
# connect to ldap server
sub ldapconnect {
	$ldap = Net::LDAP->new( $server, version => 3 ) or die "new failed : $!";
	$ldap->bind( $rootdn,password => 'secret') or die "cannot bind :$!";
}
# disconnect from the server 
sub ldapdisconnect {
	$ldap->unbind;
}
# add a user 
sub adduser {
	open(TMP,">/tmp/ldapuser.tmp") || die "cannot cache data: $1";
	open(TEMPLATE,"< adduser.ldif") || die "cannot read template file: $!";
	while (<TEMPLATE>) {
           if (!/^#/) {
		@field = split(/:/,$_);
		if ($field[1,1] eq "") {
			if ($field[1,0] eq "dn") {
				$field[1,1] = "uid=$username,$ou,$suffix\n";
			}
			elsif ($field[1,0] eq "uid") {
				$field[1,1] = $username."\n";
			}
			elsif ($field[1,0] eq "userPassword") {
				print "enter value of $field[1,0]-$field[1,2]";
				$crypted = generate_pass();
				$field[1,1] = $crypted."\n";
                        }
			elsif ($field[1,0] eq "uidNumber") {
				$idNumber = generate_uid_num();
				$field[1,1] = $idNumber."\n";
                        }
			elsif ($field[1,0] eq "gidNumber") {
                                $field[1,1] = $idNumber."\n";
                        }
			elsif ($field[1,0] eq "homeDirectory") {
				$field[1,1] = $homeDir.$username."\n";
                        }
			elsif ($field[1,0] eq "cn") {
				print "enter value of $field[1,0]-$field[1,2]";
                                my $name = <STDIN>;
                                $name =~ s/\n//;
				$field[1,1] = $name."\n";
                        }
			else {
				print "enter value of $field[1,0]-$field[1,2]";
				$field[1,1] = <STDIN>;
			}
		}
		print TMP "$field[1,0]:$field[1,1]";
	   }
	}
	close (TMP);
	print "adding user $username to LDAP . . .";
	`ldapadd -x -w secret -D"$rootdn" -f /tmp/ldapuser.tmp`;
	print "user added\n";
	print "creating home folder . . .";
	system ("cp -Rfa /etc/skel/ $homeDir$username & chown -R $username:$username $homeDir$username");
}
# encrypt the password using {crypt}
sub generate_pass {
        srand (time());
	my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
	my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
	my $plaintext = <STDIN>;
	my $crypttext = "{CRYPT}".crypt ($plaintext, $salt);
	return $crypttext;
}
# get the latest uid/gid number to get a new uid for the user or the group
sub generate_uid_num {
	ldapconnect();
	$searchbase = $suffix;
	my $filter = "!(uidNumber=65534)";
	my $attrs = "";
	my $results = $ldap->search(base=>$searchbase,filter=>$filter,attrs=>$attrs);
	my $count = $results->count;
	my $entry;
	my $list;
	for (my $i=0; $i<$count; $i++) {
	        $entry = $results->entry($i);
		if (($entry->get_value('uidNumber')>499  and $entry->get_value('uidNumber')<65534)or($entry->get_value('gidNumber')>499 and $entry->get_value('gidNumber')<65534)) {
                $list = $list.",".$entry->get_value('uidNumber').",".$entry->get_value('gidNumber');
        	}
	}
	ldapdisconnect();
	my @uids = split(/,/,$list);
	@uids = sort { $a <=> $b } @uids;
	my $count = @uids;
	my $last = $uids[$count-1];
	my $newuid = $last+1;
	return $newuid;
}
# add a group
sub addgroup {
	my $name = @_[0];
	my $mode = @_[1];
	open(TMP2,">/tmp/ldapgroup.tmp") || die "cannot cache data: $1";
	open(TEMPLATE2,"< addgroup.ldif") || die "cannot read template file: $!";
	while (<TEMPLATE2>) {
           if (!/^#/) {
		@field = split(/:/,$_);
		if ($field[1,1] eq "") {
			if ($field[1,0] eq "dn") {
				$field[1,1] = "cn=$name,$gou,$suffix\n";
				print TMP2 "$field[1,0]:$field[1,1]";
			}
			elsif ($field[1,0] eq "gidNumber") {
                                $field[1,1] = $idNumber."\n";
				print TMP2 "$field[1,0]:$field[1,1]";
                        }
			elsif ($field[1,0] eq "cn") {
				$field[1,1] = $name."\n";
				print TMP2 "$field[1,0]:$field[1,1]";
                        }
			elsif ($field[1,0] eq "memberUid") {
				if ($mode eq "user") {
					print "enter value of $field[1,0]-$field[1,2]";
					my $input = <STDIN>;
					chomp ($input);
					my @members = split(/,/,$input);
					ldapconnect();
					foreach $member (@members) {	
					        $searchbase = $ou.",".$suffix;
					        my $filter = "uid=$member";
					        my $attrs = "uidNumber";
					        my $results = $ldap->search(base=>$searchbase,filter=>$filter,attrs=>$attrs);
						my $entry = $results->entry(0);
						my $memberUid = $entry->get_value('uidNumber');
						$field[1,1] ="$memberUid\n";
						print TMP2 "$field[1,0]:$field[1,1]";
					}
				ldapdisconnect();
				}
				elsif ($mode eq "auto") {
					$field[1,1] = $idNumber;
					print TMP2 "$field[1,0]:$field[1,1]";
				}
			}
		}
		else { print TMP2 "$field[1,0]:$field[1,1]"; }
	   }
	}
	close (TMP);
	print "adding group $group to LDAP . . .";
	`ldapadd -x -w secret -D"$rootdn" -f /tmp/ldapgroup.tmp`;
	print "group added\n";
}
# modify the users attributes
sub usermod {
	my %ReplaceHash = ( keyword => "x", proxy => "x" );
        my $result = LDAPmodifyUsingHash ( $ldap, $dn, \%ReplaceHash );
        my $result = $ldap->modify ( $dn,replace => { %$whatToChange } );
        return $result;
}
# check if user/group exists
sub ifexist {
	ldapconnect();
        my ($attrib,$entity,$action) = @_;
	my $searchbas,$filter,$attrs;
	if ($entity eq "group") {
		$searchbase = $gou.",".$suffix;
		$filter = "cn=$attrib";
		$attrs = "gidNumber";
	}
	else {
		$searchbase = $ou.",".$suffix;
		$filter = "uid=$attrib";
		$attrs = "uidNumber";
	}
        my $results = $ldap->search(base=>$searchbase,filter=>$filter,attrs=>$attrs);
	my $count = $results->count;
	if ($count > 0) {
		if ($action eq "exit") {
			print STDOUT "dependency failure ( user/group exists\nin case you are adding new user you can use -g with useradd to assign another default group)\n";
			exit;
		}
	}
	ldapdisconnect();
}

#adduser.ldif the template for new users
#this is the template for creating users
#syntax:
#attr:value:comment
#only missing values will be prompted by the scrip
#note that even if you won't have comments you need to add the ":"
dn::identifier usually the full name or the username
objectClass:top
objectClass:person
objectClass:posixAccount
objectClass:shadowAccount
objectClass:organizationalPerson
objectClass:inetOrgPerson
uid::username
userPassword::password not less than 5 char
uidNumber::
gidNumber::
gecos::more information divided by commas
loginShell:/bin/bash
homeDirectory::home folder
shadowLastChange:10877
shadowMin:0
shadowMax:999999
shadowWarning:7
shadowInactive:-1
shadowExpire:-1
shadowFlag:0
cn::
givenname::surname
sn::full name
mail::user's email
title::job title
StreetAddress::home address
l::city
telephoneNumber::home phone number
mobile::mobile number