summaryrefslogtreecommitdiff
path: root/perl/lib/Wallet/Config.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perl/lib/Wallet/Config.pm')
-rw-r--r--perl/lib/Wallet/Config.pm827
1 files changed, 827 insertions, 0 deletions
diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm
new file mode 100644
index 0000000..527658c
--- /dev/null
+++ b/perl/lib/Wallet/Config.pm
@@ -0,0 +1,827 @@
+# Wallet::Config -- Configuration handling for the wallet server.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2007, 2008, 2010, 2013, 2014
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+package Wallet::Config;
+require 5.006;
+
+use strict;
+use warnings;
+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.05';
+
+# Path to the config file to load.
+$PATH = $ENV{WALLET_CONFIG} || '/etc/wallet/wallet.conf';
+
+=head1 NAME
+
+Wallet::Config - Configuration handling for the wallet server
+
+=for stopwords
+DBI DSN SQLite subdirectories KEYTAB keytab kadmind KDC add-ons kadmin DNS
+SRV kadmin keytabs remctl backend lowercased NETDB ACL NetDB unscoped
+usernames rekey hostnames Allbery wallet-backend keytab-backend Heimdal
+rekeys WebAuth WEBAUTH keyring LDAP DN GSS-API integrations
+
+=head1 SYNOPSIS
+
+ use Wallet::Config;
+ my $driver = $Wallet::Config::DB_DRIVER;
+ my $info;
+ if (defined $Wallet::Config::DB_INFO) {
+ $info = $Wallet::Config::DB_INFO;
+ } else {
+ $info = "database=$Wallet::Config::DB_NAME";
+ $info .= ";host=$Wallet::Config::DB_HOST"
+ if $Wallet::Config::DB_HOST;
+ $info .= ";port=$Wallet::Config::DB_PORT"
+ if $Wallet::Config::DB_PORT;
+ }
+ my $dsn = "dbi:$driver:$info";
+ my $user = $Wallet::Config::DB_USER;
+ my $password = $Wallet::Config::DB_PASSWORD;
+ my $dbh = DBI->connect ($dsn, $user, $password);
+
+=head1 DESCRIPTION
+
+Wallet::Config encapsulates all of the site-specific configuration for the
+wallet server. It is implemented as a Perl class that declares and sets
+the defaults for various configuration variables and then, if it exists,
+loads the file specified by the WALLET_CONFIG environment variable or
+F</etc/wallet/wallet.conf> if that environment variable isn't set. That
+file should contain any site-specific overrides to the defaults, and at
+least some parameters must be set.
+
+This file must be valid Perl. To set a variable, use the syntax:
+
+ $VARIABLE = <value>;
+
+where VARIABLE is the variable name (always in all-capital letters) and
+<value> is the value. If setting a variable to a string and not a number,
+you should normally enclose <value> in C<''>. For example, to set the
+variable DB_DRIVER to C<MySQL>, use:
+
+ $DB_DRIVER = 'MySQL';
+
+Always remember the initial dollar sign (C<$>) and ending semicolon
+(C<;>). Those familiar with Perl syntax can of course use the full range
+of Perl expressions.
+
+This configuration file should end with the line:
+
+ 1;
+
+This ensures that Perl doesn't think there is an error when loading the
+file.
+
+=head1 DATABASE CONFIGURATION
+
+=over 4
+
+=item DB_DDL_DIRECTORY
+
+Specifies the directory used to dump the database schema in formats for
+each possible database server. This also includes diffs between schema
+versions, for upgrades. The default value is F</usr/local/share/wallet>,
+which matches the default installation location.
+
+=cut
+
+our $DB_DDL_DIRECTORY = '/usr/local/share/wallet';
+
+=item DB_DRIVER
+
+Sets the Perl database driver to use for the wallet database. Common
+values would be C<SQLite> or C<MySQL>. Less common values would be
+C<Oracle>, C<Sybase>, or C<ODBC>. The appropriate DBD::* Perl module for
+the chosen driver must be installed and will be dynamically loaded by the
+wallet. For more information, see L<DBI>.
+
+This variable must be set.
+
+=cut
+
+our $DB_DRIVER;
+
+=item DB_INFO
+
+Sets the remaining contents for the DBI DSN (everything after the driver).
+Using this variable provides full control over the connect string passed
+to DBI. When using SQLite, set this variable to the path to the SQLite
+database. If this variable is set, DB_NAME, DB_HOST, and DB_PORT are
+ignored. For more information, see L<DBI> and the documentation for the
+database driver you're using.
+
+Either DB_INFO or DB_NAME must be set. If you don't need to pass any
+additional information to DBI, set DB_INFO to the empty string (C<''>).
+
+=cut
+
+our $DB_INFO;
+
+=item DB_NAME
+
+If DB_INFO is not set, specifies the database name. The third part of the
+DBI connect string will be set to C<database=DB_NAME>, possibly with a
+host and port appended if DB_HOST and DB_PORT are set. For more
+information, see L<DBI> and the documentation for the database driver
+you're using.
+
+Either DB_INFO or DB_NAME must be set.
+
+=cut
+
+our $DB_NAME;
+
+=item DB_HOST
+
+If DB_INFO is not set, specifies the database host. C<;host=DB_HOST> will
+be appended to the DBI connect string. For more information, see L<DBI>
+and the documentation for the database driver you're using.
+
+=cut
+
+our $DB_HOST;
+
+=item DB_PORT
+
+If DB_PORT is not set, specifies the database port. C<;port=DB_PORT> will
+be appended to the DBI connect string. If this variable is set, DB_HOST
+should also be set. For more information, see L<DBI> and the
+documentation for the database driver you're using.
+
+=cut
+
+our $DB_PORT;
+
+=item DB_USER
+
+Specifies the user for database authentication. Some database backends,
+particularly SQLite, do not need this.
+
+=cut
+
+our $DB_USER;
+
+=item DB_PASSWORD
+
+Specifies the password for database authentication. Some database
+backends, particularly SQLite, do not need this.
+
+=cut
+
+our $DB_PASSWORD;
+
+=back
+
+=head1 DUO OBJECT CONFIGURATION
+
+These configuration variables only need to be set if you intend to use the
+C<duo> object type (the Wallet::Object::Duo class).
+
+=over 4
+
+=item DUO_AGENT
+
+If this configuration variable is set, its value should be an object that
+is call-compatible with LWP::UserAgent. This object will be used instead
+of LWP::UserAgent to make API calls to Duo. This is primarily useful for
+testing, allowing replacement of the user agent with a mock implementation
+so that a test can run without needing a Duo account.
+
+=cut
+
+our $DUO_AGENT;
+
+=item DUO_KEY_FILE
+
+The path to a file in JSON format that contains the key and hostname data
+for the Duo Admin API integration used to manage integrations via wallet.
+This file should be in the format expected by the C<key_file> parameter
+to the Net::Duo::Admin constructor. See L<Net::Duo::Admin> for more
+information.
+
+DUO_KEY_FILE must be set to use Duo objects.
+
+=cut
+
+our $DUO_KEY_FILE;
+
+=item DUO_TYPE
+
+The type of integration to create. Currently, only one type of integration
+can be created by one wallet configuration. This restriction may be relaxed
+in the future. The default value is C<unix> to create UNIX integrations.
+
+=cut
+
+our $DUO_TYPE = 'unix';
+
+=back
+
+=head1 FILE OBJECT CONFIGURATION
+
+These configuration variables only need to be set if you intend to use the
+C<file> object type (the Wallet::Object::File class).
+
+=over 4
+
+=item FILE_BUCKET
+
+The directory into which to store file objects. File objects will be
+stored in subdirectories of this directory. See L<Wallet::Object::File>
+for the full details of the naming scheme. This directory must be
+writable by the wallet server and the wallet server must be able to create
+subdirectories of it.
+
+FILE_BUCKET must be set to use file objects.
+
+=cut
+
+our $FILE_BUCKET;
+
+=item FILE_MAX_SIZE
+
+The maximum size of data that can be stored in a file object in bytes. If
+this configuration variable is set, an attempt to store data larger than
+this limit will be rejected.
+
+=cut
+
+our $FILE_MAX_SIZE;
+
+=back
+
+=head1 KEYTAB OBJECT CONFIGURATION
+
+These configuration variables only need to be set if you intend to use the
+C<keytab> object type (the Wallet::Object::Keytab class).
+
+=over 4
+
+=item KEYTAB_FILE
+
+Specifies the keytab to use to authenticate to B<kadmind>. The principal
+whose key is stored in this keytab must have the ability to create,
+modify, inspect, and delete any principals that should be managed by the
+wallet. (In MIT Kerberos F<kadm5.acl> parlance, this is C<admci>
+privileges.)
+
+KEYTAB_FILE must be set to use keytab objects.
+
+=cut
+
+our $KEYTAB_FILE;
+
+=item KEYTAB_FLAGS
+
+These flags, if any, are passed to the C<addprinc> command when creating a
+new principal in the Kerberos KDC. To not pass any flags, set
+KEYTAB_FLAGS to the empty string. The default value is C<-clearpolicy>,
+which clears any password strength policy from principals created by the
+wallet. (Since the wallet randomizes the keys, password strength checking
+is generally pointless and may interact poorly with the way C<addprinc
+-randkey> works when third-party add-ons for password strength checking
+are used.)
+
+=cut
+
+our $KEYTAB_FLAGS = '-clearpolicy';
+
+=item KEYTAB_HOST
+
+Specifies the host on which the kadmin service is running. This setting
+overrides the C<admin_server> setting in the [realms] section of
+F<krb5.conf> and any DNS SRV records and allows the wallet to run on a
+system that doesn't have a Kerberos configuration for the wallet's realm.
+
+=cut
+
+our $KEYTAB_HOST;
+
+=item KEYTAB_KADMIN
+
+The path to the B<kadmin> command-line client. The default value is
+C<kadmin>, which will cause the wallet to search for B<kadmin> on its
+default PATH.
+
+=cut
+
+our $KEYTAB_KADMIN = 'kadmin';
+
+=item KEYTAB_KRBTYPE
+
+The Kerberos KDC implementation type, either C<Heimdal> or C<MIT>
+(case-insensitive). KEYTAB_KRBTYPE must be set to use keytab objects.
+
+=cut
+
+our $KEYTAB_KRBTYPE;
+
+=item KEYTAB_PRINCIPAL
+
+The principal whose key is stored in KEYTAB_FILE. The wallet will
+authenticate as this principal to the kadmin service.
+
+KEYTAB_PRINCIPAL must be set to use keytab objects, at least until
+B<kadmin> is smart enough to use the first principal found in the keytab
+it's using for authentication.
+
+=cut
+
+our $KEYTAB_PRINCIPAL;
+
+=item KEYTAB_REALM
+
+Specifies the realm in which to create Kerberos principals. The keytab
+object implementation can only work in a single realm for a given wallet
+installation and the keytab object names are stored without realm.
+KEYTAB_REALM is added when talking to the KDC via B<kadmin>.
+
+KEYTAB_REALM must be set to use keytab objects. C<ktadd> doesn't always
+default to the local realm.
+
+=cut
+
+our $KEYTAB_REALM;
+
+=item KEYTAB_TMP
+
+A directory into which the wallet can write keytabs temporarily while
+processing C<get> commands from clients. The keytabs are written into
+this directory with predictable names, so this should not be a system
+temporary directory such as F</tmp> or F</var/tmp>. It's best to create a
+directory solely for this purpose that's owned by the user the wallet
+server will run as.
+
+KEYTAB_TMP must be set to use keytab objects.
+
+=cut
+
+our $KEYTAB_TMP;
+
+=back
+
+=head2 Retrieving Existing Keytabs
+
+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.
+
+For MIT Kerberos, the keytab object backend therefore optionally supports
+retrieving existing keys, and hence keytabs, for Kerberos principals by
+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.
+
+This is not required for Heimdal; for Heimdal, setting the C<unchanging>
+flag is all that's needed.
+
+=over 4
+
+=item KEYTAB_REMCTL_CACHE
+
+Specifies the ticket cache to use when retrieving existing keytabs from
+the KDC. This is only used to implement support for the C<unchanging>
+flag. The ticket cache must be for a principal with access to run
+C<keytab retrieve> via remctl on KEYTAB_REMCTL_HOST.
+
+=cut
+
+our $KEYTAB_REMCTL_CACHE;
+
+=item KEYTAB_REMCTL_HOST
+
+The host to which to connect with remctl to retrieve existing keytabs.
+This is only used to implement support for the C<unchanging> flag. This
+host must provide the C<keytab retrieve> command and KEYTAB_REMCTL_CACHE
+must also be set to a ticket cache for a principal with access to run that
+command.
+
+=cut
+
+our $KEYTAB_REMCTL_HOST;
+
+=item KEYTAB_REMCTL_PRINCIPAL
+
+The service principal to which to authenticate when retrieving existing
+keytabs. This is only used to implement support for the C<unchanging>
+flag. If this variable is not set, the default is formed by prepending
+C<host/> to KEYTAB_REMCTL_HOST. (Note that KEYTAB_REMCTL_HOST is not
+lowercased first.)
+
+=cut
+
+our $KEYTAB_REMCTL_PRINCIPAL;
+
+=item KEYTAB_REMCTL_PORT
+
+The port on KEYTAB_REMCTL_HOST to which to connect with remctl to retrieve
+existing keytabs. This is only used to implement support for the
+C<unchanging> flag. If this variable is not set, the default remctl port
+will be used.
+
+=cut
+
+our $KEYTAB_REMCTL_PORT;
+
+=back
+
+=head1 WEBAUTH KEYRING OBJECT CONFIGURATION
+
+These configuration variables only need to be set if you intend to use the
+C<wakeyring> object type (the Wallet::Object::WAKeyring class).
+
+=over 4
+
+=item WAKEYRING_BUCKET
+
+The directory into which to store WebAuth keyring objects. WebAuth
+keyring objects will be stored in subdirectories of this directory. See
+L<Wallet::Object::WAKeyring> for the full details of the naming scheme.
+This directory must be writable by the wallet server and the wallet server
+must be able to create subdirectories of it.
+
+WAKEYRING_BUCKET must be set to use WebAuth keyring objects.
+
+=cut
+
+our $WAKEYRING_BUCKET;
+
+=item WAKEYRING_REKEY_INTERVAL
+
+The interval, in seconds, at which new keys are generated in a keyring.
+The object implementation will try to arrange for there to be keys added
+to the keyring separated by this interval.
+
+It's useful to provide some interval to install the keyring everywhere
+that it's used before the key becomes inactive. Every keyring will
+therefore normally have at least three keys: one that's currently active,
+one that becomes valid in the future but less than
+WAKEYRING_REKEY_INTERVAL from now, and one that becomes valid between one
+and two of those intervals into the future. This means that one has twice
+this interval to distribute the keyring everywhere it is used.
+
+Internally, this is implemented by adding a new key that becomes valid in
+twice this interval from the current time if the newest key becomes valid
+at or less than this interval in the future.
+
+The default value is 60 * 60 * 24 (one day).
+
+=cut
+
+our $WAKEYRING_REKEY_INTERVAL = 60 * 60 * 24;
+
+=item WAKEYRING_PURGE_INTERVAL
+
+The interval, in seconds, from the key creation date after which keys are
+removed from the keyring. This is used to clean up old keys and finish
+key rotation. Keys won't be removed unless there are more than three keys
+in the keyring to try to keep a misconfiguration from removing all valid
+keys.
+
+The default value is 60 * 60 * 24 * 90 (90 days).
+
+=cut
+
+our $WAKEYRING_PURGE_INTERVAL = 60 * 60 * 24 * 90;
+
+=back
+
+=head1 LDAP ACL CONFIGURATION
+
+These configuration variables are only needed if you intend to use the
+C<ldap-attr> ACL type (the Wallet::ACL::LDAP::Attribute class). They
+specify the LDAP server and additional connection and data model
+information required for the wallet to check for the existence of
+attributes.
+
+=over 4
+
+=item LDAP_HOST
+
+The LDAP server name to use to verify LDAP ACLs. This variable must be
+set to use LDAP ACLs.
+
+=cut
+
+our $LDAP_HOST;
+
+=item LDAP_BASE
+
+The base DN under which to search for the entry corresponding to a
+principal. Currently, the wallet always does a full subtree search under
+this base DN. This variable must be set to use LDAP ACLs.
+
+=cut
+
+our $LDAP_BASE;
+
+=item LDAP_FILTER_ATTR
+
+The attribute used to find the entry corresponding to a principal. The
+LDAP entry containing this attribute with a value equal to the principal
+will be found and checked for the required attribute and value. If this
+variable is not set, the default is C<krb5PrincipalName>.
+
+=cut
+
+our $LDAP_FILTER_ATTR;
+
+=item LDAP_CACHE
+
+Specifies the Kerberos ticket cache to use when connecting to the LDAP
+server. GSS-API authentication is always used; there is currently no
+support for any other type of bind. The ticket cache must be for a
+principal with access to verify the values of attributes that will be used
+with this ACL type. This variable must be set to use LDAP ACLs.
+
+=cut
+
+our $LDAP_CACHE;
+
+=back
+
+Finally, depending on the structure of the LDAP directory being queried,
+there may not be any attribute in the directory whose value exactly
+matches the Kerberos principal. The attribute designated by
+LDAP_FILTER_ATTR may instead hold a transformation of the principal name
+(such as the principal with the local realm stripped off, or rewritten
+into an LDAP DN form). If this is the case, define a Perl function named
+ldap_map_principal. This function will be called whenever an LDAP
+attribute ACL is being verified. It will take one argument, the
+principal, and is expected to return the value to search for in the LDAP
+directory server.
+
+For example, if the principal name without the local realm is stored in
+the C<uid> attribute in the directory, set LDAP_FILTER_ATTR to C<uid> and
+then define ldap_map_attribute as follows:
+
+ sub ldap_map_principal {
+ my ($principal) = @_;
+ $principal =~ s/\@EXAMPLE\.COM$//;
+ return $principal;
+ }
+
+Note that this example only removes the local realm (here, EXAMPLE.COM).
+Any principal from some other realm will be left fully qualified, and then
+presumably will not be found in the directory.
+
+=head1 NETDB ACL CONFIGURATION
+
+These configuration variables are only needed if you intend to use the
+C<netdb> ACL type (the Wallet::ACL::NetDB class). They specify the remctl
+connection information for retrieving user roles from NetDB and the local
+realm to remove from principals (since NetDB normally expects unscoped
+local usernames).
+
+=over 4
+
+=item NETDB_REALM
+
+The wallet uses fully-qualified principal names (including the realm), but
+NetDB normally expects local usernames without the realm. If this
+variable is set, the given realm will be stripped from any principal names
+before passing them to NetDB. Principals in other realms will be passed
+to NetDB without modification.
+
+=cut
+
+our $NETDB_REALM;
+
+=item NETDB_REMCTL_CACHE
+
+Specifies the ticket cache to use when querying the NetDB remctl interface
+for user roles. The ticket cache must be for a principal with access to
+run C<netdb node-roles> via remctl on KEYTAB_REMCTL_HOST. This variable
+must be set to use NetDB ACLs.
+
+=cut
+
+our $NETDB_REMCTL_CACHE;
+
+=item NETDB_REMCTL_HOST
+
+The host to which to connect with remctl to query NetDB for user roles.
+This host must provide the C<netdb node-roles> command and
+NETDB_REMCTL_CACHE must also be set to a ticket cache for a principal with
+access to run that command. This variable must be set to use NetDB ACLs.
+
+=cut
+
+our $NETDB_REMCTL_HOST;
+
+=item NETDB_REMCTL_PRINCIPAL
+
+The service principal to which to authenticate when querying NetDB for
+user roles. If this variable is not set, the default is formed by
+prepending C<host/> to NETDB_REMCTL_HOST. (Note that NETDB_REMCTL_HOST is
+not lowercased first.)
+
+=cut
+
+our $NETDB_REMCTL_PRINCIPAL;
+
+=item NETDB_REMCTL_PORT
+
+The port on NETDB_REMCTL_HOST to which to connect with remctl to query
+NetDB for user roles. If this variable is not set, the default remctl
+port will be used.
+
+=cut
+
+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.
+
+=head1 NAMING ENFORCEMENT
+
+By default, wallet permits administrators to create objects of any name
+(unless the object backend rejects the name). However, naming standards
+for objects can be enforced, even for administrators, by defining a Perl
+function in the configuration file named verify_name. If such a function
+exists, it will be called for any object creation and will be passed the
+type of object, the object name, and the identity of the person doing the
+creation. If it returns undef or the empty string, object creation will
+be allowed. If it returns anything else, object creation is rejected and
+the return value is used as the error message.
+
+This function is also called for naming audits done via Wallet::Report
+to find any existing objects that violate a (possibly updated) naming
+policy. In this case, the third argument (the identity of the person
+creating the object) will be undef. As a general rule, if the third
+argument is undef, the function should apply the most liberal accepted
+naming policy so that the audit returns only objects that violate all
+naming policies, but some sites may wish different results for their audit
+reports.
+
+Please note that this return status is backwards from what one would
+normally expect. A false value is success; a true value is failure with
+an error message.
+
+For example, the following verify_name function would ensure that any
+keytab objects for particular principals have fully-qualified hostnames:
+
+ sub verify_name {
+ my ($type, $name, $user) = @_;
+ my %host_based = map { $_ => 1 }
+ qw(HTTP cifs host imap ldap nfs pop sieve smtp webauth);
+ return unless $type eq 'keytab';
+ return unless $name =~ m%/%;
+ my ($service, $instance) = split ('/', $name, 2);
+ return unless $host_based{$service};
+ return "host name $instance must be fully qualified"
+ unless $instance =~ /\./;
+ return;
+ }
+
+Objects that aren't of type C<keytab> or which aren't for a host-based key
+have no naming requirements enforced by this example.
+
+=head1 ACL NAMING ENFORCEMENT
+
+Similar to object names, by default wallet permits administrators to
+create ACLs with any name. However, naming standards for ACLs can be
+enforced by defining a Perl function in the configuration file named
+verify_acl_name. If such a function exists, it will be called for any ACL
+creation or rename and will be passed given the new ACL name and the
+identity of the person doing the creation. If it returns undef or the
+empty string, object creation will be allowed. If it returns anything
+else, object creation is rejected and the return value is used as the
+error message.
+
+This function is also called for naming audits done via Wallet::Report to
+find any existing objects that violate a (possibly updated) naming policy.
+In this case, the second argument (the identity of the person creating the
+ACL) will be undef. As a general rule, if the second argument is undef,
+the function should apply the most liberal accepted naming policy so that
+the audit returns only ACLs that violate all naming policies, but some
+sites may wish different results for their audit reports.
+
+Please note that this return status is backwards from what one would
+normally expect. A false value is success; a true value is failure with
+an error message.
+
+For example, the following verify_acl_name function would ensure that any
+ACLs created contain a slash and the part before the slash be one of
+C<host>, C<group>, C<user>, or C<service>.
+
+ sub verify_acl_name {
+ my ($name, $user) = @_;
+ return 'ACL names must contain a slash' unless $name =~ m,/,;
+ my ($first, $rest) = split ('/', $name, 2);
+ my %types = map { $_ => 1 } qw(host group user service);
+ unless ($types{$first}) {
+ return "unknown ACL type $first";
+ }
+ return;
+ }
+
+Obvious improvements could be made, such as checking that the part after
+the slash for a C<host/> ACL looked like a host name and the part after a
+slash for a C<user/> ACL look like a user name.
+
+=head1 ENVIRONMENT
+
+=over 4
+
+=item WALLET_CONFIG
+
+If this environment variable is set, it is taken to be the path to the
+wallet configuration file to load instead of F</etc/wallet/wallet.conf>.
+
+=back
+
+=cut
+
+# Now, load the configuration file so that it can override the defaults.
+if (-r $PATH) {
+ do $PATH or die (($@ || $!) . "\n");
+}
+
+1;
+__END__
+
+=head1 SEE ALSO
+
+DBI(3), Wallet::Object::Keytab(3), Wallet::Server(3), wallet-backend(8)
+
+This module is part of the wallet system. The current version is
+available from L<http://www.eyrie.org/~eagle/software/wallet/>.
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=cut