Exemplo n.º 1
0
/*
 * Internal helper function for remctld_start and remctld_start_fakeroot.
 *
 * Takes the Kerberos test configuration (the keytab principal is used as the
 * server principal), the configuration file to use (found via
 * test_file_path), and then any additional arguments to pass to remctld,
 * ending with a NULL.  Returns the PID of the running remctld process.  If
 * anything fails, calls bail.
 *
 * The path to remctld is obtained from the PATH_REMCTLD #define.  If this is
 * not set, remctld_start_internal calls skip_all.
 */
static struct process *
remctld_start_internal(struct kerberos_config *krbconf, const char *config,
                       va_list args, bool fakeroot)
{
    va_list args_copy;
    char *tmpdir, *pidfile, *confpath;
    size_t i, length;
    const char *arg, **argv;
    struct process *process;
    const char *path_remctld = PATH_REMCTLD;

    /* Check prerequisites. */
    if (path_remctld[0] == '\0')
        skip_all("remctld not found");

    /* Determine the location of the PID file and remove any old ones. */
    tmpdir = test_tmpdir();
    basprintf(&pidfile, "%s/remctld.pid", tmpdir);
    if (access(pidfile, F_OK) == 0)
        bail("remctld may already be running: %s exists", pidfile);

    /* Build the argv used to run remctld. */
    confpath = test_file_path(config);
    if (confpath == NULL)
        bail("cannot find remctld config %s", config);
    length = 11;
    va_copy(args_copy, args);
    while ((arg = va_arg(args_copy, const char *)) != NULL)
        length++;
    va_end(args_copy);
    argv = bmalloc(length * sizeof(const char *));
    i = 0;
    argv[i++] = path_remctld;
    argv[i++] = "-mdSF";
    argv[i++] = "-p";
    argv[i++] = "14373";
    argv[i++] = "-s";
    argv[i++] = krbconf->principal;
    argv[i++] = "-P";
    argv[i++] = pidfile;
    argv[i++] = "-f";
    argv[i++] = confpath;
    while ((arg = va_arg(args, const char *)) != NULL)
        argv[i++] = arg;
    argv[i] = NULL;

    /* Start remctld using process_start or process_start_fakeroot. */
    if (fakeroot)
        process = process_start_fakeroot(argv, pidfile);
    else
        process = process_start(argv, pidfile);

    /* Clean up and return. */
    test_file_path_free(confpath);
    free(pidfile);
    test_tmpdir_free(tmpdir);
    free(argv);
    return process;
}
Exemplo n.º 2
0
static void
kerberos_kinit(void)
{
    static const char * const format[] = {
        "kinit --no-afslog -k -t %s %s >/dev/null 2>&1 </dev/null",
        "kinit -k -t %s %s >/dev/null 2>&1 </dev/null",
        "kinit -t %s %s >/dev/null 2>&1 </dev/null",
        "kinit -k -K %s %s >/dev/null 2>&1 </dev/null"
    };
    FILE *file;
    char *path;
    char principal[BUFSIZ], *command;
    size_t i;
    int status;

    /* Read the principal corresponding to the keytab. */
    path = test_file_path("config/principal");
    if (path == NULL) {
        test_file_path_free(config->keytab);
        config->keytab = NULL;
        return;
    }
    file = fopen(path, "r");
    if (file == NULL) {
        test_file_path_free(path);
        return;
    }
    test_file_path_free(path);
    if (fgets(principal, sizeof(principal), file) == NULL)
        bail("cannot read %s", path);
    fclose(file);
    if (principal[strlen(principal) - 1] != '\n')
        bail("no newline in %s", path);
    principal[strlen(principal) - 1] = '\0';
    config->principal = bstrdup(principal);

    /* Now do the Kerberos initialization. */
    for (i = 0; i < ARRAY_SIZE(format); i++) {
        basprintf(&command, format[i], config->keytab, principal);
        status = system(command);
        free(command);
        if (status != -1 && WEXITSTATUS(status) == 0)
            break;
    }
    if (status == -1 || WEXITSTATUS(status) != 0)
        bail("cannot get Kerberos tickets");
}
Exemplo n.º 3
0
/*
 * Generate a krb5.conf file for testing and set KRB5_CONFIG to point to it.
 * The [appdefaults] section will be stripped out and the default realm will
 * be set to the realm specified, if not NULL.  This will use config/krb5.conf
 * in preference, so users can configure the tests by creating that file if
 * the system file isn't suitable.
 *
 * Depends on data/generate-krb5-conf being present in the test suite.
 */
