diff options
author | Jon Robertson <jonrober@stanford.edu> | 2015-02-07 13:59:34 -0800 |
---|---|---|
committer | Jon Robertson <jonrober@stanford.edu> | 2015-06-08 15:24:34 -0700 |
commit | 55875aa020f31751f295ae6c07547fe2949c5e82 (patch) | |
tree | eb176bde578b87b23fff239e3913a85d9cf7936f /perl/lib | |
parent | 0e16def8a9e12f9b2232b29da79cdacb6710b086 (diff) |
Added a new password object type
The password type inherits almost everything from the file object, but
if you try to get a password object that has never been stored, we
generate a random string to put in the object rather than just
erroring out. The maximum and minimum length of the string can be set
in the wallet config.
If a password object was stored earlier and then cleared out, we don't
generate another random string.
Change-Id: I17a65ca7dac9d4430e8a731f417297890ee612bb
Diffstat (limited to 'perl/lib')
-rw-r--r-- | perl/lib/Wallet/Admin.pm | 1 | ||||
-rw-r--r-- | perl/lib/Wallet/Config.pm | 43 | ||||
-rw-r--r-- | perl/lib/Wallet/Object/Password.pm | 210 |
3 files changed, 254 insertions, 0 deletions
diff --git a/perl/lib/Wallet/Admin.pm b/perl/lib/Wallet/Admin.pm index 8120e9c..a8b8368 100644 --- a/perl/lib/Wallet/Admin.pm +++ b/perl/lib/Wallet/Admin.pm @@ -131,6 +131,7 @@ sub default_data { [ 'duo-radius', 'Wallet::Object::Duo::RadiusProxy' ], [ 'duo-rdp', 'Wallet::Object::Duo::RDP' ], [ 'file', 'Wallet::Object::File' ], + [ 'password', 'Wallet::Object::Password' ], [ 'keytab', 'Wallet::Object::Keytab' ], [ 'wa-keyring', 'Wallet::Object::WAKeyring' ]); ($r1) = $self->{schema}->resultset('Type')->populate (\@record); diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm index 2eb57f9..76c7ecd 100644 --- a/perl/lib/Wallet/Config.pm +++ b/perl/lib/Wallet/Config.pm @@ -260,6 +260,49 @@ our $FILE_MAX_SIZE; =back +=head1 PASSWORD OBJECT CONFIGURATION + +These configuration variables only need to be set if you intend to use the +C<password> object type (the Wallet::Object::Password class). You will also +need to set the FILE_MAX_SIZE value from the file object configuration, as +that is inherited. + +=over 4 + +=item PWD_FILE_BUCKET + +The directory into which to store password objects. Password objects will +be stored in subdirectories of this directory. See +L<Wallet::Object::Password> 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. + +PWD_FILE_BUCKET must be set to use file objects. + +=cut + +our $PWD_FILE_BUCKET; + +=item PWD_LENGTH_MIN + +The minimum length for any auto-generated password objects created when get +is run before data is stored. + +=cut + +our $PWD_LENGTH_MIN = 20; + +=item PWD_LENGTH_MAX + +The maximum length for any auto-generated password objects created when get +is run before data is stored. + +=cut + +our $PWD_LENGTH_MAX = 21; + +=back + =head1 KEYTAB OBJECT CONFIGURATION These configuration variables only need to be set if you intend to use the diff --git a/perl/lib/Wallet/Object/Password.pm b/perl/lib/Wallet/Object/Password.pm new file mode 100644 index 0000000..d06c8a6 --- /dev/null +++ b/perl/lib/Wallet/Object/Password.pm @@ -0,0 +1,210 @@ +# Wallet::Object::Password -- Password object implementation for the wallet. +# +# Written by Jon Robertson <jonrober@stanford.edu> +# Copyright 2015 +# The Board of Trustees of the Leland Stanford Junior University +# +# See LICENSE for licensing terms. + +############################################################################## +# Modules and declarations +############################################################################## + +package Wallet::Object::Password; +require 5.006; + +use strict; +use warnings; +use vars qw(@ISA $VERSION); + +use Crypt::GeneratePassword qw(chars); +use Digest::MD5 qw(md5_hex); +use Wallet::Config (); +use Wallet::Object::File; + +@ISA = qw(Wallet::Object::File); + +# 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'; + +############################################################################## +# File naming +############################################################################## + +# Returns the path into which that password object will be stored or undef on +# error. On error, sets the internal error. +sub file_path { + my ($self) = @_; + my $name = $self->{name}; + unless ($Wallet::Config::PWD_FILE_BUCKET) { + $self->error ('password support not configured'); + return; + } + unless ($name) { + $self->error ('password objects may not have empty names'); + return; + } + my $hash = substr (md5_hex ($name), 0, 2); + $name =~ s/([^\w-])/sprintf ('%%%02X', ord ($1))/ge; + my $parent = "$Wallet::Config::PWD_FILE_BUCKET/$hash"; + unless (-d $parent || mkdir ($parent, 0700)) { + $self->error ("cannot create password bucket $hash: $!"); + return; + } + return "$Wallet::Config::PWD_FILE_BUCKET/$hash/$name"; +} + +############################################################################## +# Core methods +############################################################################## + +# Return the contents of the file. +sub get { + my ($self, $user, $host, $time) = @_; + $time ||= time; + my $id = $self->{type} . ':' . $self->{name}; + if ($self->flag_check ('locked')) { + $self->error ("cannot get $id: object is locked"); + return; + } + my $path = $self->file_path; + return unless $path; + + # If nothing is yet stored, generate a random password and save it to + # the file. + my $schema = $self->{schema}; + my %search = (ob_type => $self->{type}, + ob_name => $self->{name}); + my $object = $schema->resultset('Object')->find (\%search); + unless ($object->ob_stored_on) { + unless (open (FILE, '>', $path)) { + $self->error ("cannot store initial settings for $id: $!\n"); + return; + } + my $pass = chars ($Wallet::Config::PWD_LENGTH_MIN, + $Wallet::Config::PWD_LENGTH_MAX); + print FILE $pass; + $self->log_action ('store', $user, $host, $time); + unless (close FILE) { + $self->error ("cannot get $id: $!"); + return; + } + } + + unless (open (FILE, '<', $path)) { + $self->error ("cannot get $id: object has not been stored"); + return; + } + local $/; + my $data = <FILE>; + unless (close FILE) { + $self->error ("cannot get $id: $!"); + return; + } + $self->log_action ('get', $user, $host, $time); + return $data; +} + +1; +__END__ + +############################################################################## +# Documentation +############################################################################## + +=head1 NAME + +Wallet::Object::Password - Password object implementation for wallet + +=for stopwords +API HOSTNAME DATETIME keytab remctld backend nul Allbery wallet-backend + +=head1 SYNOPSIS + + my @name = qw(file mysql-lsdb) + my @trace = ($user, $host, time); + my $object = Wallet::Object::Password->create (@name, $schema, @trace); + unless ($object->store ("the-password\n")) { + die $object->error, "\n"; + } + my $password = $object->get (@trace); + $object->destroy (@trace); + +=head1 DESCRIPTION + +Wallet::Object::Password is an extension of Wallet::Object::File, +acting as a representation of simple file objects in the wallet. The +difference between the two is that if there is no data stored in a +password object when a user tries to get it for the first time, then a +random string suited for a password will be generated and put into the +object data. + +It implements the wallet object API and provides the necessary +glue to store a file on the wallet server, retrieve it later, and delete +it when the password object is deleted. + +To use this object, the configuration option specifying where on the +wallet server to store password objects must be set. See +L<Wallet::Config> for details on this configuration parameter and +information about how to set wallet configuration. + +=head1 METHODS + +This object mostly inherits from Wallet::Object::File. See the +documentation for that class for all generic methods. Below are only +those methods that are overridden or behave specially for this +implementation. + +=over 4 + +=item get(PRINCIPAL, HOSTNAME [, DATETIME]) + +Retrieves the current contents of the file object or undef on error. +store() must be called before get() will be successful. The caller should +call error() to get the error message if get() returns undef. PRINCIPAL, +HOSTNAME, and DATETIME are stored as history information. PRINCIPAL +should be the user who is downloading the keytab. If DATETIME isn't +given, the current time is used. + +=back + +=head1 FILES + +=over 4 + +=item PWD_FILE_BUCKET/<hash>/<file> + +Password files are stored on the wallet server under the directory +PWD_FILE_BUCKET as set in the wallet configuration. <hash> is the +first two characters of the hex-encoded MD5 hash of the wallet password +object name, used to not put too many files in the same directory. +<file> is the name of the password object with all characters other +than alphanumerics, underscores, and dashes replaced by C<%> and the +hex code of the character. + +=back + +=head1 LIMITATIONS + +The wallet implementation itself can handle arbitrary password object +names. However, due to limitations in the B<remctld> server usually +used to run B<wallet-backend>, password object names containing nul +characters (ASCII 0) may not be permitted. The file system used for +storing file objects may impose a length limitation on the +password object name. + +=head1 SEE ALSO + +remctld(8), Wallet::Config(3), Wallet::Object::File(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 + +Jon Robertson <jonrober@stanford.edu> + +=cut |