1: #!/usr/bin/perl -w
2: # by Steve Haslam
3:
4: # description:
5:
6: # adds a user into an LDAP group- where "users" and "groups" are the
7: # objects seen by nss_ldap.
8:
9: # would be nice to be able to use pam-ldap here, but I've not got
10: # that to work yet.
11:
12: # notes:
13: # at least on my machine, Term::Readkey gives loads of "use of
14: # uninitialized value" errors. ymmv.
15:
16: # illustrates lots of things, and a resonably nice showcase for some
17: # techniques for working with Net::LDAP (e.g. the ldapassert() sub)
18:
19: require 5;
20: use strict;
21: use Net::LDAP;
22: use Net::LDAP::Util qw(ldap_error_text);
23: use Term::ReadKey;
24: use Getopt::Std;
25: use vars qw($hostname $basedn $ldap $sr $username $groupname $userdn $groupdn $entry $admindn $adminpw $verbose %opts);
26:
27: getopts('h:D:b:v', \%opts) && @ARGV == 2 or die "Syntax: $0 [-v] [-h hostname] [-D binddn] [-b basedn] username groupname";
28:
29: ($username, $groupname) = (lc($ARGV[0]), lc($ARGV[1]));
30:
31: $verbose = 1 if ($opts{'v'});
32:
33: # Print something if the -v switch was given
34: sub printv {
35: print(@_) if ($verbose);
36: }
37:
38: # Wrapper for a Net::LDAP call- assert the server message is a "success"
39: # code- die with a decoded error message if it isn't
40: sub ldapassert {
41: my $mesg = shift;
42: my $op = shift;
43: $mesg->code && die "LDAP error".($op?" during $op":"").": ".ldap_error_text($mesg->code)."\n";
44: $mesg;
45: }
46:
47: # Extract a configuration option from the nss-ldap configuration file
48: # /etc/libnss-ldap.conf is the Debian conf file location
49: # /etc/ldap.conf is an alternative plausible location
50: sub confoption {
51: my $optname = lc(shift);
52: my $conffile;
53:
54: foreach $conffile (qw|/etc/libnss-ldap.conf /etc/ldap.conf|) {
55: if (-f $conffile) {
56: open(LDAPCONF, $conffile) or die "Unable to open nss-ldap configuration file $conffile: $!\n";
57: while (<LDAPCONF>) {
58: s/\#.*//;
59: chomp;
60: my($keyword, $value) = split(/ +/, $_, 2);
61: next unless (defined($keyword) && defined($value));
62: $keyword = lc($keyword);
63: if ($keyword eq $optname) {
64: close(LDAPCONF);
65: printv "[ldapconf $conffile] using \"$value\" for \"$optname\"\n";
66: return $value;
67: }
68: }
69: return undef;
70: }
71: }
72:
73: printv "[ldapconf] no value for \"$optname\"\n";
74:
75: return undef;
76: }
77:
78: $hostname = $opts{'h'} || confoption('host');
79: $basedn = $opts{'b'} || confoption('base');
80:
81: $ldap = Net::LDAP->new($hostname) or die "$@";
82:
83: # Bind as administrator user
84: $admindn = $opts{'D'} || confoption('binddn');
85:
86: # Get admin password iff a bind dn was specified
87: if ($admindn) {
88: print "LDAP password: ";
89: ReadMode('noecho');
90: $adminpw = ReadLine;
91: chomp($adminpw);
92: ReadMode(0);
93: print "\n";
94: }
95:
96: # Perform bind
97: # bind anonymously if no pw given
98: if ($adminpw) {
99: printv "Binding as $admindn\n";
100: ldapassert($ldap->bind(dn => $admindn, password => $adminpw), "bind");
101: }
102: else {
103: printv "Binding anonymously\n";
104: ldapassert($ldap->bind, "anonymous bind");
105: }
106:
107: # Find the user- get the user dn
108: $sr = ldapassert($ldap->search(base => "ou=People, $basedn", filter => "(&(objectClass=posixAccount)(uid=$username))"), "user search");
109:
110: if ($sr->count == 0) {
111: die "Unknown user '$username'\n";
112: }
113: elsif ($sr->count > 1) {
114: die "Ambiguous user '$username' (this is really bad)\n";
115: }
116:
117: $entry = $sr->shift_entry;
118: $userdn = $entry->dn;
119:
120: # Find the group- get the group dn
121: $sr = ldapassert($ldap->search(base => "ou=Group, $basedn", filter => "(&(objectClass=posixGroup)(cn=$groupname))"), "group search");
122:
123: if ($sr->count == 0) {
124: die "Unknown group '$groupname'\n";
125: }
126: elsif ($sr->count > 1) {
127: die "Ambiguous group '$groupname' (this is really bad)\n";
128: }
129:
130: $entry = $sr->shift_entry;
131: $groupdn = $entry->dn;
132:
133: # Is the user already in the group?
134: foreach (@{$entry->get('memberuid')}) {
135: if (lc($_) eq lc($username)) {
136: print "$username is already a member of $groupname\n";
137: exit(0);
138: }
139: }
140:
141: # OK, now update the group entry
142: # $entry is the group entry
143: printv "Adding [$userdn] to [$groupdn]\n";
144:
145: $entry->add(memberuid => $username);
146:
147: ldapassert($entry->update($ldap), "update"); # Write updated entry to directory server
148:
149: exit(0);