void
kerberos_generate_conf(const char *realm)
{
    char *path;
    const char *argv[3];

    if (tmpdir_conf != NULL)
        kerberos_cleanup_conf();
    path = test_file_path("data/generate-krb5-conf");
    if (path == NULL)
        bail("cannot find generate-krb5-conf");
    argv[0] = path;
    argv[1] = realm;
    argv[2] = NULL;
    run_setup(argv);
    test_file_path_free(path);
    tmpdir_conf = test_tmpdir();
    basprintf(&krb5_config, "KRB5_CONFIG=%s/krb5.conf", tmpdir_conf);
    putenv(krb5_config);
    if (atexit(kerberos_cleanup_conf) != 0)
        sysdiag("cannot register cleanup function");
}
Exemplo n.º 4
0
/*
 * Obtain Kerberos tickets for the principal specified in config/principal
 * using the keytab specified in config/keytab, both of which are presumed to
 * be in tests in either the build or the source tree.  Also sets KRB5_KTNAME
 * and KRB5CCNAME.
 *
 * Returns the contents of config/principal in newly allocated memory or NULL
 * if Kerberos tests are apparently not configured.  If Kerberos tests are
 * configured but something else fails, calls bail.
 */
struct kerberos_config *
kerberos_setup(enum kerberos_needs needs)
{
    char *path;
    char buffer[BUFSIZ];
    FILE *file = NULL;

    /* If we were called before, clean up after the previous run. */
    if (config != NULL)
        kerberos_cleanup();
    config = bcalloc(1, sizeof(struct kerberos_config));

    /*
     * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME
     * environment variables and obtain initial tickets.
     */
    config->keytab = test_file_path("config/keytab");
    if (config->keytab == NULL) {
        if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH)
            skip_all("Kerberos tests not configured");
    } else {
        tmpdir_ticket = test_tmpdir();
        basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket);
        basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket);
        basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab);
        putenv(krb5ccname);
        putenv(krb5_ktname);
        kerberos_kinit();
    }

    /*
     * If we have a config/password file, read it and fill out the relevant
     * members of our config struct.
     */
    path = test_file_path("config/password");
    if (path != NULL)
        file = fopen(path, "r");
    if (file == NULL) {
        if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH)
            skip_all("Kerberos tests not configured");
    } else {
        if (fgets(buffer, sizeof(buffer), file) == NULL)
            bail("cannot read %s", path);
        if (buffer[strlen(buffer) - 1] != '\n')
            bail("no newline in %s", path);
        buffer[strlen(buffer) - 1] = '\0';
        config->userprinc = bstrdup(buffer);
        if (fgets(buffer, sizeof(buffer), file) == NULL)
            bail("cannot read password from %s", path);
        fclose(file);
        if (buffer[strlen(buffer) - 1] != '\n')
            bail("password too long in %s", path);
        buffer[strlen(buffer) - 1] = '\0';
        config->password = bstrdup(buffer);

        /*
         * Strip the realm from the principal and set realm and username.
         * This is not strictly correct; it doesn't cope with escaped @-signs
         * or enterprise names.
         */
        config->username = bstrdup(config->userprinc);
        config->realm = strchr(config->username, '@');
        if (config->realm == NULL)
            bail("test principal has no realm");
        *config->realm = '\0';
        config->realm++;
    }
    test_file_path_free(path);

    /*
     * If we have PKINIT configuration, read it and fill out the relevant
     * members of our config struct.
     */
    path = test_file_path("config/pkinit-principal");
    if (path != NULL)
        file = fopen(path, "r");
    if (file != NULL) {
        if (fgets(buffer, sizeof(buffer), file) == NULL)
            bail("cannot read %s", path);
        if (buffer[strlen(buffer) - 1] != '\n')
            bail("no newline in %s", path);
        buffer[strlen(buffer) - 1] = '\0';
        fclose(file);
        test_file_path_free(path);
        path = test_file_path("config/pkinit-cert");
        if (path != NULL) {
            config->pkinit_principal = bstrdup(buffer);
            config->pkinit_cert = bstrdup(path);
        }
    }
    test_file_path_free(path);
    if (config->pkinit_cert == NULL && (needs & TAP_KRB_NEEDS_PKINIT) != 0)
        skip_all("PKINIT tests not configured");

    /*
     * Register the cleanup function so that the caller doesn't have to do
     * explicit cleanup.
     */
    test_cleanup_register(kerberos_cleanup_handler);

    /* Return the configuration. */
    return config;
}
Exemplo n.º 5
0
int
main(void)
{
    pam_handle_t *pamh;
    struct pam_args *args;
    struct pam_conv conv = { NULL, NULL };
    bool status;
    struct vector *cells;
    char *program;
    struct output *seen;
    const char *argv_bool[2] = { NULL, NULL };
    const char *argv_err[2] = { NULL, NULL };
    const char *argv_empty[] = { NULL };
#ifdef HAVE_KRB5
    const char *argv_all[] = {
        "cells=stanford.edu,ir.stanford.edu", "debug", "expires=1d",
        "ignore_root", "minimum_uid=1000", "program=/bin/true"
    };
    char *krb5conf;
#else
    const char *argv_all[] = {
        "cells=stanford.edu,ir.stanford.edu", "debug", "expires=86400",
        "ignore_root", "minimum_uid=1000", "program=/bin/true"
    };
#endif

    if (pam_start("test", NULL, &conv, &pamh) != PAM_SUCCESS)
        sysbail("cannot create pam_handle_t");
    args = putil_args_new(pamh, 0);
    if (args == NULL)
        bail("cannot create PAM argument struct");

    plan(161);

    /* First, check just the defaults. */
    args->config = config_new();
    status = putil_args_defaults(args, options, optlen);
    ok(status, "Setting the defaults");
    ok(args->config->cells == NULL, "...cells default");
    is_int(false, args->config->debug, "...debug default");
    is_int(10, args->config->expires, "...expires default");
    is_int(true, args->config->ignore_root, "...ignore_root default");
    is_int(0, args->config->minimum_uid, "...minimum_uid default");
    ok(args->config->program == NULL, "...program default");

    /* Now parse an empty set of PAM arguments.  Nothing should change. */
    status = putil_args_parse(args, 0, argv_empty, options, optlen);
    ok(status, "Parse of empty argv");
    ok(args->config->cells == NULL, "...cells still default");
    is_int(false, args->config->debug, "...debug still default");
    is_int(10, args->config->expires, "...expires default");
    is_int(true, args->config->ignore_root, "...ignore_root still default");
    is_int(0, args->config->minimum_uid, "...minimum_uid still default");
    ok(args->config->program == NULL, "...program still default");

    /* Now, check setting everything. */
    status = putil_args_parse(args, 6, argv_all, options, optlen);
    ok(status, "Parse of full argv");
    if (args->config->cells == NULL)
        ok_block(4, false, "...cells is set");
    else {
        ok(args->config->cells != NULL, "...cells is set");
        is_int(2, args->config->cells->count, "...with two cells");
        is_string("stanford.edu", args->config->cells->strings[0],
                  "...first is stanford.edu");
        is_string("ir.stanford.edu", args->config->cells->strings[1],
                  "...second is ir.stanford.edu");
    }
    is_int(true, args->config->debug, "...debug is set");
    is_int(86400, args->config->expires, "...expires is set");
    is_int(true, args->config->ignore_root, "...ignore_root is set");
    is_int(1000, args->config->minimum_uid, "...minimum_uid is set");
    is_string("/bin/true", args->config->program, "...program is set");
    config_free(args->config);
    args->config = NULL;

    /* Test deep copying of defaults. */
    cells = vector_new();
    if (cells == NULL)
        sysbail("cannot allocate memory");
    vector_add(cells, "foo.com");
    vector_add(cells, "bar.com");
    options[0].defaults.list = cells;
    program = strdup("/bin/false");
    if (program == NULL)
        sysbail("cannot allocate memory");
    options[5].defaults.string = program;
    args->config = config_new();
    status = putil_args_defaults(args, options, optlen);
    ok(status, "Setting defaults with new defaults");
    if (args->config->cells == NULL)
        ok_block(4, false, "...cells is set");
    else {
        ok(args->config->cells != NULL, "...cells is set");
        is_int(2, args->config->cells->count, "...with two cells");
        is_string("foo.com", args->config->cells->strings[0],
                  "...first is foo.com");
        is_string("bar.com", args->config->cells->strings[1],
                  "...second is bar.com");
    }
    is_string("/bin/false", args->config->program,
              "...program is /bin/false");
    status = putil_args_parse(args, 6, argv_all, options, optlen);
    ok(status, "Parse of full argv after defaults");
    if (args->config->cells == NULL)
        ok_block(4, false, "...cells is set");
    else {
        ok(args->config->cells != NULL, "...cells is set");
        is_int(2, args->config->cells->count, "...with two cells");
        is_string("stanford.edu", args->config->cells->strings[0],
                  "...first is stanford.edu");
        is_string("ir.stanford.edu", args->config->cells->strings[1],
                  "...second is ir.stanford.edu");
    }
    is_int(true, args->config->debug, "...debug is set");
    is_int(86400, args->config->expires, "...expires is set");
    is_int(true, args->config->ignore_root, "...ignore_root is set");
    is_int(1000, args->config->minimum_uid, "...minimum_uid is set");
    is_string("/bin/true", args->config->program, "...program is set");
    is_string("foo.com", cells->strings[0], "...first cell after parse");
    is_string("bar.com", cells->strings[1], "...second cell after parse");
    is_string("/bin/false", program, "...string after parse");
    config_free(args->config);
    args->config = NULL;
    is_string("foo.com", cells->strings[0], "...first cell after free");
    is_string("bar.com", cells->strings[1], "...second cell after free");
    is_string("/bin/false", program, "...string after free");
    options[0].defaults.list = NULL;
    options[5].defaults.string = NULL;
    vector_free(cells);
    free(program);

    /* Test specifying the default for a vector parameter as a string. */
    options[0].type = TYPE_STRLIST;
    options[0].defaults.string = "foo.com,bar.com";
    args->config = config_new();
    status = putil_args_defaults(args, options, optlen);
    ok(status, "Setting defaults with string default for vector");
    if (args->config->cells == NULL)
        ok_block(4, false, "...cells is set");
    else {
        ok(args->config->cells != NULL, "...cells is set");
        is_int(2, args->config->cells->count, "...with two cells");
        is_string("foo.com", args->config->cells->strings[0],
                  "...first is foo.com");
        is_string("bar.com", args->config->cells->strings[1],
                  "...second is bar.com");
    }
    config_free(args->config);
    args->config = NULL;
    options[0].type = TYPE_LIST;
    options[0].defaults.string = NULL;

    /* Should be no errors so far. */
    ok(pam_output() == NULL, "No errors so far");

    /* Test various ways of spelling booleans. */
    args->config = config_new();
    TEST_BOOL("debug", args->config->debug, true);
    TEST_BOOL("debug=false", args->config->debug, false);
    TEST_BOOL("debug=true", args->config->debug, true);
    TEST_BOOL("debug=no", args->config->debug, false);
    TEST_BOOL("debug=yes", args->config->debug, true);
    TEST_BOOL("debug=off", args->config->debug, false);
    TEST_BOOL("debug=on", args->config->debug, true);
    TEST_BOOL("debug=0", args->config->debug, false);
    TEST_BOOL("debug=1", args->config->debug, true);
    TEST_BOOL("debug=False", args->config->debug, false);
    TEST_BOOL("debug=trUe", args->config->debug, true);
    TEST_BOOL("debug=No", args->config->debug, false);
    TEST_BOOL("debug=Yes", args->config->debug, true);
    TEST_BOOL("debug=OFF", args->config->debug, false);
    TEST_BOOL("debug=ON", args->config->debug, true);
    config_free(args->config);
    args->config = NULL;

    /* Test for various parsing errors. */
    args->config = config_new();
    TEST_ERROR("debug=", LOG_ERR,
               "invalid boolean in setting: debug=");
    TEST_ERROR("debug=truth", LOG_ERR,
               "invalid boolean in setting: debug=truth");
    TEST_ERROR("minimum_uid", LOG_ERR,
               "value missing for option minimum_uid");
    TEST_ERROR("minimum_uid=", LOG_ERR,
               "value missing for option minimum_uid=");
    TEST_ERROR("minimum_uid=foo", LOG_ERR,
               "invalid number in setting: minimum_uid=foo");
    TEST_ERROR("minimum_uid=1000foo", LOG_ERR,
               "invalid number in setting: minimum_uid=1000foo");
    TEST_ERROR("program", LOG_ERR, "value missing for option program");
    TEST_ERROR("cells", LOG_ERR, "value missing for option cells");
    config_free(args->config);
    args->config = NULL;

#ifdef HAVE_KRB5

    /* Test for Kerberos krb5.conf option parsing. */
    krb5conf = test_file_path("data/krb5-pam.conf");
    if (krb5conf == NULL)
        bail("cannot find data/krb5-pam.conf");
    if (setenv("KRB5_CONFIG", krb5conf, 1) < 0)
        sysbail("cannot set KRB5_CONFIG");
    krb5_free_context(args->ctx);
    status = krb5_init_context(&args->ctx);
    if (status != 0)
        bail("cannot parse test krb5.conf file");
    args->config = config_new();
    status = putil_args_defaults(args, options, optlen);
    ok(status, "Setting the defaults");
    status = putil_args_krb5(args, "testing", options, optlen);
    ok(status, "Options from krb5.conf");
    ok(args->config->cells == NULL, "...cells default");
    is_int(true, args->config->debug, "...debug set from krb5.conf");
    is_int(1800, args->config->expires, "...expires set from krb5.conf");
    is_int(true, args->config->ignore_root, "...ignore_root default");
    is_int(1000, args->config->minimum_uid,
           "...minimum_uid set from krb5.conf");
    ok(args->config->program == NULL, "...program default");
    status = putil_args_krb5(args, "other-test", options, optlen);
    ok(status, "Options from krb5.conf (other-test)");
    is_int(-1000, args->config->minimum_uid,
           "...minimum_uid set from krb5.conf other-test");

    /* Test with a realm set, which should expose more settings. */
    krb5_free_context(args->ctx);
    status = krb5_init_context(&args->ctx);
    if (status != 0)
        bail("cannot parse test krb5.conf file");
    args->realm = strdup("FOO.COM");
    if (args->realm == NULL)
        sysbail("cannot allocate memory");
    status = putil_args_krb5(args, "testing", options, optlen);
    ok(status, "Options from krb5.conf with FOO.COM");
    is_int(2, args->config->cells->count, "...cells count from krb5.conf");
    is_string("foo.com", args->config->cells->strings[0],
              "...first cell from krb5.conf");
    is_string("bar.com", args->config->cells->strings[1],
              "...second cell from krb5.conf");
    is_int(true, args->config->debug, "...debug set from krb5.conf");
    is_int(1800, args->config->expires, "...expires set from krb5.conf");
    is_int(true, args->config->ignore_root, "...ignore_root default");
    is_int(1000, args->config->minimum_uid,
           "...minimum_uid set from krb5.conf");
    is_string("/bin/false", args->config->program,
              "...program from krb5.conf");

    /* Test with a different realm. */
    free(args->realm);
    args->realm = strdup("BAR.COM");
    if (args->realm == NULL)
        sysbail("cannot allocate memory");
    status = putil_args_krb5(args, "testing", options, optlen);
    ok(status, "Options from krb5.conf with BAR.COM");
    is_int(2, args->config->cells->count, "...cells count from krb5.conf");
    is_string("bar.com", args->config->cells->strings[0],
              "...first cell from krb5.conf");
    is_string("foo.com", args->config->cells->strings[1],
              "...second cell from krb5.conf");
    is_int(true, args->config->debug, "...debug set from krb5.conf");
    is_int(1800, args->config->expires, "...expires set from krb5.conf");
    is_int(true, args->config->ignore_root, "...ignore_root default");
    is_int(1000, args->config->minimum_uid,
           "...minimum_uid set from krb5.conf");
    is_string("echo /bin/true", args->config->program,
              "...program from krb5.conf");
    config_free(args->config);
    args->config = config_new();
    status = putil_args_krb5(args, "other-test", options, optlen);
    ok(status, "Options from krb5.conf (other-test with realm)");
    ok(args->config->cells == NULL, "...cells is NULL");
    is_string("echo /bin/true", args->config->program,
              "...program from krb5.conf");
    config_free(args->config);
    args->config = NULL;

    /* Test for time parsing errors. */
    args->config = config_new();
    TEST_ERROR("expires=ft87", LOG_ERR,
               "bad time value in setting: expires=ft87");
    config_free(args->config);

    /* Test error reporting from the krb5.conf parser. */
    args->config = config_new();
    status = putil_args_krb5(args, "bad-number", options, optlen);
    ok(status, "Options from krb5.conf (bad-number)");
    seen = pam_output();
    is_string("invalid number in krb5.conf setting for minimum_uid: 1000foo",
              seen->lines[0].line, "...and correct error reported");
    is_int(LOG_ERR, seen->lines[0].priority, "...with correct priority");
    pam_output_free(seen);
    config_free(args->config);
    args->config = NULL;

    /* Test error reporting on times from the krb5.conf parser. */
    args->config = config_new();
    status = putil_args_krb5(args, "bad-time", options, optlen);
    ok(status, "Options from krb5.conf (bad-time)");
    seen = pam_output();
    if (seen == NULL)
        ok_block(2, false, "...no error output");
    else {
        is_string("invalid time in krb5.conf setting for expires: ft87",
                  seen->lines[0].line, "...and correct error reported");
        is_int(LOG_ERR, seen->lines[0].priority, "...with correct priority");
    }
    pam_output_free(seen);
    config_free(args->config);
    args->config = NULL;

    test_file_path_free(krb5conf);

#else /* !HAVE_KRB5 */

    skip_block(37, "Kerberos support not configured");

#endif

    putil_args_free(args);
    pam_end(pamh, 0);
    return 0;
}
Exemplo n.º 6
0
bool
kerberos_expire_password(const char *principal, time_t expires)
{
    char *path, *user;
    const char *realm;
    krb5_context ctx;
    krb5_principal admin = NULL;
    krb5_principal princ = NULL;
    krb5_error_code code;
    kadm5_config_params params;
    kadm5_principal_ent_rec ent;
    void *handle;
    bool okay = false;

    /* Set up for making our call. */
    path = test_file_path("config/admin-keytab");
    if (path == NULL)
        return false;
    code = krb5_init_context(&ctx);
    if (code != 0)
        bail_krb5(ctx, code, "error initializing Kerberos");
    admin = kerberos_keytab_principal(ctx, path);
    realm = krb5_principal_get_realm(ctx, admin);
    code = krb5_set_default_realm(ctx, realm);
    if (code != 0)
        bail_krb5(ctx, code, "cannot set default realm");
    code = krb5_unparse_name(ctx, admin, &user);
    if (code != 0)
        bail_krb5(ctx, code, "cannot unparse admin principal");
    code = krb5_parse_name(ctx, principal, &princ);
    if (code != 0)
        bail_krb5(ctx, code, "cannot parse principal %s", principal);

    /*
     * If the actual kadmin calls fail, we may be built with MIT Kerberos
     * against a Heimdal server or vice versa.  Return false to skip the
     * tests.
     */
    memset(&params, 0, sizeof(params));
    params.realm = (char *) realm;
    params.mask = KADM5_CONFIG_REALM;
    code = kadm5_init_with_skey_ctx(ctx, user, path, KADM5_ADMIN_SERVICE,
                                    &params, KADM5_STRUCT_VERSION,
                                    KADM5_API_VERSION, &handle);
    if (code != 0) {
        diag_krb5(ctx, code, "error initializing kadmin");
        goto done;
    }
    memset(&ent, 0, sizeof(ent));
    ent.principal = princ;
    ent.pw_expiration = expires;
    code = kadm5_modify_principal(handle, &ent, KADM5_PW_EXPIRATION);
    if (code == 0)
        okay = true;
    else
        diag_krb5(ctx, code, "error setting password expiration");

done:
    kadm5_destroy(handle);
    krb5_free_unparsed_name(ctx, user);
    krb5_free_principal(ctx, admin);
    krb5_free_principal(ctx, princ);
    krb5_free_context(ctx);
    test_file_path_free(path);
    return okay;
}