]> eyrie.org Git - kerberos/perl-kerberos.git/commitdiff
Fix password quality checking
authorRuss Allbery <eagle@eyrie.org>
Fri, 28 Feb 2014 23:31:43 +0000 (15:31 -0800)
committerRuss Allbery <eagle@eyrie.org>
Fri, 28 Feb 2014 23:31:43 +0000 (15:31 -0800)
When using the kadmin libraries in server mode, we have to do
password quality checking ourselves, since the library never does
it.  Add the required code to the chpass implementation, add a
test configuration, and test password quality.

lib/Authen/Kerberos/Kadmin.pm
lib/Authen/Kerberos/Kadmin.xs
t/data/kdb/kdc.conf
t/data/kdb/password-quality [new file with mode: 0755]
t/kadmin/heimdal.t

index d780a6c4c0ce568206a108e3865542f09332110a..1596358bb86564aa7635c19751b346a67d65da81 100644 (file)
@@ -154,7 +154,8 @@ or system-configured default will be used.
 
 =head1 INSTANCE METHODS
 
-All instance methods throw exceptions on any error.
+All instance methods throw Authen::Kerberos::Exception exceptions on any
+error.
 
 =over 4
 
@@ -162,6 +163,10 @@ All instance methods throw exceptions on any error.
 
 Change the Kerberos password for PRINCIPAL to PASSWORD.
 
+If password quality checking is enabled via the C<password_quality>
+parameter to the constructor, this method will fail and throw an exception
+on any password quality check failure.
+
 =back
 
 =cut
@@ -170,4 +175,8 @@ Change the Kerberos password for PRINCIPAL to PASSWORD.
 
 Russ Allbery <eagle@eyrie.org>
 
+=head1 SEE ALSO
+
+L<Authen::Kerberos::Exception>
+
 =cut
index 94f453d793fcce51dafdf6d6e8e9a65832e00412..10f17a4e95a039dec2b671e8258d930b4382ee98 100644 (file)
 
 #include <krb5.h>
 #include <kadm5/admin.h>
+#include <kadm5/kadm5_err.h>
 
 /*
  * Define a struct that wraps the kadmin API handle so that we can include
- * some other data structures that we need to use.
+ * some other data structures and configuration parameters that we need to
+ * use.
  */
 typedef struct {
     void *handle;
     krb5_context ctx;
+    bool quality;
 } *Authen__Kerberos__Kadmin;
 
 /* Used to check that an object argument to a function is not NULL. */
@@ -182,6 +185,7 @@ new(class, args)
     }
     self->ctx = ctx;
     self->handle = handle;
+    self->quality = quality;
     RETVAL = self;
 }
   OUTPUT:
@@ -209,11 +213,30 @@ chpass(self, principal, password)
   PREINIT:
     krb5_error_code code;
     krb5_principal princ = NULL;
+    krb5_data pwd_data;
+    const char *reason;
   CODE:
 {
     code = krb5_parse_name(self->ctx, principal, &princ);
     if (code != 0)
         kadmin_croak(self->ctx, code, "krb5_parse_name", FALSE);
+
+    /*
+     * If configured to do quality checking, we need to do that manually,
+     * since the server-side kadmin libraries never check quality.
+     */
+    if (self->quality) {
+        pwd_data.data = (char *) password;
+        pwd_data.length = strlen(password);
+        reason = kadm5_check_password_quality(self->ctx, princ, &pwd_data);
+        if (reason != NULL) {
+            krb5_set_error_message(self->ctx, KADM5_PASS_Q_DICT, "%s", reason);
+            kadmin_croak(self->ctx, KADM5_PASS_Q_DICT,
+                         "kadm5_check_password_quality", FALSE);
+        }
+    }
+
+    /* Do the actual password change. */
     code = kadm5_chpass_principal(self->handle, princ, password);
     krb5_free_principal(self->ctx, princ);
     if (code != 0)
index a39411ecb36d2e9b6ab33304ca6cf8932730467a..c27fdfc2f3b880b5590b13e5e6e5f82284b1ae73 100644 (file)
@@ -6,3 +6,7 @@
         dbname    = db:./heimdal
         realm     = TEST.EXAMPLE.COM
     }
+
+[password_quality]
+    policies         = external-check
+    external_program = ./t/data/kdb/password-quality
diff --git a/t/data/kdb/password-quality b/t/data/kdb/password-quality
new file mode 100755 (executable)
index 0000000..570d63c
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+#
+# Trivial Heimdal quality check program used for testing that quality checking
+# happens.  Accept any password other than "password".
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2014
+#     The Board of Trustees of the Leland Stanford Junior University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+use 5.010;
+use strict;
+use warnings;
+
+# Check whether the password is "password".
+while (defined(my $line = <STDIN>)) {
+    if ($line =~ m{ \A new-password: [ ] password \n \z }xms) {
+        warn "weak password\n"
+          or die "Cannot write to standard output: $!\n";
+        exit(0);
+    }
+}
+
+# Everything looks good.
+print {*STDOUT} "APPROVED\n"
+  or die "Cannot write to standard output: $!\n";
+exit(0);
index 77af47daa4cbb08d9f766a0b444a65ec824891f3..49ba4b169bfa9fa3a15ba007d3b35290fdcfe2af 100755 (executable)
@@ -31,7 +31,7 @@ use warnings;
 
 use File::Copy qw(copy);
 
-use Test::More tests => 6;
+use Test::More tests => 10;
 
 BEGIN {
     use_ok('Authen::Kerberos::Kadmin');
@@ -59,6 +59,7 @@ my $kadmin = Authen::Kerberos::Kadmin->new(
         db_name => 'db:./t/tmp/heimdal',
         realm   => 'TEST.EXAMPLE.COM',
         server  => 1,
+        password_quality => 1,
     }
 );
 isa_ok($kadmin, 'Authen::Kerberos::Kadmin');
@@ -69,6 +70,26 @@ ok(eval { $kadmin->chpass('test@TEST.EXAMPLE.COM', 'some password') },
     'Password change is successful');
 is($@, q{}, '...with no exception');
 
+# Test password change to something that should be rejected by the password
+# quality check.
+ok(
+    !eval { $kadmin->chpass('test@TEST.EXAMPLE.COM', 'password') },
+    'Password change to bad-quality password rejected'
+);
+my $error = $@;
+isa_ok($error, 'Authen::Kerberos::Exception', 'Thrown exception');
+my ($function, $message);
+if (ref($error) && $error->isa('Authen::Kerberos::Exception')) {
+    $function = $error->function;
+    $message  = $error->message;
+}
+is($function, 'kadm5_check_password_quality', '...with correct function');
+is(
+    $message,
+    'External password quality program failed: weak password',
+    '...and correct message'
+);
+
 # The same should fail if we attempt it with an unknown database.
 $kadmin = Authen::Kerberos::Kadmin->new(
     {