Commit 077ae1a3 authored by Russ Allbery's avatar Russ Allbery

Rework internal error handling to use Kerberos errors

Now that we've dropped the old API, we can drop the error handling
mode, which predates rich Kerberos errors.  Replace it with use of
krb5_set_error_message everywhere, and change a lot of functions
to return a krb5_error_code instead of a boolean or some special
status code.

As part of this change, all password changes are queued for Active
Directory if they fail regardless of the reason for the failure.
parent 12048e19
This diff is collapsed.
......@@ -36,7 +36,7 @@
* argument to this function. Returns 0 on success, non-zero on failure.
* This function returns failure only if it could not allocate memory.
*/
int
krb5_error_code
pwupdate_init(struct plugin_config **result, krb5_context ctx)
{
struct plugin_config *config;
......@@ -44,7 +44,7 @@ pwupdate_init(struct plugin_config **result, krb5_context ctx)
/* Allocate our internal data. */
config = calloc(1, sizeof(struct plugin_config));
if (config == NULL)
return 1;
return sync_error_system(ctx, "cannot allocate memory");
/* Get Active Directory connection information from krb5.conf. */
sync_config_string(ctx, "ad_keytab", &config->ad_keytab);
......@@ -215,13 +215,13 @@ principal_allowed(struct plugin_config *config, krb5_context ctx,
* If the new password is NULL, that means that the keys are being randomized.
* Currently, we can't do anything in that case, so just skip it.
*/
int
krb5_error_code
pwupdate_precommit_password(struct plugin_config *config, krb5_context ctx,
krb5_principal principal,
const char *password, int pwlen,
char *errstr, int errstrlen)
const char *password, int pwlen)
{
int status;
krb5_error_code code;
const char *message;
if (config->ad_realm == NULL)
return 0;
......@@ -233,24 +233,19 @@ pwupdate_precommit_password(struct plugin_config *config, krb5_context ctx,
goto queue;
if (config->ad_queue_only)
goto queue;
status = pwupdate_ad_change(config, ctx, principal, password, pwlen,
errstr, errstrlen);
if (status == 3) {
syslog(LOG_INFO, "pwupdate: AD password change failed, queuing: %s",
errstr);
code = pwupdate_ad_change(config, ctx, principal, password, pwlen);
if (code != 0) {
message = krb5_get_error_message(ctx, code);
syslog(LOG_INFO, "krb5-sync: AD password change failed, queuing: %s",
message);
krb5_free_error_message(ctx, message);
goto queue;
}
return status;
return 0;
queue:
status = pwupdate_queue_write(config, ctx, principal, "ad", "password",
password);
if (status)
return 0;
else {
strlcpy(errstr, "queueing AD password change failed", errstrlen);
return 1;
}
return pwupdate_queue_write(config, ctx, principal, "ad", "password",
password);
}
......@@ -258,12 +253,11 @@ queue:
* Actions to take after the password is changed in the local database.
* Currently, there are none.
*/
int
krb5_error_code
pwupdate_postcommit_password(struct plugin_config *config UNUSED,
krb5_context ctx UNUSED,
krb5_principal principal UNUSED,
const char *password UNUSED, int pwlen UNUSED,
char *errstr UNUSED, int errstrlen UNUSED)
const char *password UNUSED, int pwlen UNUSED)
{
return 0;
}
......@@ -278,12 +272,11 @@ pwupdate_postcommit_password(struct plugin_config *config UNUSED,
* If a status change is already queued, or if making the status change fails,
* queue it for later processing.
*/
int
krb5_error_code
pwupdate_postcommit_status(struct plugin_config *config, krb5_context ctx,
krb5_principal principal, int enabled,
char *errstr, int errstrlen)
krb5_principal principal, int enabled)
{
int status;
krb5_error_code code;
if (config->ad_admin_server == NULL
|| config->ad_keytab == NULL
......@@ -296,19 +289,12 @@ pwupdate_postcommit_status(struct plugin_config *config, krb5_context ctx,
goto queue;
if (config->ad_queue_only)
goto queue;
status = pwupdate_ad_status(config, ctx, principal, enabled, errstr,
errstrlen);
if (status != 0)
code = pwupdate_ad_status(config, ctx, principal, enabled);
if (code != 0)
goto queue;
return status;
return 0;
queue:
status = pwupdate_queue_write(config, ctx, principal, "ad",
enabled ? "enable" : "disable", NULL);
if (status)
return 0;
else {
strlcpy(errstr, "queueing AD status change failed", errstrlen);
return 1;
}
return pwupdate_queue_write(config, ctx, principal, "ad",
enabled ? "enable" : "disable", NULL);
}
/*
* Error reporting routines.
* Store errors in the Kerberos context.
*
* Set the plugin error string based on a provided error message and an
* optional Kerberos error to append to the end of the string.
* Provides helper functions for the rest of the plugin code to store an error
* message in the Kerberos context.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2006, 2007, 2010, 2012
* Written by Russ Allbery <rra@stanford.edu>
* Copyright 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
*/
#include <config.h>
#include <portable/kadmin.h>
#include <portable/krb5.h>
#include <portable/system.h>
#include <errno.h>
#include <ldap.h>
#include <plugin/internal.h>
/*
* Given an error buffer, its length, a Kerberos context, a Kerberos error,
* and a format string, write the resulting error string into the buffer and
* append the Kerberos error.
* Internal helper function to set the Kerberos error message given a format,
* an error code, and a variable argument structure. Returns the error code
* set, which is normally the same as the one passed in, but which may change
* if we can't allocate memory.
*/
static krb5_error_code
set_error(krb5_context ctx, krb5_error_code code, const char *format,
va_list args)
{
char *message;
if (vasprintf(&message, format, args) < 0)
return sync_error_system(ctx, "cannot allocate memory");
krb5_set_error_message(ctx, code, "%s", message);
free(message);
return code;
}
/*
* Set the Kerberos error code to indicate a server configuration error and
* set the message to the format and arguments passed to this function.
*/
krb5_error_code
sync_error_config(krb5_context ctx, const char *format, ...)
{
va_list args;
krb5_error_code code;
va_start(args, format);
code = set_error(ctx, KADM5_MISSING_KRB5_CONF_PARAMS, format, args);
va_end(args);
return code;
}
/*
* Set the Kerberos error code to a generic kadmin failure error and the
* message to the format and arguments passed to this function. This is used
* for internal failures of various types.
*/
krb5_error_code
sync_error_generic(krb5_context ctx, const char *format, ...)
{
va_list args;
krb5_error_code code;
va_start(args, format);
code = set_error(ctx, KADM5_FAILURE, format, args);
va_end(args);
return code;
}
/*
* Set the Kerberos error code to a generic service unavailable error and the
* message to the format and arguments passed to this function with the LDAP
* error string appended.
*/
krb5_error_code
sync_error_ldap(krb5_context ctx, int code, const char *format, ...)
{
va_list args;
char *message;
bool okay = true;
int oerrno;
va_start(args, format);
if (vasprintf(&message, format, args) < 0) {
oerrno = errno;
krb5_set_error_message(ctx, errno, "cannot allocate memory: %s",
strerror(errno));
okay = false;
}
va_end(args);
if (!okay)
return oerrno;
krb5_set_error_message(ctx, KADM5_FAILURE, "%s: %s", message,
ldap_err2string(code));
free(message);
return KADM5_FAILURE;
}
/*
* Set the Kerberos error code to the current errno and the message to the
* format and arguments passed to this function.
*/
void
pwupdate_set_error(char *buffer, size_t length, krb5_context ctx,
krb5_error_code code, const char *format, ...)
krb5_error_code
sync_error_system(krb5_context ctx, const char *format, ...)
{
va_list args;
ssize_t used;
const char *message;
char *message;
bool okay = true;
int oerrno = errno;
va_start(args, format);
used = vsnprintf(buffer, length, format, args);
if (vasprintf(&message, format, args) < 0) {
oerrno = errno;
krb5_set_error_message(ctx, errno, "cannot allocate memory: %s",
strerror(errno));
okay = false;
}
va_end(args);
if (ctx == NULL || code == 0)
return;
if (used < 0 || (size_t) used >= length)
return;
message = krb5_get_error_message(ctx, code);
if (message != NULL)
snprintf(buffer + used, length - used, ": %s", message);
krb5_free_error_message(ctx, message);
if (!okay)
return oerrno;
krb5_set_error_message(ctx, oerrno, "%s: %s", message, strerror(oerrno));
free(message);
return oerrno;
}
......@@ -55,11 +55,7 @@ typedef struct kadm5_hook {
static krb5_error_code
init(krb5_context ctx, void **data)
{
krb5_error_code code = 0;
if (pwupdate_init((struct plugin_config **) data, ctx) != 0)
code = errno;
return code;
return pwupdate_init((struct plugin_config **) data, ctx);
}
......@@ -80,9 +76,8 @@ static krb5_error_code
chpass(krb5_context ctx, void *data, enum kadm5_hook_stage stage,
krb5_principal princ, const char *password)
{
char error[BUFSIZ];
size_t length;
int status = 0;
krb5_error_code code = 0;
/*
* If password is NULL, we have a new key set but no password (meaning
......@@ -95,18 +90,11 @@ chpass(krb5_context ctx, void *data, enum kadm5_hook_stage stage,
/* Dispatch to the appropriate function. */
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
status = pwupdate_precommit_password(data, ctx, princ, password,
length, error, sizeof(error));
code = pwupdate_precommit_password(data, ctx, princ, password, length);
else if (stage == KADM5_HOOK_STAGE_POSTCOMMIT)
status = pwupdate_postcommit_password(data, ctx, princ, password,
length, error, sizeof(error));
if (status == 0)
return 0;
else {
krb5_set_error_message(ctx, KADM5_FAILURE,
"cannot synchronize password: %s", error);
return KADM5_FAILURE;
}
code = pwupdate_postcommit_password(data, ctx, princ, password,
length);
return code;
}
......@@ -136,20 +124,12 @@ static krb5_error_code
modify(krb5_context ctx, void *data, enum kadm5_hook_stage stage,
kadm5_principal_ent_t entry, uint32_t mask)
{
char error[BUFSIZ];
int enabled, status;
int enabled;
if (mask & KADM5_ATTRIBUTES && stage == KADM5_HOOK_STAGE_POSTCOMMIT) {
enabled = !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX);
status = pwupdate_postcommit_status(data, ctx, entry->principal,
enabled, error, sizeof(error));
if (status == 0)
return 0;
else {
krb5_set_error_message(ctx, KADM5_FAILURE,
"cannot synchronize status: %s", error);
return KADM5_FAILURE;
}
return pwupdate_postcommit_status(data, ctx, entry->principal,
enabled);
}
return 0;
}
......
......@@ -44,26 +44,28 @@ BEGIN_DECLS
#pragma GCC visibility push(hidden)
/* General public API. */
int pwupdate_init(struct plugin_config **, krb5_context);
krb5_error_code pwupdate_init(struct plugin_config **, krb5_context);
void pwupdate_close(struct plugin_config *);
int pwupdate_precommit_password(struct plugin_config *, krb5_context,
krb5_principal, const char *password,
int pwlen, char *errstr, int errstrlen);
int pwupdate_postcommit_password(struct plugin_config *, krb5_context,
krb5_principal, const char *password,
int pwlen, char *errstr, int errstrlen);
int pwupdate_postcommit_status(struct plugin_config *, krb5_context,
krb5_principal, int enabled, char *errstr,
int errstrlen);
krb5_error_code pwupdate_precommit_password(struct plugin_config *,
krb5_context, krb5_principal,
const char *password,
int pwlen);
krb5_error_code pwupdate_postcommit_password(struct plugin_config *,
krb5_context, krb5_principal,
const char *password,
int pwlen);
krb5_error_code pwupdate_postcommit_status(struct plugin_config *,
krb5_context, krb5_principal,
int enabled);
/* Password changing. */
int pwupdate_ad_change(struct plugin_config *, krb5_context, krb5_principal,
const char *password, int pwlen, char *errstr,
int errstrlen);
krb5_error_code pwupdate_ad_change(struct plugin_config *, krb5_context,
krb5_principal, const char *password,
int pwlen);
/* Account status update. */
int pwupdate_ad_status(struct plugin_config *, krb5_context, krb5_principal,
int enabled, char *errstr, int errstrlen);
krb5_error_code pwupdate_ad_status(struct plugin_config *, krb5_context,
krb5_principal, int enabled);
/* Instance lookups. */
int pwupdate_instance_exists(struct plugin_config *, krb5_context,
......@@ -73,13 +75,10 @@ int pwupdate_instance_exists(struct plugin_config *, krb5_context,
int pwupdate_queue_conflict(struct plugin_config *, krb5_context,
krb5_principal, const char *domain,
const char *operation);
int pwupdate_queue_write(struct plugin_config *, krb5_context, krb5_principal,
const char *domain, const char *operation,
const char *password);
/* Error handling. */
void pwupdate_set_error(char *, size_t, krb5_context, krb5_error_code,
const char *, ...);
krb5_error_code pwupdate_queue_write(struct plugin_config *, krb5_context,
krb5_principal, const char *domain,
const char *operation,
const char *password);
/*
* Obtain configuration settings from krb5.conf. These are wrappers around
......@@ -92,6 +91,20 @@ void sync_config_boolean(krb5_context, const char *, bool *)
void sync_config_string(krb5_context, const char *, char **)
__attribute__((__nonnull__));
/*
* Store a configuration, generic, or system error in the Kerberos context,
* appending the strerror results to the message in the _system case and the
* LDAP error string in the _ldap case. Returns the error code set.
*/
krb5_error_code sync_error_config(krb5_context, const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 2, 3)));
krb5_error_code sync_error_generic(krb5_context, const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 2, 3)));
krb5_error_code sync_error_ldap(krb5_context, int, const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 3, 4)));
krb5_error_code sync_error_system(krb5_context, const char *format, ...)
__attribute__((__nonnull__, __format__(printf, 2, 3)));
/* Undo default visibility change. */
#pragma GCC visibility pop
......
......@@ -49,11 +49,7 @@ krb5_error_code kadm5_hook_krb5_sync_initvt(krb5_context, int, int,
static kadm5_ret_t
init(krb5_context ctx, kadm5_hook_modinfo **data)
{
krb5_error_code code = 0;
if (pwupdate_init((struct plugin_config **) data, ctx) != 0)
code = errno;
return code;
return pwupdate_init((struct plugin_config **) data, ctx);
}
......@@ -76,9 +72,8 @@ chpass(krb5_context ctx, kadm5_hook_modinfo *data, int stage,
int n_ks_tuple UNUSED, krb5_key_salt_tuple *ks_tuple UNUSED,
const char *password)
{
char error[BUFSIZ];
size_t length;
int status = 0;
krb5_error_code code = 0;
/*
* If password is NULL, we have a new key set but no password (meaning
......@@ -91,20 +86,12 @@ chpass(krb5_context ctx, kadm5_hook_modinfo *data, int stage,
/* Dispatch to the appropriate function. */
length = strlen(password);
if (stage == KADM5_HOOK_STAGE_PRECOMMIT)
status = pwupdate_precommit_password((struct plugin_config *) data,
ctx, princ, password,
length, error, sizeof(error));
code = pwupdate_precommit_password((struct plugin_config *) data,
ctx, princ, password, length);
else if (stage == KADM5_HOOK_STAGE_POSTCOMMIT)
status = pwupdate_postcommit_password((struct plugin_config *) data,
ctx, princ, password,
length, error, sizeof(error));
if (status == 0)
return 0;
else {
krb5_set_error_message(ctx, KADM5_FAILURE,
"cannot synchronize password: %s", error);
return KADM5_FAILURE;
}
code = pwupdate_postcommit_password((struct plugin_config *) data,
ctx, princ, password, length);
return code;
}
......@@ -135,21 +122,12 @@ static kadm5_ret_t
modify(krb5_context ctx, kadm5_hook_modinfo *data, int stage,
kadm5_principal_ent_t entry, long mask)
{
char error[BUFSIZ];
int enabled, status;
int enabled;
if (mask & KADM5_ATTRIBUTES && stage == KADM5_HOOK_STAGE_POSTCOMMIT) {
enabled = !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX);
status = pwupdate_postcommit_status((struct plugin_config *) data,
ctx, entry->principal,
enabled, error, sizeof(error));
if (status == 0)
return 0;
else {
krb5_set_error_message(ctx, KADM5_FAILURE,
"cannot synchronize status: %s", error);
return KADM5_FAILURE;
}
return pwupdate_postcommit_status((struct plugin_config *) data,
ctx, entry->principal, enabled);
}
return 0;
}
......
......@@ -9,7 +9,7 @@
* undone.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2006, 2007, 2010
* Copyright 2006, 2007, 2010, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
......@@ -34,12 +34,14 @@
#define MAX_QUEUE_STR "99"
/* Write out a string, checking that all of it was written. */
#define WRITE_CHECK(fd, s) \
do { \
ssize_t result; \
result = write((fd), (s), strlen(s)); \
if (result < 0 || (size_t) result != strlen(s)) \
goto fail; \
#define WRITE_CHECK(fd, s) \
do { \
ssize_t result; \
result = write((fd), (s), strlen(s)); \
if (result < 0 || (size_t) result != strlen(s)) { \
code = sync_error_system(ctx, "cannot write queue file"); \
goto fail; \
} \
} while (0)
......@@ -100,13 +102,13 @@ queue_prefix(krb5_context ctx, krb5_principal principal, const char *domain,
{
char *user = NULL, *prefix = NULL;
char *p;
krb5_error_code retval;
krb5_error_code code;
/* Enable and disable should go into the same queue. */
if (strcmp(operation, "disable") == 0)
operation = "enable";
retval = krb5_unparse_name(ctx, principal, &user);
if (retval != 0)
code = krb5_unparse_name(ctx, principal, &user);
if (code != 0)
return NULL;
p = strchr(user, '@');
if (p != NULL)
......@@ -204,9 +206,9 @@ fail:
/*
* Queue an action. Takes the plugin configuration, the Kerberos context, the
* principal, the domain, the operation, and a password (which may be NULL for
* enable and disable). Returns true on success, false on failure.
* enable and disable). Returns a Kerberos error code.
*/
int
krb5_error_code
pwupdate_queue_write(struct plugin_config *config, krb5_context ctx,
krb5_principal principal, const char *domain,
const char *operation, const char *password)
......@@ -214,26 +216,30 @@ pwupdate_queue_write(struct plugin_config *config, krb5_context ctx,
char *prefix = NULL, *timestamp = NULL, *path = NULL, *user = NULL;
char *p;
unsigned int i;
int status;
krb5_error_code code;
int lock = -1, fd = -1;
krb5_error_code retval;
if (config->queue_dir == NULL)
return 0;
return sync_error_config(ctx, "configuration setting queue_dir"
" missing");
prefix = queue_prefix(ctx, principal, domain, operation);
if (prefix == NULL)
return 0;
return sync_error_system(ctx, "cannot generate queue prefix");
/*
* Lock the queue before the timestamp so that another writer coming up
* at the same time can't get an earlier timestamp.
*/
lock = lock_queue(config);
if (lock < 0)
if (lock < 0) {
code = sync_error_system(ctx, "cannot lock queue");
goto fail;
}
timestamp = queue_timestamp();
if (timestamp == NULL)
if (timestamp == NULL) {
code = sync_error_system(ctx, "cannot generate timestamp");
goto fail;
}
/* Find a unique filename for the queue file. */
for (i = 0; i < MAX_QUEUE; i++) {
......@@ -241,10 +247,12 @@ pwupdate_queue_write(struct plugin_config *config, krb5_context ctx,
free(path);
path = NULL;
}
status = asprintf(&path, "%s/%s%s-%02d", config->queue_dir, prefix,
timestamp, i);
if (status < 0)
code = asprintf(&path, "%s/%s%s-%02d", config->queue_dir, prefix,
timestamp, i);
if (code < 0) {
code = sync_error_system(ctx, "cannot create queue file name");
goto fail;
}
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd >= 0)
break;
......@@ -254,8 +262,8 @@ pwupdate_queue_write(struct plugin_config *config, krb5_context ctx,
* Get the username from the principal and chop off the realm, dealing
* properly with escaped @ characters.
*/
retval = krb5_unparse_name(ctx, principal, &user);
if (retval != 0)
code = krb5_unparse_name(ctx, principal, &user);
if (code != 0)
goto fail;
for (p = user; *p != '\0'; p++) {
if (p[0] == '\\' && p[1] != '\0') {
......@@ -285,7 +293,7 @@ pwupdate_queue_write(struct plugin_config *config, krb5_context ctx,
free(prefix);
free(timestamp);
free(path);
return 1;
return 0;
fail:
if (fd >= 0) {
......@@ -303,5 +311,5 @@ fail:
free(timestamp);
if (path != NULL)
free(path);
return 0;
return code;
}
......@@ -50,6 +50,11 @@
# define KADM5_PASS_Q_GENERIC KADM5_PASS_Q_DICT
#endif
/* Heimdal doesn't define KADM5_MISSING_KRB5_CONF_PARAMS. */
#ifndef KADM5_MISSING_KRB5_CONF_PARAMS
# define KADM5_MISSING_KRB5_CONF_PARAMS KADM5_MISSING_CONF_PARAMS
#endif
/*
* Heimdal provides _ctx functions that take an existing context. MIT always
* requires the context be passed in. Code should use the _ctx variant, and
......
......@@ -2,7 +2,7 @@
* Tests for the Heimdal module API.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2012
* Copyright 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
......@@ -62,6 +62,7 @@ main(void)
void *data = NULL;
struct kadm5_hook *hook = NULL;
kadm5_principal_ent_rec entity;
const char *message;
krb5conf = test_file_path("data/krb5.conf");
if (krb5conf == NULL)
......@@ -123,10 +124,12 @@ main(void)
ok(data != NULL, "...and data is not NULL");
code = hook->chpass(ctx, data, KADM5_HOOK_STAGE_PRECOMMIT, princ,
"test");
is_int(KADM5_FAILURE, code, "chpass");
is_string("cannot synchronize password: queueing AD password change"
" failed", krb5_get_error_message(ctx, code),
"...with correct error message");
is_int(ENOENT, code, "chpass");
message = krb5_get_error_message(ctx, code);
is_int(strncmp("cannot lock queue", message,
strlen("cannot lock queue")),
0, "...with correct error message");
krb5_free_error_message(ctx, message);
/* Test chpass with a NULL password. */
code = hook->chpass(ctx, data, KADM5_HOOK_STAGE_PRECOMMIT, princ,
......@@ -142,21 +145,25 @@ main(void)
entity.attributes = KRB5_KDB_DISALLOW_ALL_TIX;
code = hook->create(ctx, data, KADM5_HOOK_STAGE_PRECOMMIT, &entity,
0, "test");
is_int(KADM5_FAILURE, code, "create");
is_string("cannot synchronize password: queueing AD password change"
" failed", krb5_get_error_message(ctx, code),
"...with correct error message");
is_int(ENOENT, code, "create");
message = krb5_get_error_message(ctx, code);
is_int(strncmp("cannot lock queue", message,
strlen("cannot lock queue")),
0, "...with correct error message");
krb5_free_error_message(ctx, message);
code = hook->modify(ctx, data, KADM5_HOOK_STAGE_POSTCOMMIT, &entity,
KADM5_ATTRIBUTES);
is_int(KADM5_FAILURE, code, "modify");
is_string("cannot synchronize status: queueing AD status change"
" failed", krb5_get_error_message(ctx, code),
"...with correct error message");
is_int(ENOENT, code, "modify");
message = krb5_get_error_message(ctx, code);
is_int(strncmp("cannot lock queue", message,
strlen("cannot lock queue")),
0, "...with correct error message");
krb5_free_error_message(ctx, message);
/* Test create with a NULL password. */
code = hook->create(ctx, data, KADM5_HOOK_STAGE_PRECOMMIT, &entity,
0, NULL);
is_int(0, code, "create");
is_int(0, code, "create with NULL password");
/* Close down the module. */
hook->fini(ctx, data);
......
......@@ -2,7 +2,7 @@
* Tests for the MIT Kerberos module API.
*
* Written by Russ Allbery <eagle@eyrie.org>
* Copyright 2012
* Copyright 2012, 2013
* The Board of Trustees of the Leland Stanford Junior University
*
* See LICENSE for licensing terms.
......@@ -48,6 +48,7 @@ main(void)
kadm5_hook_vftable_1 hook;
kadm5_hook_modinfo *data = NULL;
kadm5_principal_ent_rec entity;
const char *message;
krb5conf = test_file_path("data/krb5.conf");
if (krb5conf == NULL)
......@@ -115,10 +116,12 @@ main(void)
ok(data != NULL, "...and data is not NULL");
code = hook.chpass(ctx, data, KADM5_HOOK_STAGE_PRECOMMIT, princ,
false, 0, NULL, "test");