nss_wrapper: fix segfault in nwrap_gr_copy_r()
[ira/wip.git] / lib / nss_wrapper / nss_wrapper.pl
1 #!/usr/bin/perl
2 #
3
4 use strict;
5
6 use Getopt::Long;
7 use Cwd qw(abs_path);
8
9 my $opt_help = 0;
10 my $opt_passwd_path = undef;
11 my $opt_group_path = undef;
12 my $opt_action = undef;
13 my $opt_type = undef;
14 my $opt_name = undef;
15 my $opt_member = undef;
16
17 my $passwdfn = undef;
18 my $groupfn = undef;
19 my $memberfn = undef;
20 my $actionfn = undef;
21
22 sub passwd_add($$$$);
23 sub passwd_delete($$$$);
24 sub group_add($$$$);
25 sub group_delete($$$$);
26 sub member_add($$$$);
27 sub member_delete($$$$);
28
29 sub check_path($$);
30
31 my $result = GetOptions(
32         'help|h|?'      => \$opt_help,
33         'passwd_path=s' => \$opt_passwd_path,
34         'group_path=s'  => \$opt_group_path,
35         'action=s'      => \$opt_action,
36         'type=s'        => \$opt_type,
37         'name=s'        => \$opt_name,
38         'member=s'      => \$opt_member
39 );
40
41 sub usage($;$)
42 {
43         my ($ret, $msg) = @_;
44
45         print $msg."\n\n" if defined($msg);
46
47         print "usage:
48
49         --help|-h|-?            Show this help.
50
51         --passwd_path <path>    Path of the 'passwd' file.
52         --group_path <path>     Path of the 'group' file.
53
54         --type <type>           'passwd', 'group' and 'member' are supported.
55
56         --action <action>       'add' or 'delete'.
57
58         --name <name>           The name of the object.
59
60         --member <member>       The name of the member.
61 ";
62         exit($ret);
63 }
64
65 usage(1) if (not $result);
66
67 usage(0) if ($opt_help);
68
69 if (not defined($opt_action)) {
70         usage(1, "missing: --action [add|delete]");
71 }
72 if ($opt_action eq "add") {
73         $passwdfn = \&passwd_add;
74         $groupfn = \&group_add;
75         $memberfn = \&member_add;
76 } elsif ($opt_action eq "delete") {
77         $passwdfn = \&passwd_delete;
78         $groupfn = \&group_delete;
79         $memberfn = \&member_delete;
80 } else {
81         usage(1, "invalid: --action [add|delete]: '$opt_action'");
82 }
83
84 if (not defined($opt_type)) {
85         usage(1, "missing: --type [passwd|group|member]");
86 }
87 if ($opt_type eq "member" and not defined($opt_member)) {
88         usage(1, "missing: --member <member>");
89 }
90 my $opt_fullpath_passwd;
91 my $opt_fullpath_group;
92 if ($opt_type eq "passwd") {
93         $actionfn = $passwdfn;
94         $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
95 } elsif ($opt_type eq "group") {
96         $actionfn = $groupfn;
97         $opt_fullpath_group = check_path($opt_group_path, $opt_type);
98 } elsif ($opt_type eq "member") {
99         $actionfn = $memberfn;
100         $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
101         $opt_fullpath_group = check_path($opt_group_path, "group");
102 } else {
103         usage(1, "invalid: --type [passwd|group]: '$opt_type'")
104 }
105
106 if (not defined($opt_name)) {
107         usage(1, "missing: --name <name>");
108 }
109 if ($opt_name eq "") {
110         usage(1, "invalid: --name <name>");
111 }
112
113 exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name);
114
115 sub check_path($$)
116 {
117         my ($path,$type) = @_;
118
119         if (not defined($path)) {
120                 usage(1, "missing: --$type\_path <path>");
121         }
122         if ($path eq "" or $path eq "/") {
123                 usage(1, "invalid: --$type\_path <path>: '$path'");
124         }
125         my $fullpath = abs_path($path);
126         if (not defined($fullpath)) {
127                 usage(1, "invalid: --$type\_path <path>: '$path'");
128         }
129         return $fullpath;
130 }
131
132 sub passwd_add_entry($$);
133
134 sub passwd_load($)
135 {
136         my ($path) = @_;
137         my @lines;
138         my $passwd = undef;
139
140         open(PWD, "<$path") or die("Unable to open '$path' for read");
141         @lines = <PWD>;
142         close(PWD);
143
144         $passwd->{array} = ();
145         $passwd->{name} = {};
146         $passwd->{uid} = {};
147         $passwd->{path} = $path;
148
149         foreach my $line (@lines) {
150                 passwd_add_entry($passwd, $line);
151         }
152
153         return $passwd;
154 }
155
156 sub group_add_entry($$);
157
158 sub group_load($)
159 {
160         my ($path) = @_;
161         my @lines;
162         my $group = undef;
163
164         open(GROUP, "<$path") or die("Unable to open '$path' for read");
165         @lines = <GROUP>;
166         close(GROUP);
167
168         $group->{array} = ();
169         $group->{name} = {};
170         $group->{gid} = {};
171         $group->{path} = $path;
172
173         foreach my $line (@lines) {
174                 group_add_entry($group, $line);
175         }
176
177         return $group;
178 }
179
180 sub passwd_lookup_name($$)
181 {
182         my ($passwd, $name) = @_;
183
184         return undef unless defined($passwd->{name}{$name});
185
186         return $passwd->{name}{$name};
187 }
188
189 sub group_lookup_name($$)
190 {
191         my ($group, $name) = @_;
192
193         return undef unless defined($group->{name}{$name});
194
195         return $group->{name}{$name};
196 }
197
198 sub passwd_lookup_uid($$)
199 {
200         my ($passwd, $uid) = @_;
201
202         return undef unless defined($passwd->{uid}{$uid});
203
204         return $passwd->{uid}{$uid};
205 }
206
207 sub group_lookup_gid($$)
208 {
209         my ($group, $gid) = @_;
210
211         return undef unless defined($group->{gid}{$gid});
212
213         return $group->{gid}{$gid};
214 }
215
216 sub passwd_get_free_uid($)
217 {
218         my ($passwd) = @_;
219         my $uid = 1000;
220
221         while (passwd_lookup_uid($passwd, $uid)) {
222                 $uid++;
223         }
224
225         return $uid;
226 }
227
228 sub group_get_free_gid($)
229 {
230         my ($group) = @_;
231         my $gid = 1000;
232
233         while (group_lookup_gid($group, $gid)) {
234                 $gid++;
235         }
236
237         return $gid;
238 }
239
240 sub passwd_add_entry($$)
241 {
242         my ($passwd, $str) = @_;
243
244         chomp $str;
245         my @e = split(':', $str);
246
247         push(@{$passwd->{array}}, \@e);
248         $passwd->{name}{$e[0]} = \@e;
249         $passwd->{uid}{$e[2]} = \@e;
250 }
251
252 sub group_add_entry($$)
253 {
254         my ($group, $str) = @_;
255
256         chomp $str;
257         my @e = split(':', $str);
258
259         push(@{$group->{array}}, \@e);
260         $group->{name}{$e[0]} = \@e;
261         $group->{gid}{$e[2]} = \@e;
262 }
263
264 sub passwd_remove_entry($$)
265 {
266         my ($passwd, $eref) = @_;
267
268         for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
269                 if ($eref == $passwd->{array}[$i]) {
270                         $passwd->{array}[$i] = undef;
271                 }
272         }
273
274         delete $passwd->{name}{${$eref}[0]};
275         delete $passwd->{uid}{${$eref}[2]};
276 }
277
278 sub group_remove_entry($$)
279 {
280         my ($group, $eref) = @_;
281
282         for (my $i = 0; defined($group->{array}[$i]); $i++) {
283                 if ($eref == $group->{array}[$i]) {
284                         $group->{array}[$i] = undef;
285                 }
286         }
287
288         delete $group->{name}{${$eref}[0]};
289         delete $group->{gid}{${$eref}[2]};
290 }
291
292 sub group_add_member($$$)
293 {
294         my ($group, $eref, $username) = @_;
295
296         my @members;
297         my $str = @$eref[3] || undef;
298         if ($str) {
299                 @members = split(",", $str);
300         }
301
302         foreach my $member (@members) {
303                 if ($member and $member eq $username) {
304                         die("account[$username] is already member of '@$eref[0]'");
305                 }
306         }
307
308         push(@members, $username);
309
310         my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
311
312         group_remove_entry($group, $eref);
313
314         group_add_entry($group, $gwent);
315 }
316
317 sub group_delete_member($$$)
318 {
319         my ($group, $eref, $username) = @_;
320
321         my @members = undef;
322         my $str = @$eref[3] || undef;
323         if ($str) {
324                 @members = split(",", $str);
325         }
326         my @new_members;
327         my $removed = 0;
328
329         foreach my $member (@members) {
330                 if ($member and $member ne $username) {
331                         push(@new_members, $member);
332                 } else {
333                         $removed = 1;
334                 }
335         }
336
337         if ($removed != 1) {
338                 die("account[$username] is not member of '@$eref[0]'");
339         }
340
341         my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
342
343         group_remove_entry($group, $eref);
344
345         group_add_entry($group, $gwent);
346 }
347
348 sub passwd_save($)
349 {
350         my ($passwd) = @_;
351         my @lines = ();
352         my $path = $passwd->{path};
353         my $tmppath = $path.$$;
354
355         foreach my $eref (@{$passwd->{array}}) {
356                 next unless defined($eref);
357
358                 my $line = join(':', @{$eref});
359                 push(@lines, $line);
360         }
361
362         open(PWD, ">$tmppath") or die("Unable to open '$tmppath' for write");
363         print PWD join("\n", @lines)."\n";
364         close(PWD);
365         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
366 }
367
368 sub group_save($)
369 {
370         my ($group) = @_;
371         my @lines = ();
372         my $path = $group->{path};
373         my $tmppath = $path.$$;
374
375         foreach my $eref (@{$group->{array}}) {
376                 next unless defined($eref);
377
378                 my $line = join(':', @{$eref});
379                 if (scalar(@{$eref}) == 3) {
380                         $line .= ":";
381                 }
382                 push(@lines, $line);
383         }
384
385         open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
386         print GROUP join("\n", @lines)."\n";
387         close(GROUP);
388         rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
389 }
390
391 sub passwd_add($$$$)
392 {
393         my ($path, $dummy, $dummy2, $name) = @_;
394
395         #print "passwd_add: '$name' in '$path'\n";
396
397         my $passwd = passwd_load($path);
398
399         my $e = passwd_lookup_name($passwd, $name);
400         die("account[$name] already exists in '$path'") if defined($e);
401
402         my $uid = passwd_get_free_uid($passwd);
403         my $gid = 65534;# nogroup gid
404
405         my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
406
407         passwd_add_entry($passwd, $pwent);
408
409         passwd_save($passwd);
410
411         return 0;
412 }
413
414 sub passwd_delete($$$$)
415 {
416         my ($path, $dummy, $dummy2, $name) = @_;
417
418         #print "passwd_delete: '$name' in '$path'\n";
419
420         my $passwd = passwd_load($path);
421
422         my $e = passwd_lookup_name($passwd, $name);
423         die("account[$name] does not exists in '$path'") unless defined($e);
424
425         passwd_remove_entry($passwd, $e);
426
427         passwd_save($passwd);
428
429         return 0;
430 }
431
432 sub group_add($$$$)
433 {
434         my ($dummy, $dummy2, $path, $name) = @_;
435
436         #print "group_add: '$name' in '$path'\n";
437
438         my $group = group_load($path);
439
440         my $e = group_lookup_name($group, $name);
441         die("group[$name] already exists in '$path'") if defined($e);
442
443         my $gid = group_get_free_gid($group);
444
445         my $gwent = $name.":x:".$gid.":"."";
446
447         group_add_entry($group, $gwent);
448
449         group_save($group);
450
451         #printf("%d\n", $gid);
452
453         return 0;
454 }
455
456 sub group_delete($$$$)
457 {
458         my ($dummy, $dummy2, $path, $name) = @_;
459
460         #print "group_delete: '$name' in '$path'\n";
461
462         my $group = group_load($path);
463
464         my $e = group_lookup_name($group, $name);
465         die("group[$name] does not exists in '$path'") unless defined($e);
466
467         group_remove_entry($group, $e);
468
469         group_save($group);
470
471         return 0;
472 }
473
474 sub member_add($$$$)
475 {
476         my ($passwd_path, $username, $group_path, $groupname) = @_;
477
478         #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
479
480         my $group = group_load($group_path);
481
482         my $g = group_lookup_name($group, $groupname);
483         die("group[$groupname] does not exists in '$group_path'") unless defined($g);
484
485         my $passwd = passwd_load($passwd_path);
486
487         my $u = passwd_lookup_name($passwd, $username);
488         die("account[$username] does not exists in '$passwd_path'") unless defined($u);
489
490         group_add_member($group, $g, $username);
491
492         group_save($group);
493
494         return 0;
495 }
496
497 sub member_delete($$$$)
498 {
499         my ($passwd_path, $username, $group_path, $groupname) = @_;
500
501         #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
502
503         my $group = group_load($group_path);
504
505         my $g = group_lookup_name($group, $groupname);
506         die("group[$groupname] does not exists in '$group_path'") unless defined($g);
507
508         my $passwd = passwd_load($passwd_path);
509
510         my $u = passwd_lookup_name($passwd, $username);
511         die("account[$username] does not exists in '$passwd_path'") unless defined($u);
512
513         group_delete_member($group, $g, $username);
514
515         group_save($group);
516
517         return 0;
518 }