ndr: Add support for pulling/printing an ipv6address type
[samba.git] / lib / nss_wrapper / nss_wrapper.pl
index b1c9be5365bc37234a782c2794d6d98119ed055f..48fa2c52b8f6e739a788a9ce13b58c5835f2fc01 100644 (file)
@@ -7,26 +7,37 @@ use Getopt::Long;
 use Cwd qw(abs_path);
 
 my $opt_help = 0;
-my $opt_path = undef;
+my $opt_passwd_path = undef;
+my $opt_group_path = undef;
 my $opt_action = undef;
 my $opt_type = undef;
 my $opt_name = undef;
+my $opt_member = undef;
+my $opt_gid = 65534;# nogroup gid
 
 my $passwdfn = undef;
 my $groupfn = undef;
+my $memberfn = undef;
 my $actionfn = undef;
 
-sub passwd_add($$);
-sub passwd_delete($$);
-sub group_add($$);
-sub group_delete($$);
+sub passwd_add($$$$$);
+sub passwd_delete($$$$$);
+sub group_add($$$$$);
+sub group_delete($$$$$);
+sub member_add($$$$$);
+sub member_delete($$$$$);
+
+sub check_path($$);
 
 my $result = GetOptions(
        'help|h|?'      => \$opt_help,
-       'path=s'        => \$opt_path,
+       'passwd_path=s' => \$opt_passwd_path,
+       'group_path=s'  => \$opt_group_path,
        'action=s'      => \$opt_action,
        'type=s'        => \$opt_type,
-       'name=s'        => \$opt_name
+       'name=s'        => \$opt_name,
+       'member=s'      => \$opt_member,
+       'gid=i'         => \$opt_gid
 );
 
 sub usage($;$)
@@ -39,15 +50,18 @@ sub usage($;$)
 
        --help|-h|-?            Show this help.
 
-       --path <path>           Path of the 'passwd' or 'group' file.
+       --passwd_path <path>    Path of the 'passwd' file.
+       --group_path <path>     Path of the 'group' file.
 
-       --type <type>           Only 'passwd' is supported yet,
-                               but 'group' and maybe 'member' will be added
-                               in future.
+       --type <type>           'passwd', 'group' and 'member' are supported.
 
        --action <action>       'add' or 'delete'.
 
        --name <name>           The name of the object.
+
+       --member <member>       The name of the member.
+
+       --gid <gid>             Primary Group ID for new users.
 ";
        exit($ret);
 }
@@ -56,38 +70,39 @@ usage(1) if (not $result);
 
 usage(0) if ($opt_help);
 
-if (not defined($opt_path)) {
-       usage(1, "missing: --path <path>");
-}
-if ($opt_path eq "" or $opt_path eq "/") {
-       usage(1, "invalid: --path <path>: '$opt_path'");
-}
-my $opt_fullpath = abs_path($opt_path);
-if (not defined($opt_fullpath)) {
-       usage(1, "invalid: --path <path>: '$opt_path'");
-}
-
-
 if (not defined($opt_action)) {
        usage(1, "missing: --action [add|delete]");
 }
 if ($opt_action eq "add") {
        $passwdfn = \&passwd_add;
        $groupfn = \&group_add;
+       $memberfn = \&member_add;
 } elsif ($opt_action eq "delete") {
        $passwdfn = \&passwd_delete;
        $groupfn = \&group_delete;
+       $memberfn = \&member_delete;
 } else {
        usage(1, "invalid: --action [add|delete]: '$opt_action'");
 }
 
 if (not defined($opt_type)) {
-       usage(1, "missing: --type [passwd|group]");
+       usage(1, "missing: --type [passwd|group|member]");
+}
+if ($opt_type eq "member" and not defined($opt_member)) {
+       usage(1, "missing: --member <member>");
 }
+my $opt_fullpath_passwd;
+my $opt_fullpath_group;
 if ($opt_type eq "passwd") {
        $actionfn = $passwdfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, $opt_type);
 } elsif ($opt_type eq "group") {
        $actionfn = $groupfn;
+       $opt_fullpath_group = check_path($opt_group_path, $opt_type);
+} elsif ($opt_type eq "member") {
+       $actionfn = $memberfn;
+       $opt_fullpath_passwd = check_path($opt_passwd_path, "passwd");
+       $opt_fullpath_group = check_path($opt_group_path, "group");
 } else {
        usage(1, "invalid: --type [passwd|group]: '$opt_type'")
 }
