]> eyrie.org Git - kerberos/perl-kerberos.git/commitdiff
Add basic kadmin get support
authorRuss Allbery <rra@cpan.org>
Wed, 16 Apr 2014 07:19:22 +0000 (00:19 -0700)
committerRuss Allbery <rra@cpan.org>
Wed, 16 Apr 2014 07:19:22 +0000 (00:19 -0700)
Create an Authen::Kerberos::Kadmin::Entry object, add basic get
support to Authen::Kerberos::Kadmin to return it, and support a
last_password_change method.  Use that to test that the password
was really changed in the kadmin test suite.

lib/Authen/Kerberos/Kadmin.xs
t/kadmin/heimdal.t
typemap

index d351916f276fea340f394869f31a24b18b777d52..933dd564029480b2bbdc35212dc20da75c4cd60f 100644 (file)
@@ -55,6 +55,37 @@ typedef struct {
     bool quality;
 } *Authen__Kerberos__Kadmin;
 
+/*
+ * Wrapper for a kadmin principal entry.  We want to store the underlying
+ * Kerberos context, used to return some Kerberos data structures from the
+ * principal, and the mask, which stores which parameters we modified.
+ */
+typedef struct {
+    void *handle;
+    SV *ctx;
+    uint32_t mask;
+    kadm5_principal_ent_t ent;
+} *Authen__Kerberos__Kadmin__Entry;
+
+
+/*
+ * Given an SV containing a kadmin handle, return the underlying handle
+ * pointer for use with direct Kerberos calls.  Takes the type of object from
+ * which the context is being retrieved for error reporting.
+ */
+static void *
+handle_from_sv(SV *handle_sv, const char *type)
+{
+    IV handle_iv;
+    void *handle;
+
+    if (handle_sv == NULL)
+        croak("no Kerberos kadmin handle in %s object", type);
+    handle_iv = SvIV(handle_sv);
+    handle = INT2PTR(void *, handle_iv);
+    return handle;
+}
+
 
 /* XS code below this point. */
 
@@ -77,6 +108,7 @@ new(class, args)
     bool quality = FALSE;
     const char *config_file;
     char **files;
+    SV *sv;
   CODE:
 {
     code = krb5_init_context(&ctx);
@@ -145,7 +177,9 @@ new(class, args)
         krb5_free_context(ctx);
         croak("cannot allocate memory");
     }
-    self->ctx = sv_setref_pv(newSV(0), "Authen::Kerberos", ctx);
+    sv = sv_setref_pv(sv_newmortal(), "Authen::Kerberos", ctx);
+    self->ctx = SvRV(sv);
+    SvREFCNT_inc_simple_void_NN(self->ctx);
     self->handle = handle;
     self->quality = quality;
     RETVAL = self;
@@ -175,12 +209,12 @@ chpass(self, principal, password)
   PREINIT:
     krb5_context ctx;
     krb5_error_code code;
