summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2007-08-28 06:22:35 +0000
committerRuss Allbery <rra@stanford.edu>2007-08-28 06:22:35 +0000
commit3c3d5c870b45ba64b105742788384d8f83456a91 (patch)
tree2f71de3dd80aeb38e6c12726bd03bb195095314a
parent3c24b3a018dac170048d40419e1b00a78adaf5bb (diff)
Add the keytab object implementation. Add the skeleton of a config
module which currently doesn't do anything useful except define some variables so that I can do syntax checking.
-rw-r--r--perl/Wallet/Config.pm41
-rw-r--r--perl/Wallet/Object/Keytab.pm184
2 files changed, 225 insertions, 0 deletions
diff --git a/perl/Wallet/Config.pm b/perl/Wallet/Config.pm
new file mode 100644
index 0000000..6dd2313
--- /dev/null
+++ b/perl/Wallet/Config.pm
@@ -0,0 +1,41 @@
+# Wallet::Config -- Configuration handling for the wallet server.
+# $Id$
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2007 Board of Trustees, Leland Stanford Jr. University
+#
+# See README for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::Config;
+require 5.006;
+
+use strict;
+use vars qw($PATH $VERSION);
+
+# This version should be increased on any code change to this module. Always
+# use two digits for the minor version with a leading zero if necessary so
+# that it will sort properly.
+$VERSION = '0.01';
+
+# Path to the config file to load.
+$PATH = '/etc/wallet.conf';
+
+##############################################################################
+# Variables
+##############################################################################
+
+# Configuration for the keytab object type.
+our $KEYTAB_FILE;
+our $KEYTAB_FLAGS = '-clearpolicy';
+our $KEYTAB_HOST;
+our $KEYTAB_KADMIN = 'kadmin';
+our $KEYTAB_PRINCIPAL;
+our $KEYTAB_REALM;
+our $KEYTAB_TMP;
+
+1;
+__END__
diff --git a/perl/Wallet/Object/Keytab.pm b/perl/Wallet/Object/Keytab.pm
new file mode 100644
index 0000000..f03a29c
--- /dev/null
+++ b/perl/Wallet/Object/Keytab.pm
@@ -0,0 +1,184 @@
+# Wallet::Object::Keytab -- Keytab object implementation for the wallet.
+# $Id$
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2007 Board of Trustees, Leland Stanford Jr. University
+#
+# See README for licensing terms.
+
+##############################################################################
+# Modules and declarations
+##############################################################################
+
+package Wallet::Object::Keytab;
+require 5.006;
+
+use strict;
+use vars qw(@ISA $VERSION);
+
+use Wallet::Config ();
+
+@ISA = qw(Wallet::Object);
+
+# This version should be increased on any code change to this module. Always
+# use two digits for the minor version with a leading zero if necessary so
+# that it will sort properly.
+$VERSION = '0.01';
+
+##############################################################################
+# kadmin Interaction
+##############################################################################
+
+# Make sure that principals are well-formed and don't contain characters that
+# will cause us problems when talking to kadmin. Takes a principal and
+# returns true if it's okay, false otherwise.
+sub _valid_principal {
+ my ($self, $principal) = @_;
+ if ($principal !~ m,^[\w-]+(/[\w_-]+)?\@[\w._-]+,) {
+ return undef;
+ }
+ return 1;
+}
+
+# Run a kadmin command and capture the output. Returns the output, either as
+# a list of lines or, in scalar context, as one string. The exit status of
+# kadmin is often worthless.
+sub _kadmin {
+ my ($self, $command) = @_;
+ my @args = ('-p', $Wallet::Config::KEYTAB_PRINCIPAL, '-k', '-t',
+ $Wallet::Config::KEYTAB_FILE, '-q', $command);
+ push (@args, '-s', $Wallet::Config::KEYTAB_HOST)
+ if $Wallet::Config::KEYTAB_HOST;
+ my $pid = open (KADMIN, '-|');
+ if (not defined $pid) {
+ die "error: cannot fork: $!\n";
+ } elsif ($pid == 0) {
+ open (STDERR, '>&STDOUT') or die "error: cannot dup stdout: $!\n";
+ exec ($Wallet::Config::KEYTAB_KADMIN, @args)
+ or die "error: cannot run $Wallet::Config::KEYTAB_KADMIN\n";
+ }
+ local $_;
+ my @output;
+ while (<KADMIN>) {
+ push (@output, $_) unless /Authenticating as principal/;
+ }
+ close KADMIN;
+ return wantarray ? @output : join ('', @output);
+}
+
+# Check whether a given principal already exists in Kerberos. Returns true if
+# so, false otherwise.
+sub _kadmin_exists {
+ my ($self, $principal) = @_;
+ return undef unless $self->_valid_principal ($principal);
+ my $output = $self->_kadmin ("getprinc $principal");
+ if ($output =~ /does not exist/) {
+ return undef;
+ } else {
+ return 1;
+ }
+}
+
+# Create a principal in Kerberos. Return true if successful, false otherwise.
+sub _kadmin_addprinc {
+ my ($self, $principal) = @_;
+ unless ($self->_valid_principal ($principal)) {
+ $self->{error} = "invalid principal name: $principal";
+ return undef;
+ }
+ my $flags = $Wallet::Config::KEYTAB_FLAGS;
+ my $output = $self->_kadmin ("addprinc -randkey $flags $principal");
+ if ($output =~ /^add_principal: (.*)/m) {
+ return undef;
+ }
+ return 1;
+}
+
+# Create a keytab from a principal. Return true if successful, false
+# otherwise. If the keytab creation fails, sets the error.
+sub _kadmin_ktadd {
+ my ($self, $principal, $file) = @_;
+ unless ($self->_valid_principal ($principal)) {
+ $self->{error} = "invalid principal name: $principal";
+ return undef;
+ }
+ my $output = $self->_kadmin ("ktadd -q -k $file $principal");
+ if ($output =~ /^ktadd: (.*)/m) {
+ $self->{error} = "error creating keytab for $principal: $1";
+ return undef;
+ }
+ return 1;
+}
+
+# Delete a principal from Kerberos. Return true if successful, false
+# otherwise. If the deletion fails, sets the error. If the principal doesn't
+# exist, return success; we're bringing reality in line with our expectations.
+sub _kadmin_delprinc {
+ my ($self, $principal) = @_;
+ unless ($self->_valid_principal ($principal)) {
+ $self->{error} = "invalid principal name: $principal";
+ return undef;
+ }
+ if (not $self->_kadmin_exists ($principal)) {
+ return 1;
+ }
+ my $output = $self->_kadmin ("delprinc $principal");
+ if ($output =~ /^delete_principal: (.*)/m) {
+ $self->{error} = "error deleting $principal: $1";
+ return undef;
+ }
+ return 1;
+}
+
+##############################################################################
+# Implementation
+##############################################################################
+
+# Override create to start by creating the principal in Kerberos and only
+# create the entry in the database if that succeeds. Error handling isn't
+# great here since we don't have a way to communicate the error back to the
+# caller.
+sub create {
+ my ($class, $name, $type, $dbh, $creator, $host, $time) = @_;
+ if ($name !~ /\@/ && $Wallet::Config::KEYTAB_REALM) {
+ $name .= '@' . $Wallet::Config::KEYTAB_REALM;
+ }
+ return undef if not $class->_kadmin_addprinc ($name);
+ return $class->SUPER::create ($name, $type, $dbh, $creator, $host, $time);
+}
+
+# Override destroy to delete the principal out of Kerberos as well.
+sub destroy {
+ my ($self, $user, $host, $time) = @_;
+ return undef if not $self->_kadmin_delprinc ($self->{name});
+ return $self->SUPER::destroy ($user, $host, $time);
+}
+
+# Our get implementation. Generate a keytab into a temporary file and then
+# return that as the return value.
+sub get {
+ my ($self, $user, $host, $time) = @_;
+ $time ||= time;
+ my $file = $Wallet::Config::KEYTAB_TMP . "/keytab.$$";
+ return undef if not $self->_kadmin_ktadd ($self->{name});
+ local *KEYTAB;
+ unless (open (KEYTAB, '<', $file)) {
+ my $princ = $self->{name};
+ $self->{error} = "error creating keytab for principal $princ: $!";
+ return undef;
+ }
+ local $/;
+ undef $!;
+ my $data = <KEYTAB>;
+ if ($!) {
+ my $princ = $self->{name};
+ $self->{error} = "error creating keytab for principal $princ: $!";
+ return undef;
+ }
+ close KEYTAB;
+ $self->log_action ('get', $user, $host, $time);
+ return $data;
+}
+
+1;
+__END__;