@@ -99,7 +114,24 @@ if ($opt_name eq "") {
        usage(1, "invalid: --name <name>");
 }
 
-exit $actionfn->($opt_fullpath, $opt_name);
+exit $actionfn->($opt_fullpath_passwd, $opt_member, $opt_fullpath_group, $opt_name, $opt_gid);
+
+sub check_path($$)
+{
+       my ($path,$type) = @_;
+
+       if (not defined($path)) {
+               usage(1, "missing: --$type\_path <path>");
+       }
+       if ($path eq "" or $path eq "/") {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       my $fullpath = abs_path($path);
+       if (not defined($fullpath)) {
+               usage(1, "invalid: --$type\_path <path>: '$path'");
+       }
+       return $fullpath;
+}
 
 sub passwd_add_entry($$);
 
@@ -125,6 +157,30 @@ sub passwd_load($)
        return $passwd;
 }
 
+sub group_add_entry($$);
+
+sub group_load($)
+{
+       my ($path) = @_;
+       my @lines;
+       my $group = undef;
+
+       open(GROUP, "<$path") or die("Unable to open '$path' for read");
+       @lines = <GROUP>;
+       close(GROUP);
+
+       $group->{array} = ();
+       $group->{name} = {};
+       $group->{gid} = {};
+       $group->{path} = $path;
+
+       foreach my $line (@lines) {
+               group_add_entry($group, $line);
+       }
+
+       return $group;
+}
+
 sub passwd_lookup_name($$)
 {
        my ($passwd, $name) = @_;
@@ -134,6 +190,15 @@ sub passwd_lookup_name($$)
        return $passwd->{name}{$name};
 }
 
+sub group_lookup_name($$)
+{
+       my ($group, $name) = @_;
+
+       return undef unless defined($group->{name}{$name});
+
+       return $group->{name}{$name};
+}
+
 sub passwd_lookup_uid($$)
 {
        my ($passwd, $uid) = @_;
@@ -143,6 +208,15 @@ sub passwd_lookup_uid($$)
        return $passwd->{uid}{$uid};
 }
 
+sub group_lookup_gid($$)
+{
+       my ($group, $gid) = @_;
+
+       return undef unless defined($group->{gid}{$gid});
+
+       return $group->{gid}{$gid};
+}
+
 sub passwd_get_free_uid($)
 {
        my ($passwd) = @_;
@@ -155,6 +229,18 @@ sub passwd_get_free_uid($)
        return $uid;
 }
 
+sub group_get_free_gid($)
+{
+       my ($group) = @_;
+       my $gid = 1000;
+
+       while (group_lookup_gid($group, $gid)) {
+               $gid++;
+       }
+
+       return $gid;
+}
+
 sub passwd_add_entry($$)
 {
        my ($passwd, $str) = @_;
@@ -167,11 +253,23 @@ sub passwd_add_entry($$)
        $passwd->{uid}{$e[2]} = \@e;
 }
 