-    krb5_principal princ = NULL;
+    krb5_principal princ;
     krb5_data pwd_data;
     const char *reason;
   CODE:
 {
-    ctx = krb5_context_from_sv(SvRV(self->ctx), "Authen::Kerberos::Kadmin");
+    ctx = krb5_context_from_sv(self->ctx, "Authen::Kerberos::Kadmin");
     code = krb5_parse_name(ctx, principal, &princ);
     if (code != 0)
         krb5_croak(ctx, code, "krb5_parse_name", FALSE);
@@ -194,6 +228,7 @@ chpass(self, principal, password)
         pwd_data.length = strlen(password);
         reason = kadm5_check_password_quality(ctx, princ, &pwd_data);
         if (reason != NULL) {
+            krb5_free_principal(ctx, princ);
             krb5_set_error_message(ctx, KADM5_PASS_Q_DICT, "%s", reason);
             krb5_croak(ctx, KADM5_PASS_Q_DICT, "kadm5_check_password_quality",
                        FALSE);
@@ -207,3 +242,79 @@ chpass(self, principal, password)
         krb5_croak(ctx, code, "kadm5_chpass_principal", FALSE);
     XSRETURN_YES;
 }
+
+
+Authen::Kerberos::Kadmin::Entry
+get(self, principal)
+    Authen::Kerberos::Kadmin self
+    const char *principal
+  PREINIT:
+    krb5_context ctx;
+    krb5_error_code code;
+    krb5_principal princ;
+    kadm5_principal_ent_t ent;
+    uint32_t mask;
+    Authen__Kerberos__Kadmin__Entry entry;
+  CODE:
+{
+    ctx = krb5_context_from_sv(self->ctx, "Authen::Kerberos::Kadmin");
+    ent = calloc(1, sizeof(*ent));
+    if (ent == NULL)
+        croak("cannot allocate memory");
+    code = krb5_parse_name(ctx, principal, &princ);
+    if (code != 0)
+        krb5_croak(ctx, code, "krb5_parse_name", FALSE);
+
+    /* By default, get everything except the keys. */
+    mask = KADM5_PRINCIPAL_NORMAL_MASK;
+    code = kadm5_get_principal(self->handle, princ, ent, mask);
+    krb5_free_principal(ctx, princ);
+    if (code != 0)
+        krb5_croak(ctx, code, "kadm5_get_principal", TRUE);
+
+    /* Build our internal representation. */
+    entry = calloc(1, sizeof(*entry));
+    if (entry == NULL)
+        croak("cannot allocate memory");
+    entry->handle = SvRV(ST(0));
+    SvREFCNT_inc_simple_void_NN(entry->handle);
+    entry->ctx = self->ctx;
+    SvREFCNT_inc_simple_void_NN(entry->ctx);
+    entry->ent = ent;
+    RETVAL = entry;
+}
+  OUTPUT:
+    RETVAL
+
+
+
+MODULE = Authen::Kerberos::Kadmin    PACKAGE = Authen::Kerberos::Kadmin::Entry
+
+void
+DESTROY(self)
+    Authen::Kerberos::Kadmin::Entry self
+  PREINIT:
+    void *handle;
+  CODE:
+{
+    if (self == NULL)
+        return;
+    handle = handle_from_sv(self->handle, "Authen::Kerberos::Kadmin::Entry");
+    kadm5_free_principal_ent(handle, self->ent);
+    SvREFCNT_dec(self->handle);
+    SvREFCNT_dec(self->ctx);
+    free(self);
+}
+
+
+krb5_timestamp
+last_password_change(self)
+    Authen::Kerberos::Kadmin::Entry self
+  CODE:
+{
+    CROAK_NULL_SELF(self, "Authen::Kerberos::Kadmin::Entry",
+                    "last_password_change");
+    RETVAL = self->ent->last_pwd_change;
+}
+  OUTPUT:
+    RETVAL
index a5a22799302c678c4d3caf970cbe66156bd19bad..c63044e3ae9c9ae1fbf39de9838d05bcbd95bcb7 100755 (executable)
@@ -31,7 +31,7 @@ use warnings;
 
 use File::Copy qw(copy);
 
-use Test::More tests => 10;
+use Test::More tests => 13;
 
 BEGIN {
     use_ok('Authen::Kerberos::Kadmin');
@@ -62,12 +62,21 @@ my $kadmin = Authen::Kerberos::Kadmin->new(
 );
 isa_ok($kadmin, 'Authen::Kerberos::Kadmin');
 
+# Retrieve a known entry.
+my $entry = $kadmin->get('test@TEST.EXAMPLE.COM');
+isa_ok($entry, 'Authen::Kerberos::Kadmin::Entry');
+is($entry->last_password_change, 1393043331, 'Last password change time');
+
 # Test password change.  At the moment, we don't check whether the password
 # change is performed in the database.  We'll do that later.
 ok(eval { $kadmin->chpass('test@TEST.EXAMPLE.COM', 'some password') },
     'Password change is successful');
 is($@, q{}, '...with no exception');
 
+# Check that the last password change time was updated.
+$entry = $kadmin->get('test@TEST.EXAMPLE.COM');
+ok(time - $entry->last_password_change < 10, 'Last password change updated');
+
 # Test password change to something that should be rejected by the password
 # quality check.
 ok(
diff --git a/typemap b/typemap
index 38677dd00f0d3819364941f6918ab7d80b514409..02eeaa3c297b112e41d3c5d21c08e453efcf81d0 100644 (file)
--- a/typemap
+++ b/typemap
@@ -30,6 +30,7 @@ krb5_timestamp                  T_IV
 Authen::Kerberos                T_PTROBJ_NU
 Authen::Kerberos::Creds         T_PTROBJ_NU
 Authen::Kerberos::Kadmin        T_PTROBJ_NU
+Authen::Kerberos::Kadmin::Entry T_PTROBJ_NU
 Authen::Kerberos::Keytab        T_PTROBJ_NU
 Authen::Kerberos::KeytabEntry   T_PTROBJ_NU
 Authen::Kerberos::Principal     T_PTROBJ_NU