diff options
| author | Bill MacAllister <whm@dropbox.com> | 2016-04-03 18:40:00 +0000 | 
|---|---|---|
| committer | Russ Allbery <eagle@eyrie.org> | 2018-05-27 17:33:31 -0700 | 
| commit | 2b05e1d33eff84aec21202d09821a54c95446a24 (patch) | |
| tree | c1771862000218526c44c158cc5e60f6f35bd8cd | |
| parent | 18f0408114e67c218382d013c255f1101954ac68 (diff) | |
Add ad-keytab, update Wallet::Config
* This ad-keytab is useful in the initial setup of AD as a keytab
  store for wallet.
* Change configuration variables to correctly reflect that some values
  are relative distinguished names.
* Add a configuration variable for the base distinguished name for
  ActiveDirectory.
| -rwxr-xr-x | contrib/ad-keytab | 610 | ||||
| -rw-r--r-- | perl/lib/Wallet/Config.pm | 78 | ||||
| -rw-r--r-- | perl/lib/Wallet/Kadmin/AD.pm | 51 | 
3 files changed, 691 insertions, 48 deletions
diff --git a/contrib/ad-keytab b/contrib/ad-keytab new file mode 100755 index 0000000..2af9f85 --- /dev/null +++ b/contrib/ad-keytab @@ -0,0 +1,610 @@ +#!/usr/bin/perl -w +# +# Create, update, delete, and display keytabs stored in Active Directory. +# +# Written by Bill MacAllister <whm@dropbox.com> +# Copyright 2016 Dropbox, Inc. +# +# See LICENSE for licensing terms. + +############################################################################## +# Declarations +############################################################################## + +use Authen::SASL; +use Carp; +use Getopt::Long; +use IPC::Run qw( run timeout ); +use Net::LDAP; +use Pod::Usage; +use strict; + +my $opt_ad_server; +my $opt_base_dn; +my $opt_computer_rdn; +my $opt_config; +my $opt_debug; +my $opt_dump; +my $opt_help; +my $opt_manual; +my $opt_realm; +my $opt_user_rdn; + +# Configuration variables +our $AD_DEBUG; +our $AD_SERVER; +our $AD_COMPUTER_RDN; +our $AD_USER_RDN; +our $KEYTAB_REALM; +our $AD_BASE_DN; + +############################################################################## +# Subroutines +############################################################################## + +# Write messages to standard output and check the return status +sub msg { +    my @msgs = @_; +    for my $m (@msgs) { +        print STDOUT $m . "\n" or croak("Problem printing to STDOUT"); +    } +    return; +} + +# Write debugging messages +sub dbg { +    my ($m) = @_; +    msg("DEBUG:$m"); +    return; +} + +# Decode Active Directory's userAccountControl attribute +# Flags are powers of two starting at zero. +sub list_userAccountControl { +    my ($uac) = @_; +    my @flags = ( +        'SCRIPT', +        'ACCOUNTDISABLE', +        'HOMEDIR_REQUIRED', +        'LOCKOUT', +        'PASSWD_NOTREQD', +        'PASSWD_CANT_CHANGE', +        'ENCRYPTED_TEXT_PWD_ALLOWED', +        'TEMP_DUPLICATE_ACCOUNT', +        'NORMAL_ACCOUNT', +        'INTERDOMAIN_TRUST_ACCOUNT', +        'WORKSTATION_TRUST_ACCOUNT', +        'SERVER_TRUST_ACCOUNT', +        'DONT_EXPIRE_PASSWORD', +        'MNS_LOGON_ACCOUNT', +        'SMARTCARD_REQUIRED', +        'TRUSTED_FOR_DELEGATION', +        'NOT_DELEGATED', +        'USE_DES_KEY_ONLY', +        'DONT_REQ_PREAUTH', +        'PASSWORD_EXPIRED', +        'TRUSTED_TO_AUTH_FOR_DELEGATION', +        'PARTIAL_SECRETS_ACCOUNT' +    ); + +    my $flag_list; +    my $comma = ''; +    for (my $i=0; $i<scalar(@flags); $i++) { +        if ($uac & (2**$i)) { +            $flag_list .= $comma . $flags[$i]; +            $comma = ', '; +        } +    } +    return $flag_list; +} + +# GSS-API bind to the active directory server +sub ldap_connect { +    my $ldap; +    if ($AD_DEBUG) { +        dbg('binding to ' . $AD_SERVER); +    } +    if (!$AD_SERVER) { +        croak("Missing ldap host name, specify ad_server=\n"); +    } +    eval { +        my $sasl = Authen::SASL->new(mechanism => 'GSSAPI'); +        $ldap = Net::LDAP->new($AD_SERVER, onerror => 'die'); +        my $mesg = eval { $ldap->bind(undef, sasl => $sasl) }; +    }; +    if ($@) { +        my $error = $@; +        die "ldap bind to AD failed: $error\n"; +    } +    return $ldap; +} + +# Take a principal and split into parts.  The parts are keytab type, +# keytab identifier, the base dn, an LDAP filter, and if the keytab +# type is host the host name. +sub kerberos_attrs { +    my ($principal) = @_; + +    my %attr = (); +    my $dn; +    my $host; +    my $k_type; +    my $k_id; +    if ($principal =~ m,^(host|service)/(\S+),xms) { +        $attr{type} = $1; +        $attr{id}   = $2; +        if ($attr{type} eq 'host') { +            $attr{base}   = $AD_COMPUTER_RDN . ',' . $AD_BASE_DN; +            $attr{host}   = $attr{id}; +            $attr{host}   =~ s/[.].*//; +            $attr{dn}     = "cn=$attr{host},$attr{base}"; +            $attr{filter} = "(samAccountName=$attr{host}\$)"; +        } elsif ($attr{'type'} eq 'service') { +            $attr{base}   = $AD_USER_RDN  . ',' . $AD_BASE_DN; +            $attr{dn}     = "cn=srv-$attr{id},$attr{base}"; +            $attr{filter} = "(servicePrincipalName=$attr{type}/$attr{id})"; +        } +    } +    if ($AD_DEBUG) { +        for my $a (sort keys %attr) { +            dbg("$a = $attr{$a}"); +        } +    } +    return %attr; +} + +# Perform an LDAP search against AD and return information about +# service and host accounts. +sub ad_show { +    my ($principal, $kattr_ref) = @_; + +    my $ldap = ldap_connect(); +    my %kattr = %{$kattr_ref}; +    my $base   = $kattr{base}; +    my $filter = $kattr{filter}; +    my @attrs = (); +    if (!$opt_dump) { +        @attrs = ( +            'distinguishedName',             'objectclass', +            'dnsHostname',                   'msds-KeyVersionNumber', +            'msds-SupportedEncryptionTypes', 'name', +            'servicePrincipalName',          'samAccountName', +            'userAccountControl',            'userPrincipalName', +            'whenChanged',                   'whenCreated', +            ); +    } + +    if ($AD_DEBUG) { +        dbg("base:$base filter:$filter scope:subtree\n"); +    } + +    my $result; +    eval { +        $result = $ldap->search( +            base   => $base, +            scope  => 'subtree', +            filter => $filter, +            attrs  => \@attrs +            ); +    }; +    if ($@) { +        my $error = $@; +        die "LDAP search error: $error\n"; +    } +    if ($result->code) { +        msg("INFO base:$base filter:$filter scope:subtree\n"); +        die $result->error; +    } +    if ($AD_DEBUG) { +        dbg('returned: ' . $result->count); +    } +    if ($result->count > 0) { +        for my $entry ($result->entries) { +            for my $attr ( sort $entry->attributes ) { +                my $out = ''; +                if ($attr =~ /userAccountControl/xmsi) { +                    my $val = $entry->get_value($attr); +                    $out = "$attr: $val"; +                    $out .= ' (' . list_userAccountControl($val) . ')'; +                    msg($out); +                } else { +                    my $val_ref = $entry->get_value($attr, asref => 1); +                    my @vals = @{$val_ref}; +                    for my $val (@vals) { +                        msg("$attr: $val"); +                    } +                } +            } +        } +    } else { +        msg("$kattr{type}/$kattr{id} not found"); +    } +    msg(' '); +    return; +} + +# Check to see if a keytab exists +sub ad_exists { +    my ($principal, $kattr_ref) = @_; + +    my $ldap = ldap_connect(); +    my %kattr = %{$kattr_ref}; +    my $base   = $kattr{base}; +    my $filter = $kattr{filter}; +    my @attrs = ('objectClass', 'msds-KeyVersionNumber'); +    if ($AD_DEBUG) { +        dbg("base:$base filter:$filter scope:subtree\n"); +    } + +    my $result; +    eval { +        $result = $ldap->search( +            base   => $base, +            scope  => 'subtree', +            filter => $filter, +            attrs  => \@attrs +            ); +    }; +    if ($@) { +        my $error = $@; +        die "LDAP search error: $error\n"; +    } +    if ($result->code) { +        msg("INFO base:$base filter:$filter scope:subtree\n"); +        die $result->error; +    } +    if ($AD_DEBUG) { +        dbg('returned: ' . $result->count); +    } +    if ($result->count > 1) { +        msg('ERROR: too many AD entries for this keytab'); +        for my $entry ($result->entries) { +            msg('INFO: dn found ' . $entry->dn . "\n"); +        } +        die("INFO: use show to examine the problem\n"); +    } +    if ($result->count) { +        for my $entry ($result->entries) { +            return $entry->get_value('msds-KeyVersionNumber'); +        } +    } else { +        return 0; +    } +    return; +} + +# Run a shell command.  In this case the command will always be msktutil. +sub run_cmd { +    my @cmd = @_; + +    if ($AD_DEBUG) { +        dbg('running command:' . join(q{ }, @cmd)); +    } + +    my $in; +    my $out; +    my $err; +    my $err_flag; +    eval { +        run(\@cmd, \$in, \$out, \$err, timeout(60)); +        if ($?) { +            my $this_err = $?; +            $err_flag = 1; +            if ($this_err) { +                msg('ERROR:' . $?); +            } +            if ($err) { +                msg('ERROR (err):' . $err); +            } +        } +    }; +    if ($@) { +        msg('ERROR (status):' . $@); +        $err_flag = 1; +    } +    if ($err_flag) { +        msg('ERROR: Problem executing:' . join(q{ }, @cmd)); +        die "FATAL: Execution failed\n"; +    } + +    msg($out); +    return; +} + +# Either create or update a keytab for the principal.  Return the name +# of the keytab file created. +sub ad_create_update { +    my ($principal, $file, $action) = @_; +    my @cmd = ('/usr/sbin/msktutil'); +    push @cmd, '--' . $action; +    push @cmd, '--server',   $AD_SERVER; +    push @cmd, '--enctypes', '0x4'; +    push @cmd, '--enctypes', '0x8'; +    push @cmd, '--enctypes', '0x10'; +    push @cmd, '--keytab',   $file; +    if ($KEYTAB_REALM) { +        push @cmd, '--realm', $KEYTAB_REALM; +    } +    if ($principal =~ m,^host/(\S+),xms) { +        my $fqdn = $1; +        my $host = $fqdn; +        $host =~ s/[.].*//xms; +        push @cmd, '--base', $AD_COMPUTER_RDN; +        push @cmd, '--dont-expire-password'; +        push @cmd, '--computer-name', $host; +        push @cmd, '--upn',           "host/$fqdn"; +        push @cmd, '--hostname',      $fqdn; +    } elsif ($principal =~ m,^service/(\S+),xms) { +        my $service_id = $1; +        push @cmd, '--base', $AD_USER_RDN; +        push @cmd, '--use-service-account'; +        push @cmd, '--service',      "service/$service_id"; +        push @cmd, '--account-name', "srv-${service_id}"; +        push @cmd, '--no-pac'; +    } +    run_cmd(@cmd); +    return; +} + +# Delete a principal from Kerberos.  For AD this means just delete the +# object using LDAP. +sub ad_delete { +    my ($principal, $kattr_ref) = @_; + +    my %kattr = %{$kattr_ref}; +    if (!ad_exists($principal, $kattr_ref)) { +        msg("WARN: the keytab for $principal does not appear to exist."); +        msg("INFO: attempting the delete anyway.\n"); +    } + +    my $ldap = ldap_connect(); +    my $msgid = $ldap->delete($kattr{dn}); +    if ($msgid->code) { +        my $m; +        $m .= "ERROR: Problem deleting $kattr{dn}\n"; +        $m .= $msgid->error; +        die $m; +    } +    return 1; +} + +############################################################################## +# Main Routine +############################################################################## + +# Get options +GetOptions( +    'ad_server=s'    => \$opt_ad_server, +    'base_dn=s'      => \$opt_base_dn, +    'computer_rdn=s' => \$opt_computer_rdn, +    'config=s'       => \$opt_config, +    'debug'          => \$opt_debug, +    'dump'           => \$opt_dump, +    'help'           => \$opt_help, +    'manual'         => \$opt_manual, +    'realm'          => \$opt_realm, +    'user_rdn=s'     => \$opt_user_rdn +); + +# Help the user +if ($opt_manual) { +    pod2usage(-verbose => 2); +} +if ($opt_help || !$ARGV[0]) { +    pod2usage(-verbose => 0); +} + +# Make sure that we have kerberos credentials and that KRB5CCNAME +# points to them. +if (!$ENV{'KRB5CCNAME'}) { +    msg('ERROR: Kerberos credentials are required ... try kinit'); +    pod2usage(-verbose => 0); +} + +# Read the configuration file or croak +my $conf_file; +if ($opt_config) { +    if (-e $opt_config) { +        $conf_file = $opt_config; +    } else { +        msg("ERROR: Config file ($opt_config) not found"); +        pod2usage(-verbose => 0); +    } +} elsif ($ENV{'ADKEYTAB'}) { +    $conf_file = $ENV{'ADKEYTAB'}; +} elsif (-e '.ad-keytab.conf') { +    $conf_file = '.ad-keytab.conf'; +} else { +    $conf_file = '/etc/wallet/wallet.conf'; +} +do $conf_file or die (($@ || $!) . "\n"); + +# Process command line options +if ($opt_ad_server) { +    $AD_SERVER = $opt_ad_server; +} +if ($opt_base_dn) { +    $AD_BASE_DN = $opt_base_dn; +} +if ($opt_computer_rdn) { +    $AD_COMPUTER_RDN = $opt_computer_rdn; +} +if ($opt_user_rdn) { +    $AD_USER_RDN = $opt_user_rdn; +} +if ($opt_debug) { +    $AD_DEBUG = 1; +} + +# -- Get command line arguments +my $action = shift; +my $id     = shift; +my $keytab; +if ($ARGV[0]) { +    $keytab = shift; +} else { +    $keytab = '/etc/krb5.keytab'; +} + +my %kattr = kerberos_attrs($id); +# Validate that the keytab id makes sense for the keytab type +if ($kattr{type} eq 'service') { +    if ($kattr{id} =~ /[.]/xms) { +        msg('ERROR: service principal names may not contain periods'); +        pod2usage(-verbose => 0); +    } +    if (length($kattr{id}) > 22) { +        msg('ERROR: service principal name too long'); +        pod2usage(-verbose => 0); +    } +} elsif ($kattr{type} eq 'host') { +    if ($kattr{id} !~ /[.]/xms) { +        msg('ERROR: FQDN is required'); +        pod2usage(-verbose => 0); +    } +} else { +    msg("ERROR: unknown keytab type $kattr{type}"); +    pod2usage(-verbose => 0); +} + +if ($action =~ /^(create|update)/xms) { +    ad_create_update($id, $keytab, $1); +} elsif ($action =~ /^del/xms) { +    ad_delete($id, \%kattr); +} elsif ($action =~ /^sh/xms) { +    ad_show($id, \%kattr); +} else { +    msg("ERROR: unknown action $action"); +    pod2usage(-verbose => 0); +} + +exit; + +__END__ + +=head1 NAME + +ad-keytab + +=head1 SYNOPSIS + +ad-keytab create|update|delete|show keytab-id [keytab-file] +[--ad_server=hostname] [--computer_rdn=dn] [--user_rdn] [--dump] +[--help] [--manual] [--debug] + +=head1 DESCRIPTION + +This script is a wrapper around msktutil and ldapsearch to simplify +the creation of host and service keytabs.  The script is useful for +boot strapping the kerberos credentials required to use Active +Directory as a backend keytab store for wallet.  The script shares +the wallet configuration file. + +Generally, two keytabs will need to be created to setup update.  One +host keytab for the wallet server host and one service keytab for +wallet to use when connecting to an Active Directory Domain +Controller. + +Note, this script does not update the Wallet database which means +any keytabs created by it will be invisible from wallet. + +=head1 ACTIONS + +=over 4 + +=item create + +Add a keytab to AD and update the keytab file.  Fails if the keytab +already exists. + +=item update + +Update an existing keytab in AD and update the keytab file.  Fails if +the keytab does not exist. + +=item delete + +Delete a keytab from AD and remove it from the keytab file. + +=item show + +Show AD's view of the account corresponding to the keytab.  This action +does not use msktutil and queries AD directly using LDAP. + +=back + +=head1 OPTIONS AND ARGUMENTS + +=over 4 + +=item keytab-id + +This is either host principal name of the form host/<fqdn> or a +service principal name of the form service/<id>.  Service keytab +identifiers cannot be longer than 18 characters because of an +ActiveDirectory restriction. + +=item keytab-filename + +The name of the keytab file.  Defaults to /etc/krb5.keytab. + +=item --conf=filename + +The configuration file to read.  The script searches for a configuration +file in the following order. + +      * The command line switch --conf +      * The environment variable ADKEYTAB +      * The file .ad-keytab.conf +      * The file /etc/ad-keytab.conf + +=item --ad_server=hostname + +The name of the Active Directory host to connect to.  It is important +what the script contact only _one_ server due to the fact that +propagation within an Active Directory domain can be quite slow. + +=item --base_dn=ou=org,dc=domain,dc=tld + +The base distinguished name holding both computer and user accounts. + +=item --computer_rdn=dn + +The relative distinguished name to use as the base DN for both the +creation of host keytabs and searches of Active Directory.  The +distinguished name formed will be computer_rdn,base_dn. + +=item --user_rdn=dn + +The relative distinguished name to use as the base DN for ldap +searches of Active Directory for service keytabs.  The distinguished +name formed will be user_rdn_rdn,base_dn. + +=item --dump + +When displaying keytab attributes show all of the attributes. + +=item --help + +Displays help text. + +=item --manual + +Displays more complete help text. + +=item --debug + +Turns on debugging displays. + +=back + +=head1 SEE ALSO + +Set the documentation for Wallet::Config for configuration information, i.e. +perldoc Wallet::Config. + +=head1 AUTHOR + +Bill MacAllister <whm@dropbox.com> + +=cut diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm index 6515756..2222aba 100644 --- a/perl/lib/Wallet/Config.pm +++ b/perl/lib/Wallet/Config.pm @@ -415,40 +415,39 @@ our $KEYTAB_TMP;  =back -The following parameters are specific to generating keytabs from Active -Directory (KEYTAB_KRBTYPE is set to C<AD>). +The following parameters are specific to generating keytabs from +Active Directory (KEYTAB_KRBTYPE is set to C<AD>).  =over 4 -=item AD_CACHE - -Specifies the ticket cache to use when manipulating Active Directory objects. -The ticket cache must be for a principal able to bind to Active Directory and -run B<msktutil>. +=item AD_BASE_DN -AD_CACHE must be set to use Active Directory support. +The base distinguished name of the ActiveDirectory instance.  This is +use when Wallet uses LDAP directly to examine objects in Active +Directory.  =cut -our $AD_CACHE; +our $AD_BASE_DN; -=item AD_COMPUTER_DN +=item AD_COMPUTER_RDN -The LDAP base DN for computer objects inside Active Directory.  All keytabs of -the form host/<hostname> will be mapped to objects with a C<samAccountName> of -the <hostname> portion under this DN. +The LDAP base DN for computer objects inside Active Directory.  All +keytabs of the form host/<hostname> will be mapped to objects with a +C<samAccountName> of the <hostname> portion under this DN. -AD_COMPUTER_DN must be set if using Active Directory as the keytab backend. +AD_COMPUTER_RDN must be set if using Active Directory as the keytab +backend.  =cut -our $AD_COMPUTER_DN; +our $AD_COMPUTER_RDN;  =item AD_DEBUG -If set to true, asks for some additional debugging information, such as the -B<msktutil> command, to be logged to syslog.  These debugging messages will be -logged to the C<local3> facility. +If set to true, asks for some additional debugging information, such +as the B<msktutil> command, to be logged to syslog.  These debugging +messages will be logged to the C<local3> facility.  =cut @@ -464,17 +463,25 @@ default PATH.  our $AD_MSKTUTIL = 'msktutil'; -=item AD_USER_DN +=item AD_SERVER + +The hostname of the Active Directory Domain Controller. + +=cut + +our $AD_SERVER; + +=item AD_USER_RDN  The LDAP base DN for user objects inside Active Directory.  All keytabs of the  form service/<user> will be mapped to objects with a C<servicePrincipalName>  matching the wallet object name under this DN. -AD_USER_DN must be set if using Active Directory as the keytab backend. +AD_USER_RDN must be set if using Active Directory as the keytab backend.  =cut -our $AD_USER_DN; +our $AD_USER_RDN;  =back @@ -482,8 +489,9 @@ our $AD_USER_DN;  Heimdal provides the choice, over the network protocol, of either  downloading the existing keys for a principal or generating new random -keys.  MIT Kerberos does not; downloading a keytab over the kadmin -protocol always rekeys the principal. +keys.  Neither MIT Kerberos or ActiveDirectory support retrieving an +existing keytab; downloading a keytab over the kadmin protocol or +using msktutil always rekeys the principal.  For MIT Kerberos, the keytab object backend therefore optionally supports  retrieving existing keys, and hence keytabs, for Kerberos principals by @@ -491,6 +499,11 @@ contacting the KDC via remctl and talking to B<keytab-backend>.  This is  enabled by setting the C<unchanging> flag on keytab objects.  To configure  that support, set the following variables. +For ActiveDirectory Kerberos, the keytab object backend supports +storing the keytabs on the wallet server.  This functionality is +enabled by setting the configuration variable AD_KEYTAB_BUCKET.  (This +had not been implemented yet.) +  This is not required for Heimdal; for Heimdal, setting the C<unchanging>  flag is all that's needed. @@ -542,6 +555,25 @@ will be used.  our $KEYTAB_REMCTL_PORT; +=item AD_CACHE + +The ticket cache that hold credentials used to access the +ActiveDirectory KDC.  This must be created and maintained externally. + +=cut + +our $AD_CACHE; + +=item AD_KEYTAB_BUCKET + +The path to store a copy of keytabs created.  This is required for the +support of unchanging keytabs with an ActiveDirectory KDC.  (This has +not been implemented yet.) + +=cut + +our $AD_KEYTAB_BUCKET = '/var/lib/wallet/keytabs'; +  =back  =head1 WEBAUTH KEYRING OBJECT CONFIGURATION diff --git a/perl/lib/Wallet/Kadmin/AD.pm b/perl/lib/Wallet/Kadmin/AD.pm index ec60af9..1c13ab6 100644 --- a/perl/lib/Wallet/Kadmin/AD.pm +++ b/perl/lib/Wallet/Kadmin/AD.pm @@ -1,8 +1,8 @@  # Wallet::Kadmin::AD -- Wallet Kerberos administration API for AD  # -# Written by Bill MacAllister <bill@ca-zephyr.org> +# Written by Bill MacAllister <whm@dropbox.com>  # Copyright 2016 Russ Allbery <eagle@eyrie.org> -# Copyright 2015 Dropbox, Inc. +# Copyright 2015,2016 Dropbox, Inc.  # Copyright 2007, 2008, 2009, 2010, 2014  #     The Board of Trustees of the Leland Stanford Junior University  # @@ -100,17 +100,19 @@ sub ldap_base_filter {          my $fqdn = $1;          my $host = $fqdn;          $host =~ s/[.].*//xms; -        $base   = $Wallet::Config::AD_COMPUTER_DN;          $filter = "(samAccountName=${host}\$)"; +        $base   = $Wallet::Config::AD_COMPUTER_RDN . ',' +          . $Wallet::Config::AD_BASE_DN;      } elsif ($principal =~ m,^service/(\S+),xms) {          my $id = $1; -        $base   = $Wallet::Config::AD_USER_DN;          $filter = "(servicePrincipalName=service/${id})"; +        $base +          = $Wallet::Config::AD_USER_RDN . ',' . $Wallet::Config::AD_BASE_DN;      }      return ($base, $filter);  } -# TODO: Get a keytab from the keytab cache. +# TODO: Get a keytab from the keytab bucket.  sub get_ad_keytab {      my ($self, $principal) = @_;      return; @@ -125,13 +127,16 @@ sub get_ad_keytab {  sub msktutil {      my ($self, $args_ref) = @_;      unless (defined($Wallet::Config::KEYTAB_HOST) +        and defined($Wallet::Config::KEYTAB_PRINCIPAL) +        and defined($Wallet::Config::KEYTAB_FILE)          and defined($Wallet::Config::KEYTAB_REALM))      {          die "keytab object implementation not configured\n";      } -    unless (defined($Wallet::Config::AD_CACHE) -        and defined($Wallet::Config::AD_COMPUTER_DN) -        and defined($Wallet::Config::AD_USER_DN)) +    unless (-e $Wallet::Config::AD_MSKTUTIL +        and defined($Wallet::Config::AD_BASE_DN) +        and defined($Wallet::Config::AD_COMPUTER_RDN) +        and defined($Wallet::Config::AD_USER_RDN))      {          die "Active Directory support not configured\n";      } @@ -192,14 +197,16 @@ sub ad_create_update {          my $fqdn = $1;          my $host = $fqdn;          $host =~ s/[.].*//xms; +        push @cmd, '--base',          $Wallet::Config::COMPUTER_RDN;          push @cmd, '--dont-expire-password';          push @cmd, '--computer-name', $host; -        push @cmd, '--upn', "host/$fqdn"; -        push @cmd, '--hostname', $fqdn; +        push @cmd, '--upn',           "host/$fqdn"; +        push @cmd, '--hostname',      $fqdn;      } elsif ($principal =~ m,^service/(\S+),xms) {          my $service_id = $1; +        push @cmd, '--base',         $Wallet::Config::USER_RDN;          push @cmd, '--use-service-account'; -        push @cmd, '--service', "service/$service_id"; +        push @cmd, '--service',      "service/$service_id";          push @cmd, '--account-name', "srv-${service_id}";          push @cmd, '--no-pac';      } @@ -365,9 +372,15 @@ sub ad_delete {          if ($k_type eq 'host') {              my $host = $k_id;              $host =~ s/[.].*//; -            $dn = "cn=${host}," . $Wallet::Config::AD_COMPUTER_DN; +            $dn +              = "cn=${host}," +              . $Wallet::Config::AD_COMPUTER_RDN . ',' +              . $Wallet::Config::AD_BASE_DN;          } elsif ($k_type eq 'service') { -            $dn = "cn=srv-${k_id}," . $Wallet::Config::AD_USER_DN; +            $dn +              = "cn=srv-${k_id}," +              . $Wallet::Config::AD_USER_RDN . ',' +              . $Wallet::Config::AD_BASE_DN;          }      } @@ -435,18 +448,6 @@ using a local keytab cache.  To use this class, several configuration parameters must be set.  See  L<Wallet::Config/"KEYTAB OBJECT CONFIGURATION"> for details. -=head1 FILES - -=over 4 - -=item KEYTAB_TMP/keytab.<pid> - -The keytab is created in this file and then read into memory.  KEYTAB_TMP -is set in the wallet configuration, and <pid> is the process ID of the -current process.  The file is unlinked after being read. - -=back -  =head1 LIMITATIONS  Currently, this implementation calls an external B<msktutil> program rather  | 