+sub group_add_entry($$)
+{
+       my ($group, $str) = @_;
+
+       chomp $str;
+       my @e = split(':', $str);
+
+       push(@{$group->{array}}, \@e);
+       $group->{name}{$e[0]} = \@e;
+       $group->{gid}{$e[2]} = \@e;
+}
+
 sub passwd_remove_entry($$)
 {
        my ($passwd, $eref) = @_;
 
-       for(my $i; defined($passwd->{array}[$i]); $i++) {
+       for (my $i = 0; defined($passwd->{array}[$i]); $i++) {
                if ($eref == $passwd->{array}[$i]) {
                        $passwd->{array}[$i] = undef;
                }
@@ -181,6 +279,76 @@ sub passwd_remove_entry($$)
        delete $passwd->{uid}{${$eref}[2]};
 }
 
+sub group_remove_entry($$)
+{
+       my ($group, $eref) = @_;
+
+       for (my $i = 0; defined($group->{array}[$i]); $i++) {
+               if ($eref == $group->{array}[$i]) {
+                       $group->{array}[$i] = undef;
+               }
+       }
+
+       delete $group->{name}{${$eref}[0]};
+       delete $group->{gid}{${$eref}[2]};
+}
+
+sub group_add_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+
+       foreach my $member (@members) {
+               if ($member and $member eq $username) {
+                       die("account[$username] is already member of '@$eref[0]'");
+               }
+       }
+
+       push(@members, $username);
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
+sub group_delete_member($$$)
+{
+       my ($group, $eref, $username) = @_;
+
+       my @members = undef;
+       my $str = @$eref[3] || undef;
+       if ($str) {
+               @members = split(",", $str);
+       }
+       my @new_members;
+       my $removed = 0;
+
+       foreach my $member (@members) {
+               if ($member and $member ne $username) {
+                       push(@new_members, $member);
+               } else {
+                       $removed = 1;
+               }
+       }
+
+       if ($removed != 1) {
+               die("account[$username] is not member of '@$eref[0]'");
+       }
+
+       my $gwent = @$eref[0].":x:".@$eref[2].":".join(",", @new_members);
+
+       group_remove_entry($group, $eref);
+
+       group_add_entry($group, $gwent);
+}
+
 sub passwd_save($)
 {
        my ($passwd) = @_;
@@ -201,9 +369,32 @@ sub passwd_save($)
        rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
 }
 
-sub passwd_add($$)
+sub group_save($)
+{
+       my ($group) = @_;
+       my @lines = ();
+       my $path = $group->{path};
+       my $tmppath = $path.$$;
+
+       foreach my $eref (@{$group->{array}}) {
+               next unless defined($eref);
+
+               my $line = join(':', @{$eref});
+               if (scalar(@{$eref}) == 3) {
+                       $line .= ":";
+               }
+               push(@lines, $line);
+       }
+
+       open(GROUP, ">$tmppath") or die("Unable to open '$tmppath' for write");
+       print GROUP join("\n", @lines)."\n";
+       close(GROUP);
+       rename($tmppath, $path) or die("Unable to rename $tmppath => $path");
+}
+
+sub passwd_add($$$$$)
 {
-       my ($path, $name) = @_;
+       my ($path, $dummy, $dummy2, $name, $gid) = @_;
 
        #print "passwd_add: '$name' in '$path'\n";
 
@@ -213,7 +404,6 @@ sub passwd_add($$)
        die("account[$name] already exists in '$path'") if defined($e);
 
        my $uid = passwd_get_free_uid($passwd);
-       my $gid = 65534;# nogroup gid
 
        my $pwent = $name.":x:".$uid.":".$gid.":".$name." gecos:/nodir:/bin/false";
 
@@ -224,9 +414,9 @@ sub passwd_add($$)
        return 0;
 }
 
-sub passwd_delete($$)
+sub passwd_delete($$$$$)
 {
-       my ($path, $name) = @_;
+       my ($path, $dummy, $dummy2, $name, $dummy3) = @_;
 
        #print "passwd_delete: '$name' in '$path'\n";
 
@@ -242,24 +432,90 @@ sub passwd_delete($$)
        return 0;
 }
 
-sub group_add($$)
+sub group_add($$$$$)
 {
-       my ($path, $name) = @_;
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
 
        #print "group_add: '$name' in '$path'\n";
 
-       die("group_add: not implemented yet!");
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] already exists in '$path'") if defined($e);
+
+       my $gid = group_get_free_gid($group);
+
+       my $gwent = $name.":x:".$gid.":"."";
+
+       group_add_entry($group, $gwent);
+
+       group_save($group);
+
+       #printf("%d\n", $gid);
 
        return 0;
 }
 
-sub group_delete($$)
+sub group_delete($$$$$)
 {
-       my ($path, $name) = @_;
+       my ($dummy, $dummy2, $path, $name, $dummy3) = @_;
 
        #print "group_delete: '$name' in '$path'\n";
 
-       die("group_delete: not implemented yet!");
+       my $group = group_load($path);
+
+       my $e = group_lookup_name($group, $name);
+       die("group[$name] does not exists in '$path'") unless defined($e);
+
+       group_remove_entry($group, $e);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_add($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_add: adding '$username' in '$passwd_path' to '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_add_member($group, $g, $username);
+
+       group_save($group);
+
+       return 0;
+}
+
+sub member_delete($$$$$)
+{
+       my ($passwd_path, $username, $group_path, $groupname, $dummy) = @_;
+
+       #print "member_delete: removing '$username' in '$passwd_path' from '$groupname' in '$group_path'\n";
+
+       my $group = group_load($group_path);
+
+       my $g = group_lookup_name($group, $groupname);
+       die("group[$groupname] does not exists in '$group_path'") unless defined($g);
+
+       my $passwd = passwd_load($passwd_path);
+
+       my $u = passwd_lookup_name($passwd, $username);
+       die("account[$username] does not exists in '$passwd_path'") unless defined($u);
+
+       group_delete_member($group, $g, $username);
+
+       group_save($group);
 
        return 0;
 }