1#!/usr/bin/perl -w
2# Guenther Deschner <gd@samba.org>
3#
4# check for multiple LDAP entries
5
6use strict;
7
8use Net::LDAP;
9use Getopt::Std;
10
11my %opts;
12
13if (!@ARGV) {
14	print "usage: $0 -h host -b base -D admindn -w password [-l]\n";
15	print "\tperforms checks for multiple sid, uid and gid-entries on your LDAP server\n";
16	print "\t-l adds additional checks against the local /etc/passwd and /etc/group file\n";
17	exit 1;
18}
19
20getopts('b:h:D:w:l', \%opts);
21
22my $host =   $opts{h}	|| "localhost";
23my $suffix = $opts{b}	|| die "please set base with -b";
24my $binddn = $opts{D}	|| die "please set basedn with -D";
25my $bindpw = $opts{w}	|| die "please set password with -w";
26my $check_local_files = $opts{l} || 0;
27
28########################
29
30
31my ($ldap, $res);
32my (%passwd_h, %group_h);
33my $bad_uids = 0;
34my $bad_gids = 0;
35my $bad_sids = 0;
36my $ret = 0;
37
38if ($check_local_files) {
39	my @uids = `cut -d ':' -f 3 /etc/passwd`;
40	my @gids = `cut -d ':' -f 3 /etc/group`;
41
42	foreach my $uid (@uids) {
43		chomp($uid);
44		$passwd_h{$uid} = $uid;
45	}
46
47	foreach my $gid (@gids) {
48		chomp($gid);
49		$group_h{$gid} = $gid;
50	}
51}
52
53########
54# bind #
55########
56
57$ldap = Net::LDAP->new($host, version => '3');
58
59$res = $ldap->bind( $binddn, password => $bindpw);
60$res->code && die "failed to bind: ", $res->error;
61
62
63
64###########################
65# check for double sids   #
66###########################
67
68print "\ntesting for multiple sambaSids\n";
69
70$res = $ldap->search(
71	base => $suffix,
72	filter => "(objectclass=sambaSamAccount)");
73
74$res->code && die "failed to search: ", $res->error;
75
76foreach my $entry ($res->all_entries) {
77
78	my $sid = $entry->get_value('sambaSid');
79
80	my $local_res = $ldap->search(
81		base => $suffix,
82		filter => "(&(objectclass=sambaSamAccount)(sambaSid=$sid))");
83
84	$local_res->code && die "failed to search: ", $local_res->error;
85	if ($local_res->count > 1) {
86		print "A SambaSamAccount with sambaSid [$sid] must exactly exist once\n";
87		print "You have ", $local_res->count, " entries:\n";
88		foreach my $loc_entry ($local_res->all_entries) {
89			printf "\t%s\n", $loc_entry->dn;
90		}
91		++$bad_sids;
92	}
93}
94
95if ($bad_sids) {
96	$ret = -1;
97	print "You have $bad_sids bad sambaSids in your system. You might need to repair them\n";
98} else {
99	print "No multiple sambaSids found in your system\n";
100}
101
102print "-" x 80, "\n";
103
104###########################
105# check for double groups #
106###########################
107
108print "\ntesting for multiple gidNumbers\n";
109
110$res = $ldap->search(
111	base => $suffix,
112	filter => "(objectclass=posixGroup)");
113
114$res->code && die "failed to search: ", $res->error;
115
116foreach my $entry ($res->all_entries) {
117
118	my $gid = $entry->get_value('gidNumber');
119	my $dn  = $entry->dn;
120
121	my $local_res = $ldap->search(
122		base => $suffix,
123		filter => "(&(objectclass=posixGroup)(gidNumber=$gid))");
124
125	$local_res->code && die "failed to search: ", $local_res->error;
126	if ($local_res->count > 1) {
127		print "A PosixGroup with gidNumber [$gid] must exactly exist once\n";
128		print "You have ", $local_res->count, " entries:\n";
129		foreach my $loc_entry ($local_res->all_entries) {
130			printf "\t%s\n", $loc_entry->dn;
131		}
132		++$bad_gids;
133		next;
134	}
135
136	if ($check_local_files && exists $group_h{$gid}) {
137		print "Warning: There is a group in /etc/group that has gidNumber [$gid] as well\n";
138		print "This entry may conflict with $dn\n";
139		++$bad_gids;
140	}
141}
142
143if ($bad_gids) {
144	$ret = -1;
145	print "You have $bad_gids bad gidNumbers in your system. You might need to repair them\n";
146} else {
147	print "No multiple gidNumbers found in your system\n";
148}
149
150print "-" x 80, "\n";
151
152
153###########################
154# check for double users  #
155###########################
156
157print "\ntesting for multiple uidNumbers\n";
158
159$res = $ldap->search(
160	base => $suffix,
161	filter => "(objectclass=posixAccount)");
162
163$res->code && die "failed to search: ", $res->error;
164
165
166foreach my $entry ($res->all_entries) {
167
168	my $uid = $entry->get_value('uidNumber');
169	my $dn  = $entry->dn;
170
171	my $local_res = $ldap->search(
172		base => $suffix,
173		filter => "(&(objectclass=posixAccount)(uidNumber=$uid))");
174
175	$local_res->code && die "failed to search: ", $local_res->error;
176	if ($local_res->count > 1) {
177		print "A PosixAccount with uidNumber [$uid] must exactly exist once\n";
178		print "You have ", $local_res->count, " entries:\n";
179		foreach my $loc_entry ($local_res->all_entries) {
180			printf "\t%s\n", $loc_entry->dn;
181		}
182		++$bad_uids;
183		next;
184	}
185	if ($check_local_files && exists $passwd_h{$uid}) {
186		print "Warning: There is a user in /etc/passwd that has uidNumber [$uid] as well\n";
187		print "This entry may conflict with $dn\n";
188		++$bad_uids;
189	}
190}
191
192if ($bad_uids) {
193	$ret = -1;
194	print "You have $bad_uids bad uidNumbers in your system. You might need to repair them\n";
195} else {
196	print "No multiple uidNumbers found in your system\n";
197}
198
199$ldap->unbind;
200
201exit $ret;
202