]> eyrie.org Git - kerberos/perl-kerberos.git/commitdiff
Initial version
authorRuss Allbery <eagle@eyrie.org>
Fri, 21 Feb 2014 18:53:50 +0000 (10:53 -0800)
committerRuss Allbery <eagle@eyrie.org>
Fri, 21 Feb 2014 18:53:50 +0000 (10:53 -0800)
Compiles and loads, but hasn't been tested and doesn't support the
configuration parameters required for effective testing.  The only
functionality implemented so far is server-mode support for the
Heimdal libkadm5srv library.

21 files changed:
.gitignore [new file with mode: 0644]
Build.PL [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
MANIFEST.SKIP [new file with mode: 0644]
lib/Authen/Kerberos/Exception.pm [new file with mode: 0644]
lib/Authen/Kerberos/Kadmin.pm [new file with mode: 0644]
lib/Authen/Kerberos/Kadmin.xs [new file with mode: 0644]
t/data/perl.conf [new file with mode: 0644]
t/data/perlcriticrc [new file with mode: 0644]
t/data/perltidyrc [new file with mode: 0644]
t/docs/pod-coverage.t [new file with mode: 0755]
t/docs/pod-spelling.t [new file with mode: 0755]
t/docs/pod.t [new file with mode: 0755]
t/docs/synopsis.t [new file with mode: 0755]
t/kadmin/basic.t [new file with mode: 0755]
t/misc/exception.t [new file with mode: 0755]
t/style/coverage.t [new file with mode: 0755]
t/style/critic.t [new file with mode: 0755]
t/style/minimum-version.t [new file with mode: 0755]
t/style/strict.t [new file with mode: 0755]
typemap [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..e84b718
--- /dev/null
@@ -0,0 +1,9 @@
+/Build
+/MANIFEST.bak
+/MYMETA.json
+/MYMETA.yml
+/_build/
+/blib/
+/cover_db
+/lib/Authen/Kerberos/Kadmin.c
+*.o
diff --git a/Build.PL b/Build.PL
new file mode 100644 (file)
index 0000000..678d0c1
--- /dev/null
+++ b/Build.PL
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+#
+# Build script for the Authen::Kerberos distribution.
+#
+# 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 autodie;
+use strict;
+use warnings;
+
+use Module::Build;
+
+# Basic package configuration.
+my $build = Module::Build->new(
+    module_name          => 'Authen::Kerberos',
+    dist_abstract        => 'Perl bindings for Kerberos libraries',
+    dist_author          => 'Russ Allbery <eagle@eyrie.org>',
+    dist_version         => '0.01',
+    license              => 'mit',
+    recursive_test_files => 1,
+
+    # XS configuration.
+    extra_linker_flags => ['-lkadm5srv'],
+
+    # Other package relationships.
+    configure_requires => {
+        'Module::Build' => '0.28',
+        autodie         => 0,
+        perl            => '5.010',
+    },
+    requires => {
+        autodie => 0,
+        perl    => '5.010',
+    },
+);
+
+# Generate the build script.
+$build->create_build_script;
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..0fea325
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,18 @@
+Build.PL
+lib/Authen/Kerberos/Exception.pm
+lib/Authen/Kerberos/Kadmin.pm
+lib/Authen/Kerberos/Kadmin.xs
+MANIFEST                       This list of files
+MANIFEST.SKIP
+t/data/perl.conf
+t/docs/pod-coverage.t
+t/docs/pod-spelling.t
+t/docs/pod.t
+t/docs/synopsis.t
+t/kadmin/basic.t
+t/misc/exception.t
+t/style/coverage.t
+t/style/critic.t
+t/style/minimum-version.t
+t/style/strict.t
+typemap
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
new file mode 100644 (file)
index 0000000..363981c
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- conf -*-
+
+# Avoid version control files.
+^\.git/
+
+# Avoid generated build files.
+\bblib/
+^lib/Authen/Kerberos/Kadmin\.c$
+\.o$
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\b_build/
+\bBuild.bat$
+\bBuild.COM$
+\bBUILD.COM$
+\bbuild.com$
+
+# Avoid temp and backup files.
+~$
+\.old$
+\#$
+\b\.#
+\.bak$
+\.tmp$
+\.#
+\.rej$
+
+# Avoid OS-specific files/dirs
+# Mac OSX metadata
+\B\.DS_Store
+# Mac OSX SMB mount metadata files
+\B\._
+
+# Avoid Devel::Cover and Devel::CoverX::Covered files.
+\bcover_db\b
+\bcovered\b
+
+# Avoid MYMETA files
+^MYMETA\.
+
+# Avoid archives of this distribution
+\bAuthen-Kerberos-[\d\.\_]+
diff --git a/lib/Authen/Kerberos/Exception.pm b/lib/Authen/Kerberos/Exception.pm
new file mode 100644 (file)
index 0000000..24e020f
--- /dev/null
@@ -0,0 +1,177 @@
+# Rich exception object for Kerberos operations.
+#
+# All Kerberos APIs, including ones on some subsidiary objects, throw an
+# Authen::Kerberos::Exception on any failure of the underlying Kerberos API
+# call.  This is a rich exception object that carries the Kerberos library
+# error message, failure code, and additional information.  This Perl class
+# defines the object and provides accessor methods to extract information from
+# it.
+#
+# These objects are constructed in the static kadmin_croak function defined in
+# Kadmin.xs.  Any changes to the code here should be reflected there and vice
+# versa.
+#
+# 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.
+
+package Authen::Kerberos::Exception;
+
+use 5.010;
+use strict;
+use warnings;
+
+use overload '""' => \&to_string, 'cmp' => \&spaceship;
+
+our $VERSION;
+
+# Set $VERSION in a BEGIN block for robustness.
+BEGIN {
+    $VERSION = '0.01';
+}
+
+# There is intentionally no constructor.  This object is thrown by the
+# Kerberos C API.
+
+# Basic accessors.
+sub function { my $self = shift; return $self->{function} }
+sub message  { my $self = shift; return $self->{message} }
+sub code     { my $self = shift; return $self->{code} }
+
+# A full verbose message with all the information from the exception.
+#
+# $self - Authen::Kerberos::Exception object
+#
+# Returns: A string version of the exception information.
+sub to_string {
+    my ($self)   = @_;
+    my $message  = $self->{message};
+    my $function = $self->{function};
+    my $file     = $self->{file};
+    my $line     = $self->{line};
+
+    # Construct the verbose message, trying to follow the normal Perl text
+    # exception format.
+    my $result = q{};
+    if (defined($function)) {
+        $result .= "$function: ";
+    }
+    $result .= $message;
+    if (defined $line) {
+        $result .= " at $file line $line";
+    }
+    return $result;
+}
+
+# The cmp implmenetation converts the exception to a string and then compares
+# it to the other argument.
+#
+# $self  - Authen::Kerberos::Exception object
+# $other - The other object (generally a string) to which to compare it
+# $swap  - True if the order needs to be swapped for a proper comparison
+#
+# Returns: -1, 0, or 1 per the cmp interface contract
+sub spaceship {
+    my ($self, $other, $swap) = @_;
+    my $string = $self->to_string;
+    if ($swap) {
+        return ($other cmp $string);
+    } else {
+        return ($string cmp $other);
+    }
+}
+
+1;
+
+__END__
+
+=for stopwords
+Allbery
+
+=head1 NAME
+
+Authen::Kerberos::Exception - Rich exception for Kerberos API method errors
+
+=head1 SYNOPSIS
+
+    my $kadmin = Authen::Kerberos::Kadmin->new;
+    if (!eval { $kadmin->chpass('foo', 'password') }) {
+        if (ref($@) eq 'Authen::Kerberos::Exception') {
+            my $e = $@;
+            print 'function: ', $e->function, "\n";
+            print 'code: ', $e->code, "\n";
+            print 'message: ', $e->message, "\n";
+            print "$e\n";
+            die $e->to_string;
+        }
+    }
+
+=head1 DESCRIPTION
+
+All Authen::Kerberos::Kadmin methods will throw an exception on error.
+Exceptions produced by the underlying C API call will be represented by a
+Authen::Kerberos::Exception object.
+
+You can use this object like you would normally use $@, including printing
+it out and doing string comparisons with it, and it will convert to the
+string representation of the complete error message.  But you can also
+access the structured data stored inside the exception by treating it as
+an object and using the methods defined below.
+
+=head1 METHODS
+
+=over 4
+
+=item code()
+
+Returns the Kerberos status code for the exception.
+
+=item function()
+
+Returns the name of the Kerberos C API function that failed and caused the
+exception to be raised.
+
+=item message()
+
+Returns the Kerberos error message.  This uses the underlying Kerberos API
+calls to try to recover additional data about the cause of the error.
+
+=item spaceship([STRING], [SWAP])
+
+This method is called if the exception object is compared to a string via
+cmp.  It will compare the given string to the verbose error message and
+return the result.  If SWAP is set, it will reverse the order to compare
+the given string to the verbose error.  (This is the normal interface
+contract for an overloaded C<cmp> implementation.)
+
+=item to_string()
+
+This method is called if the exception is interpolated into a string.  It
+can also be called directly to retrieve the default string form of the
+exception.
+
+=back
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=cut
diff --git a/lib/Authen/Kerberos/Kadmin.pm b/lib/Authen/Kerberos/Kadmin.pm
new file mode 100644 (file)
index 0000000..e384b23
--- /dev/null
@@ -0,0 +1,151 @@
+# Perl bindings for the kadmin API.
+#
+# This is the Perl boostrap file for the Authen::Kerberos::Kadmin module,
+# nearly all of which is implemented in XS.  For the actual source, see
+# Kadmin.xs.  This file contains the bootstrap and export code and the
+# documentation.
+#
+# Currently, this only provides an interface to the Heimdal libkadm5srv
+# library.  This module will eventually become a level of indirection that can
+# select from several XS modules to support both MIT Kerberos and Heimdal, and
+# both the libkadm5clnt and libkadm5srv libraries.
+#
+# 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.
+
+package Authen::Kerberos::Kadmin;
+
+use 5.010;
+use strict;
+use warnings;
+
+use base qw(DynaLoader);
+
+use Exporter qw(import);
+
+our $VERSION;
+
+# Set $VERSION in a BEGIN block for robustness.
+BEGIN {
+    $VERSION = '0.01';
+}
+
+# Load the binary module.
+bootstrap Authen::Kerberos::Kadmin $VERSION;
+
+1;
+__END__
+
+=for stopwords
+Allbery Heimdal KDC kadmin libkadm5srv
+
+=head1 NAME
+
+Authen::Kerberos::Kadmin - Perl bindings for Kerberos administration
+
+=head1 SYNOPSIS
+
+    use Authen::Kerberos::Kadmin;
+
+    my $kadmin = Authen::Kerberos::Kadmin->new(
+        {
+            password_quality => 1,
+            realm            => 'EXAMPLE.COM',
+            server           => 1,
+        }
+    );
+    my ($principal, $password);
+    $kadmin->chpass($principal, $password);
+
+=head1 REQUIREMENTS
+
+Perl 5.10 or later, a Heimdal KDC, and the Heimdal libkadm5srv library.
+
+=head1 DESCRIPTION
+
+Authen::Kerberos::Kadmin provides Perl bindings for the kadmin library
+API, used to perform Kerberos administrative actions such as creating and
+deleting principals or changing their keys or flags.
+
+Currently, this module only supports Heimdal and only supports the server
+mode, where the program is run on the KDC and makes changes directly to
+the Kerberos KDC database.  It also only supports the password change
+operation currently.  More functionality will be added later.
+
+=head1 CLASS METHODS
+
+All class methods throw exceptions on any error.
+
+=over 4
+
+=item new([ARGS])
+
+Create a new Authen::Kerberos::Kadmin object, which holds the internal
+library state.  All further operations must be done with this object.
+ARGS, if present, is an anonymous hash of configuration options.
+Supported options are:
+
+=over 4
+
+=item password_quality
+
+If set to a true value, the password quality check configuration will be
+loaded and password quality checking will be enabled.  If set to a false
+value or not present, passwords passed to chpass() will not be checked for
+quality.  Be aware that, with a Heimdal KDC, password history is normally
+done via the password quality interface, so not setting this option may
+also lead to bypassing history checks.
+
+=item realm
+
+The Kerberos realm in which to take administrative actions.
+
+=item server
+
+If set to a true value, use the server kadmin API instead of the client
+API.  This mode opens the Kerberos KDC database directly to make changes
+instead of using the kadmin network protocol.  Currently, this option must
+be present and set to a true value.
+
+=back
+
+=back
+
+=head1 INSTANCE METHODS
+
+All instance methods throw exceptions on any error.
+
+=over 4
+
+=item chpass(PRINCIPAL, PASSWORD)
+
+Change the Kerberos password for PRINCIPAL to PASSWORD.
+
+=back
+
+=cut
+
+=head1 AUTHOR
+
+Russ Allbery <eagle@eyrie.org>
+
+=cut
diff --git a/lib/Authen/Kerberos/Kadmin.xs b/lib/Authen/Kerberos/Kadmin.xs
new file mode 100644 (file)
index 0000000..5a2edaf
--- /dev/null
@@ -0,0 +1,189 @@
+/* -*- c -*-
+ * Perl bindings for the kadmin API
+ *
+ * This is an XS source file, suitable for processing by xsubpp, that
+ * generates Perl bindings for the Kerberos kadmin API (libkadm5srv or
+ * libkadm5clnt).  It also provides enough restructuring so that the C
+ * function calls can be treated as method calls on a kadmin connection
+ * object.
+ *
+ * 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.
+ */
+
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+
+#include <krb5.h>
+#include <kadm5/admin.h>
+
+/*
+ * Define a struct that wraps the kadmin API handle so that we can include
+ * some other data structures that we need to use.
+ */
+typedef struct {
+    void *handle;
+    krb5_context ctx;
+} *Authen__Kerberos__Kadmin;
+
+/* Used to check that an object argument to a function is not NULL. */
+#define CROAK_NULL(o, t, f)                     \
+    do {                                        \
+        if ((o) == NULL)                        \
+            croak(t " object is undef in " f);  \
+    } while (0);
+#define CROAK_NULL_SELF(o, t, f) CROAK_NULL((o), t, t "::" f)
+
+
+/*
+ * Turn a Kerberos error into a Perl exception.  If the destroy argument is
+ * true, free the Kerberos context after setting up the exception.  This is
+ * used in cases where we're croaking inside the constructor.
+ */
+static void __attribute__((__noreturn__))
+kadmin_croak(krb5_context ctx, krb5_error_code code, const char *function,
+             bool destroy)
+{
+    HV *hv;
+    SV *rv;
+    const char *message;
+
+    hv = newHV();
+    (void) hv_store(hv, "code", 6, newSViv(code), 0);
+    message = krb5_get_error_message(ctx, code);
+    (void) hv_store(hv, "message", 7, newSVpv(message, 0), 0);
+    krb5_free_error_message(ctx, message);
+    if (destroy)
+        krb5_free_context(ctx);
+    if (function != NULL)
+        (void) hv_store(hv, "function", 6, newSVpv(function, 0), 0);
+    if (CopLINE(PL_curcop)) {
+        (void) hv_store(hv, "line", 4, newSViv(CopLINE(PL_curcop)), 0);
+        (void) hv_store(hv, "file", 4, newSVpv(CopFILE(PL_curcop), 0), 0);
+    }
+    rv = newRV_noinc((SV *) hv);
+    sv_bless(rv, gv_stashpv("Authen::Kerberos::Exception", TRUE));
+    sv_setsv(get_sv("@", TRUE), sv_2mortal(rv));
+    croak(Nullch);
+}
+
+
+/* XS code below this point. */
+
+MODULE = Authen::Kerberos::Kadmin       PACKAGE = Authen::Kerberos::Kadmin
+
+PROTOTYPES: DISABLE
+
+
+Authen::Kerberos::Kadmin
+new(class, args)
+    const char *class
+    HV *args
+  PREINIT:
+    krb5_context ctx;
+    krb5_error_code code;
+    kadm5_config_params params;
+    SV **value = NULL;
+    void *handle;
+    Authen__Kerberos__Kadmin self;
+    bool quality = FALSE;
+  CODE:
+{
+    code = krb5_init_context(&ctx);
+    if (code != 0)
+        kadmin_croak(NULL, code, "krb5_init_context", FALSE);
+
+    /* Parse the arguments to the function, if any. */
+    memset(&params, 0, sizeof(params));
+    if (args != NULL) {
+        value = hv_fetchs(args, "realm", 0);
+        if (value != NULL) {
+            params.realm = SvPV_nolen(*value);
+            params.mask = KADM5_CONFIG_REALM;
+        }
+        value = hv_fetchs(args, "server", 0);
+        if (value == NULL || !SvTRUE(*value))
+            croak("server mode required in Authen::Kerberos::Kadmin::new");
+        value = hv_fetchs(args, "password_quality", 0);
+        if (value != NULL && SvTRUE(*value))
+            quality = TRUE;
+    }
+
+    /* Create the kadmin server handle. */
+    code = kadm5_init_with_password_ctx(ctx, "kadmin/admin", NULL, NULL,
+                                        &params,  KADM5_STRUCT_VERSION,
+                                        KADM5_API_VERSION_2, &handle);
+    if (code != 0)
+        kadmin_croak(ctx, code, "kadm5_init_with_password_ctx", TRUE);
+
+    /* Set up password quality checking if desired. */
+    if (quality)
+        kadm5_setup_passwd_quality_check(ctx, NULL, NULL);
+
+    /* Flesh out our internal data structure. */
+    self = malloc(sizeof(*self));
+    if (self == NULL) {
+        kadm5_destroy(handle);
+        krb5_free_context(ctx);
+        croak("cannot allocate memory");
+    }
+    self->ctx = ctx;
+    self->handle = handle;
+    RETVAL = self;
+}
+  OUTPUT:
+    RETVAL
+
+
+void
+DESTROY(self)
+    Authen::Kerberos::Kadmin self
+  CODE:
+{
+    if (self == NULL)
+        return;
+    kadm5_destroy(self->handle);
+    krb5_free_context(self->ctx);
+    free(self);
+}
+
+
+void
+chpass(self, principal, password)
+    Authen::Kerberos::Kadmin self
+    const char *principal
+    const char *password
+  PREINIT:
+    krb5_error_code code;
+    krb5_principal princ = NULL;
+  CODE:
+{
+    code = krb5_parse_name(self->ctx, principal, &princ);
+    if (code != 0)
+        kadmin_croak(self->ctx, code, "krb5_parse_name", FALSE);
+    code = kadm5_chpass_principal(self->handle, princ, password);
+    krb5_free_principal(self->ctx, princ);
+    if (code != 0)
+        kadmin_croak(self->ctx, code, "kadm5_chpass_principal", FALSE);
+    XSRETURN_YES;
+}
diff --git a/t/data/perl.conf b/t/data/perl.conf
new file mode 100644 (file)
index 0000000..e5c1e5c
--- /dev/null
@@ -0,0 +1,10 @@
+# Configuration for Perl tests.  -*- perl -*-
+
+# The level of coverage achieved by the test suite.
+$COVERAGE_LEVEL = 100;
+
+# Default minimum version requirement.
+$MINIMUM_VERSION = '5.010';
+
+# File must end with this line.
+1;
diff --git a/t/data/perlcriticrc b/t/data/perlcriticrc
new file mode 100644 (file)
index 0000000..b05bb38
--- /dev/null
@@ -0,0 +1,100 @@
+# -*- conf -*-
+#
+# Default configuration for perlcritic.  Be sure to copy this into the source
+# for packages that run perlcritic tests automatically during the build for
+# reproducible test results.
+#
+# This file has been updated to match perlcritic 1.118.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2011, 2012, 2013
+#     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.
+
+severity = 1
+verbose  = %f:%l:%c: [%p] %m (%e, Severity: %s)\n
+
+# I prefer this policy (a lot, actually), but other people in my group at
+# Stanford really didn't like it, so this is my compromise to agree with a
+# group coding style.
+[-CodeLayout::ProhibitParensWithBuiltins]
+
+# Stanford's coding style allows postfix unless for flow control.  There
+# doesn't appear to be any way to allow it only for flow control (the logic
+# for "if" and "when" appears to be special-cased), so we have to allow unless
+# globally.
+[ControlStructures::ProhibitPostfixControls]
+allow = unless
+
+# This is handled with a separate test case that uses Test::Spelling.
+[-Documentation::PodSpelling]
+
+# Pod::Man and Pod::Text fixed this bug years ago.  I know, I maintain them.
+[-Documentation::RequirePodLinksIncludeText]
+
+# The POD sections Perl::Critic wants are incompatible with the POD template
+# from perlpodstyle, which is what I use for my POD documentation.
+[-Documentation::RequirePodSections]
+
+# This problem was fixed in Perl 5.14, which now properly preserves the value
+# of $@ even if destructors run at exit from the eval block.
+[-ErrorHandling::RequireCheckingReturnValueOfEval]
+
+# The default of 9 is too small and forces weird code contortions.
+[InputOutput::RequireBriefOpen]
+lines = 25
+
+# This is correct 80% of the time, but it isn't correct for a lot of scripts
+# inside packages, where maintaining $VERSION isn't worth the effort.
+# Unfortunately, there's no way to override it, so it gets turned off
+# globally.
+[-Modules::RequireVersionVar]
+
+# This sounds interesting but is actually useless.  Any large blocks of
+# literal text, which does not add to the complexity of the regex, will set it
+# off.
+[-RegularExpressions::ProhibitComplexRegexes]
+
+# I generally don't want to require Readonly as a prerequisite for all my Perl
+# modules.
+[-ValuesAndExpressions::ProhibitConstantPragma]
+
+# A good idea, but there are too many places where this would be more
+# confusing than helpful.  Pull out numbers if one might change them
+# independent of the algorithm, but don't do so for mathematical formulae.
+[-ValuesAndExpressions::ProhibitMagicNumbers]
+
+# Increase this to six digits so that I'm not told to add underscores to
+# port numbers (which is just silly).
+[ValuesAndExpressions::RequireNumberSeparators]
+min_value = 100000
+
+# Text::Wrap has a broken interface that requires use of package variables.
+[Variables::ProhibitPackageVars]
+add_packages = Text::Wrap
+
+# use English was one of the worst ideas in the history of Perl.  It makes the
+# code slightly more readable for amateurs at the cost of confusing
+# experienced Perl programmers and sending people in futile quests for where
+# these magical global variables are defined.
+[-Variables::ProhibitPunctuationVars]
diff --git a/t/data/perltidyrc b/t/data/perltidyrc
new file mode 100644 (file)
index 0000000..64d7da1
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- conf -*-
+#
+# Default options for perltidy for proper Perl code reformatting.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+
+-bbao           # put line breaks before any operator
+-nbbc           # don't force blank lines before comments (bad for else blocks)
+-ce             # cuddle braces around else
+-l=79           # usually use 78, but don't want 79-long lines reformatted
+-pt=2           # don't add extra whitespace around parentheses
+-sbt=2          # ...or square brackets
+-sfs            # no space before semicolon in for (not that I use this form)
diff --git a/t/docs/pod-coverage.t b/t/docs/pod-coverage.t
new file mode 100755 (executable)
index 0000000..4f07c57
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+#
+# Test that all methods are documented in POD.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(use_prereq);
+use Test::RRA::Config qw(@POD_COVERAGE_EXCLUDE);
+
+use_prereq('Test::Pod::Coverage');
+
+# Test everything found in the distribution.
+all_pod_coverage_ok({ also_private => [@POD_COVERAGE_EXCLUDE] });
diff --git a/t/docs/pod-spelling.t b/t/docs/pod-spelling.t
new file mode 100755 (executable)
index 0000000..031f821
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+#
+# Check for spelling errors in POD documentation.
+#
+# Checks all POD files in a Perl distribution using Test::Spelling.  This test
+# is disabled unless RRA_MAINTAINER_TESTS is set, since spelling dictionaries
+# vary too much between environments.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(skip_unless_maintainer use_prereq);
+
+# Only run this test for the maintainer.
+skip_unless_maintainer('Spelling tests');
+
+# Load prerequisite modules.
+use_prereq('Test::Spelling');
+
+# Check all POD in the Perl distribution.  Add the examples directory if it
+# exists.  Also add any files in usr/bin or usr/sbin, which are widely used in
+# Stanford-internal packages.
+my @files = all_pod_files();
+if (-d 'examples') {
+    push(@files, all_pod_files('examples'));
+}
+for my $dir (qw(usr/bin usr/sbin)) {
+    if (-d $dir) {
+        push(@files, glob("$dir/*"));
+    }
+}
+
+# We now have a list of all files to check, so output a plan and run the
+# tests.  We can't use all_pod_files_spelling_ok because it refuses to check
+# non-Perl files and Stanford-internal packages have a lot of shell scripts
+# with POD documentation.
+plan tests => scalar(@files);
+for my $file (@files) {
+    pod_file_spelling_ok($file);
+}
diff --git a/t/docs/pod.t b/t/docs/pod.t
new file mode 100755 (executable)
index 0000000..549e2a3
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+#
+# Check all POD documents for POD formatting errors.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2012, 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(use_prereq);
+
+use_prereq('Test::Pod');
+
+# Check all POD in the Perl distribution.  Add the examples directory if it
+# exists.  Also add any files in usr/bin or usr/sbin, which are widely used in
+# Stanford-internal packages.
+my @files = all_pod_files();
+if (-d 'examples') {
+    push(@files, all_pod_files('examples'));
+}
+for my $dir (qw(usr/bin usr/sbin)) {
+    if (-d $dir) {
+        push(@files, glob("$dir/*"));
+    }
+}
+
+# We now have a list of all files to check, so output a plan and run the
+# tests.  We can't use all_pod_files_ok because it refuses to check non-Perl
+# files and Stanford-internal packages have a lot of shell scripts with POD
+# documentation.
+plan tests => scalar(@files);
+for my $file (@files) {
+    pod_file_ok($file);
+}
diff --git a/t/docs/synopsis.t b/t/docs/synopsis.t
new file mode 100755 (executable)
index 0000000..8db1a8b
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+#
+# Check the SYNOPSIS section of the documentation for syntax errors.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(use_prereq);
+
+use_prereq('Perl::Critic::Utils');
+use_prereq('Test::Synopsis');
+
+# The default Test::Synopsis all_synopsis_ok() function requires that the
+# module be in a lib directory.  Use Perl::Critic::Utils to find the modules
+# in blib, or lib if it doesn't exist.  However, strip out anything in
+# blib/script, since scripts use a different SYNOPSIS syntax.
+my @files = Perl::Critic::Utils::all_perl_files('blib');
+@files = grep { !m{blib/script/}xms } @files;
+if (!@files) {
+    @files = Perl::Critic::Utils::all_perl_files('lib');
+}
+plan tests => scalar @files;
+
+# Run the actual tests.
+for my $file (@files) {
+    synopsis_ok($file);
+}
diff --git a/t/kadmin/basic.t b/t/kadmin/basic.t
new file mode 100755 (executable)
index 0000000..f6399c2
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+#
+# Test suite for Authen::Kerberos::Kadmin basic functionality.
+#
+# 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;
+
+use Test::More tests => 1;
+
+BEGIN {
+    use_ok('Authen::Kerberos::Kadmin');
+}
diff --git a/t/misc/exception.t b/t/misc/exception.t
new file mode 100755 (executable)
index 0000000..89212d6
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+#
+# Test suite for Authen::Kerberos::Exception
+#
+# 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;
+
+use Test::More tests => 12;
+
+BEGIN {
+    use_ok('Authen::Kerberos::Exception');
+}
+
+# We have to create an exception by hand, since there is (intentionally) no
+# Perl interface to create one and currently we don't support any functions
+# that let us easily generate a real exception.
+my $exception = {
+    code     => -1_765_328_383,
+    message  => 'A Kerberos error message',
+    function => 'krb5_foo',
+    file     => 'exception.t',
+    line     => 10,
+};
+bless($exception, 'Authen::Kerberos::Exception');
+
+# Check the accessors.
+is($exception->code,     -1_765_328_383,             'code');
+is($exception->message,  'A Kerberos error message', 'message');
+is($exception->function, 'krb5_foo',                 'function');
+
+# Check the string representation.
+is($exception->to_string,
+    'krb5_foo: A Kerberos error message at exception.t line 10', 'to_string');
+is("$exception", 'krb5_foo: A Kerberos error message at exception.t line 10',
+    'stringify');
+
+# The function, file, and line are optional.  Try without them.
+delete $exception->{function};
+delete $exception->{file};
+delete $exception->{line};
+is($exception->to_string, 'A Kerberos error message', 'to_string short');
+is("$exception",          'A Kerberos error message', 'stringify short');
+
+# Check cmp.
+my $string = "$exception";
+is($exception cmp $string, 0,  'cmp equal');
+is($string cmp $exception, 0,  'cmp equal reversed');
+is($exception cmp 'Test',  -1, 'cmp unequal');
+is('Test' cmp $exception,  1,  'cmp unequal reversed');
diff --git a/t/style/coverage.t b/t/style/coverage.t
new file mode 100755 (executable)
index 0000000..4a617b9
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+#
+# Test Perl code for test coverage.
+#
+# Test coverage checking is disabled unless RRA_MAINTAINER_TESTS is set since
+# it takes a long time, is sensitive to the versions of various libraries,
+# and will not interfere with functionality.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use File::Spec;
+use Test::More;
+use Test::RRA qw(skip_unless_maintainer use_prereq);
+use Test::RRA::Config qw($COVERAGE_LEVEL @COVERAGE_SKIP_TESTS);
+
+# Skip code coverage unless doing maintainer testing.
+skip_unless_maintainer('Coverage testing');
+
+# Load required modules.
+use_prereq('Devel::Cover');
+use_prereq('Test::Strict');
+
+# Build a list of test directories to use for coverage.
+my %ignore = map { $_ => 1 } qw(docs style), @COVERAGE_SKIP_TESTS;
+opendir(my $testdir, 't') or BAIL_OUT("cannot open t: $!");
+my @t_dirs = readdir($testdir) or BAIL_OUT("cannot read t: $!");
+closedir($testdir) or BAIL_OUT("cannot close t: $!");
+
+# Filter out ignored and system directories.
+@t_dirs = grep { !$ignore{$_} } File::Spec->no_upwards(@t_dirs);
+
+# Prepend the t directory name to the directories.
+@t_dirs = map { File::Spec->catfile('t', $_) } @t_dirs;
+
+# Disable POD coverage; that's handled separately and is confused by
+# autoloading.
+$Test::Strict::DEVEL_COVER_OPTIONS
+  = '-coverage,statement,branch,condition,subroutine';
+
+# Do the coverage analysis.
+all_cover_ok($COVERAGE_LEVEL, @t_dirs);
+
+# Hack to suppress "used only once" warnings.
+END { $Test::Strict::DEVEL_COVER_OPTIONS = q{} }
diff --git a/t/style/critic.t b/t/style/critic.t
new file mode 100755 (executable)
index 0000000..f6334df
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+#
+# Check for perlcritic errors in all code.
+#
+# Checks all Perl code in blib/lib, t, and Makefile.PL for problems uncovered
+# by perlcritic.  This test is disabled unless RRA_MAINTAINER_TESTS is set,
+# since coding style will not interfere with functionality and newer versions
+# of perlcritic may introduce new checks.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(skip_unless_maintainer use_prereq);
+use Test::RRA::Config qw(@CRITIC_IGNORE);
+
+# Skip tests unless we're running the test suite in maintainer mode.
+skip_unless_maintainer('Coding style tests');
+
+# Load prerequisite modules.
+use_prereq('Perl::Critic::Utils');
+use_prereq('Test::Perl::Critic');
+
+# Force the embedded Perl::Tidy check to use the correct configuration.
+local $ENV{PERLTIDY} = 't/data/perltidyrc';
+
+# Import the configuration file and run Perl::Critic.
+Test::Perl::Critic->import(-profile => 't/data/perlcriticrc');
+
+# By default, Test::Perl::Critic only checks blib.  We also want to check t,
+# Build.PL, and examples.
+my @files = Perl::Critic::Utils::all_perl_files('blib');
+if (!@files) {
+    @files = Perl::Critic::Utils::all_perl_files('lib');
+}
+if (-f 'Build.PL') {
+    push(@files, 'Build.PL');
+}
+for my $dir (qw(examples usr t)) {
+    if (-d $dir) {
+        push(@files, Perl::Critic::Utils::all_perl_files($dir));
+    }
+}
+
+# Strip out Autoconf templates or left-over perltidy files.
+@files = grep { !m{ [.](?:in|tdy) }xms } @files;
+
+# Strip out ignored files.
+my %ignore = map { $_ => 1 } @CRITIC_IGNORE;
+@files = grep { !$ignore{$_} } @files;
+
+# Declare a plan now that we know what we're testing.
+plan tests => scalar @files;
+
+# Run the actual tests.
+for my $file (@files) {
+    critic_ok($file);
+}
diff --git a/t/style/minimum-version.t b/t/style/minimum-version.t
new file mode 100755 (executable)
index 0000000..3b34e59
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+#
+# Check that too-new features of Perl are not being used.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More;
+use Test::RRA qw(use_prereq);
+use Test::RRA::Config qw($MINIMUM_VERSION);
+
+use_prereq('Test::MinimumVersion');
+
+# Check all files in the Perl distribution.
+all_minimum_version_ok($MINIMUM_VERSION);
diff --git a/t/style/strict.t b/t/style/strict.t
new file mode 100755 (executable)
index 0000000..ea01334
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+#
+# Test Perl code for strict, warnings, and syntax.
+#
+# The canonical version of this file is maintained in the rra-c-util package,
+# which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2013
+#     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.006;
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use File::Spec;
+use Test::RRA qw(use_prereq);
+
+use_prereq('Test::Strict');
+
+# Test everything in the distribution directory except the Build and
+# Makefile.PL scripts generated by Module::Build.  We also want to check use
+# warnings.
+$Test::Strict::TEST_SKIP = ['Build', 'Makefile.PL'];
+$Test::Strict::TEST_WARNINGS = 1;
+all_perl_files_ok(File::Spec->curdir);
+
+# Hack to suppress "used only once" warnings.
+END {
+    $Test::Strict::TEST_SKIP     = [];
+    $Test::Strict::TEST_WARNINGS = 0;
+}
diff --git a/typemap b/typemap
new file mode 100644 (file)
index 0000000..b9176d3
--- /dev/null
+++ b/typemap
@@ -0,0 +1,47 @@
+# Typemap file for the Perl bindings to the Kerberos library.
+#
+# 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.
+
+TYPEMAP
+
+Authen::Kerberos::Kadmin        T_PTROBJ_NU
+
+INPUT
+
+T_PTROBJ_NU
+    if ($arg == &PL_sv_undef) {
+        $var = NULL;
+    } else if (sv_isa($arg, \"${ntype}\")) {
+        IV tmp = SvIV((SV *) SvRV($arg));
+        $var = INT2PTR($type, tmp);
+    } else {
+        croak(\"$var is not of type ${ntype}\");
+    }
+
+OUTPUT
+
+T_PTROBJ_NU
+    if ($var == NULL)
+        $arg = &PL_sv_undef;
+    else
+        sv_setref_pv($arg, \"${ntype}\", (void *) $var);