diff options
Diffstat (limited to 'perl/Wallet')
| -rw-r--r-- | perl/Wallet/Config.pm | 59 | ||||
| -rw-r--r-- | perl/Wallet/Server.pm | 87 | 
2 files changed, 138 insertions, 8 deletions
| diff --git a/perl/Wallet/Config.pm b/perl/Wallet/Config.pm index 3bd2055..ad8070b 100644 --- a/perl/Wallet/Config.pm +++ b/perl/Wallet/Config.pm @@ -465,6 +465,65 @@ our $NETDB_REMCTL_PORT;  =back +=head1 DEFAULT OWNERS + +By default, only users in the ADMIN ACL can create new objects in the +wallet.  To allow other users to create new objects, define a Perl function +named default_owner.  This function will be called whenever a non-ADMIN user +tries to create a new object and will be passed the type and name of the +object.  It should return undef if there is no default owner for that +object.  If there is, it should return a list containing the name to use for +the ACL and then zero or more anonymous arrays of two elements each giving +the type and identifier for each ACL entry. + +For example, the following simple function says to use a default owner named +C<default> with one entry of type C<krb5> and identifier C<rra@example.com> +for the object with type C<keytab> and name C<host/example.com>: + +    sub default_owner { +        my ($type, $name) = @_; +        if ($type eq 'keytab' and $name eq 'host/example.com') { +            return ('default', [ 'krb5', 'rra@example.com' ]); +        } else { +            return; +        } +    } + +Of course, normally this function is used for more complex mappings.  Here +is a more complete example.  For objects of type keytab corresponding to +various types of per-machine principals, return a default owner that sets as +owner anyone with a NetDB role for that system and the system's host +principal.  This permits authorization management using NetDB while also +allowing the system to bootstrap itself once the host principal has been +downloaded and rekey itself using the old host principal. + +    sub default_owner { +        my ($type, $name) = @_; +        my %allowed = map { $_ => 1 } +            qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth); +        my $realm = 'example.com'; +        return unless $type eq 'keytab'; +        return unless $name =~ m%/%; +        my ($service, $instance) = split ('/', $name, 2); +        return unless $allowed{$service}; +        my $acl_name = "host/$instance"; +        my @acl = ([ 'netdb', $instance ], +                   [ 'krb5', "host/$instance\@$realm" ]); +        return ($acl_name, @acl); +    } + +The auto-created ACL used for the owner of the new object will, in the above +example, be named C<host/I<system>> where I<system> is the fully-qualified +name of the system as derived from the keytab being requested. + +If the name of the ACL returned by the default_owner function matches an ACL +that already exists in the wallet database, the existing ACL will be +compared to the default ACL returned by the default_owner function.  If the +existing ACL has the same entries as the one returned by default_owner, +creation continues if the user is authorized by that ACL.  If they don't +match, creation of the object is rejected, since the presence of an existing +ACL may indicate that something different is being done with this object. +  =cut  # Now, load the configuration file so that it can override the defaults. diff --git a/perl/Wallet/Server.pm b/perl/Wallet/Server.pm index 41072a8..bb1a90c 100644 --- a/perl/Wallet/Server.pm +++ b/perl/Wallet/Server.pm @@ -133,11 +133,73 @@ sub DESTROY {  # Object methods  ############################################################################## +# Given an object which doesn't currently exist, check whether a default_owner +# function is defined and, if so, if it returns an ACL for that object.  If +# so, create the ACL and check if the current user is authorized by that ACL. +# Returns true if so, false if not, setting the internal error as appropriate. +# +# This leaves those new ACLs in the database, which may not be the best +# behavior, but it's the simplest given the current Wallet::ACL API.  This +# should probably be revisited later. +sub create_check { +    my ($self, $type, $name) = @_; +    my $user = $self->{user}; +    my $host = $self->{host}; +    my $dbh = $self->{dbh}; +    unless (defined (&Wallet::Config::default_owner)) { +        $self->error ("$user not authorized to create ${type}:${name}"); +        return; +    } +    my ($aname, @acl) = Wallet::Config::default_owner ($type, $name); +    unless (defined $aname) { +        $self->error ("$user not authorized to create ${type}:${name}"); +        return; +    } +    my $acl = eval { Wallet::ACL->new ($aname, $dbh) }; +    if ($@) { +        $acl = eval { Wallet::ACL->create ($aname, $dbh, $user, $host) }; +        if ($@) { +            $self->error ($@); +            return; +        } +        for my $entry (@acl) { +            unless ($acl->add ($entry->[0], $entry->[1], $user, $host)) { +                $self->error ($acl->error); +                return; +            } +        } +    } else { +        my @entries = $acl->list; +        if (not @entries and $acl->error) { +            $self->error ($acl->error); +            return; +        } +        @entries = sort { $$a[0] cmp $$b[0] && $$a[1] cmp $$b[1] } @entries; +        @acl     = sort { $$a[0] cmp $$b[0] && $$a[1] cmp $$b[1] } @acl; +        my $okay = 1; +        if (@entries != @acl) { +            $okay = 0; +        } else { +            for my $i (0 .. $#entries) { +                $okay = 0 unless ($entries[$i][0] eq $acl[$i][0]); +                $okay = 0 unless ($entries[$i][1] eq $acl[$i][1]); +            } +        } +        unless ($okay) { +            $self->error ("ACL $aname exists and doesn't match default"); +            return; +        } +    } +    if ($acl->check ($user)) { +        return $aname; +    } else { +        $self->error ("$user not authorized to create ${type}:${name}"); +        return; +    } +} +  # Create a new object and returns that object.  On error, returns undef and  # sets the internal error. -# -# For the time being, we hard-code an ACL named ADMIN to use to authorize -# object creation.  This needs more work later.  sub create {      my ($self, $type, $name) = @_;      unless ($MAPPING{$type}) { @@ -148,15 +210,20 @@ sub create {      my $dbh = $self->{dbh};      my $user = $self->{user};      my $host = $self->{host}; +    my $acl;      unless ($self->{admin}->check ($user)) { -        $self->error ("$user not authorized to create ${type}:${name}"); -        return undef; +        $acl = $self->create_check ($type, $name); +        return unless $acl;      }      my $object = eval { $class->create ($type, $name, $dbh, $user, $host) };      if ($@) {          $self->error ($@); -        return undef; +        return;      } else { +        if ($acl and not $object->owner ($acl, $user, $host)) { +            $self->error ($object->error); +            return; +        }          return 1;      }  } @@ -780,10 +847,14 @@ if set, or the owner ACL if the store ACL is not set.  =item create(TYPE, NAME)  Creates a new object of type TYPE and name NAME.  TYPE must be a recognized -type for which the wallet system has a backend implementation.  To create an -object, the current user must be authorized by the ADMIN ACL.  Returns true +type for which the wallet system has a backend implementation.  Returns true  on success and false on failure. +To create an object, the current user must either be authorized by the ADMIN +ACL or authorized by the default owner as determined by the wallet +configuration.  For more information on how to map new objects to default +owners, see Wallet::Config(3). +  =item destroy(TYPE, NAME)  Destroys the object identified by TYPE and NAME.  This destroys any data | 
