~ubuntu-branches/ubuntu/saucy/libpam-krb5/saucy-proposed

« back to all changes in this revision

Viewing changes to tests/tap/kerberos.c

  • Committer: Package Import Robot
  • Author(s): Russ Allbery
  • Date: 2012-06-02 19:20:27 UTC
  • mfrom: (23.1.2 quantal)
  • Revision ID: package-import@ubuntu.com-20120602192027-m77kkzwyeovfs4a4
Tags: 4.6-1
* New upstream release.
  - New anon_fast option to attempt anonymous authentication and use
    those credentials to provide FAST armor.  (Closes: #626509)
  - New user_realm option to set the realm for unqualified user
    principals without changing the default realm for all other
    operations.
  - New no_prompt option to suppress PAM prompting in favor of letting
    the Kerberos library handle it.  (Closes: #626506)
  - New silent option that duplicates the behavior of PAM_SILENT.
  - New trace option for preliminary support of Kerberos trace logging.
  - Fix the doubled colon in password prompts from Heimdal.
  - Preserve the realm of the authentication identity when forming an
    alt_auth_map identity.
  - Allow the alt_auth_map format to contain a realm to force all mapped
    principals to be in that realm.
  - Avoid a NULL pointer dereference if krb5_init_context fails.
    (LP: #998525)
  - Close memory leaks in search_k5login and alt_auth_map.
  - Suppress bogus error messages about the realm option.
  - Retry authentication under try_first_pass for several other error
    conditions.
* Regenerate the Autotools build system with dh-autoreconf.
* Add krb5-config to Build-Depends so that the test programs don't abort
  with errors about not having a Kerberos configuration.
* Switch to xz compression for the upstream and Debian tarballs.
* Enable parallel builds.
* Update standards version to 3.9.3 (no changes required).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * Utility functions for tests that use Kerberos.
3
3
 *
4
 
 * Currently only provides kerberos_setup(), which assumes a particular set of
5
 
 * data files in either the SOURCE or BUILD directories and, using those,
6
 
 * obtains Kerberos credentials, sets up a ticket cache, and sets the
7
 
 * environment variable pointing to the Kerberos keytab to use for testing.
 
4
 * The core function is kerberos_setup, which loads Kerberos test
 
5
 * configuration and returns a struct of information.  It also supports
 
6
 * obtaining initial tickets from the configured keytab and setting up
 
7
 * KRB5CCNAME and KRB5_KTNAME if a Kerberos keytab is present.  Also included
 
8
 * are utility functions for setting up a krb5.conf file and reporting
 
9
 * Kerberos errors or warnings during testing.
 
10
 *
 
11
 * Some of the functionality here is only available if the Kerberos libraries
 
12
 * are available.
8
13
 *
9
14
 * The canonical version of this file is maintained in the rra-c-util package,
10
15
 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
11
16
 *
12
17
 * Written by Russ Allbery <rra@stanford.edu>
13
 
 * Copyright 2006, 2007, 2009, 2010, 2011
 
18
 * Copyright 2006, 2007, 2009, 2010, 2011, 2012
14
19
 *     The Board of Trustees of the Leland Stanford Junior University
15
20
 *
16
21
 * Permission is hereby granted, free of charge, to any person obtaining a
33
38
 */
34
39
 
35
40
#include <config.h>
36
 
#include <portable/krb5.h>
 
41
#ifdef HAVE_KERBEROS
 
42
# include <portable/krb5.h>
 
43
#endif
37
44
#include <portable/system.h>
38
45
 
39
46
#include <sys/stat.h>
43
50
#include <tests/tap/process.h>
44
51
#include <tests/tap/string.h>
45
52
 
46
 
 
47
 
/*
48
 
 * These variables hold the allocated strings for the principal, the
49
 
 * environment to point to a different Kerberos ticket cache, keytab, and
50
 
 * configuration file, and the temporary directories used.  We store them so
51
 
 * that we can free them on exit for cleaner valgrind output, making it easier
52
 
 * to find real memory leaks in the tested programs.
53
 
 */
54
 
static char *principal = NULL;
 
53
/*
 
54
 * Disable the requirement that format strings be literals, since it's easier
 
55
 * to handle the possible patterns for kinit commands as an array.
 
56
 */
 
57
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
 
58
 
 
59
 
 
60
/*
 
61
 * These variables hold the allocated configuration struct, the environment to
 
62
 * point to a different Kerberos ticket cache, keytab, and configuration file,
 
63
 * and the temporary directories used.  We store them so that we can free them
 
64
 * on exit for cleaner valgrind output, making it easier to find real memory
 
65
 * leaks in the tested programs.
 
66
 */
 
67
static struct kerberos_config *config = NULL;
55
68
static char *krb5ccname = NULL;
56
69
static char *krb5_ktname = NULL;
57
70
static char *krb5_config = NULL;
60
73
 
61
74
 
62
75
/*
63
 
 * Report a Kerberos error and bail out.
64
 
 */
65
 
void
66
 
bail_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
67
 
{
68
 
    const char *k5_msg = NULL;
69
 
    char *message;
70
 
    va_list args;
71
 
 
72
 
    if (ctx != NULL)
73
 
        k5_msg = krb5_get_error_message(ctx, code);
74
 
    va_start(args, format);
75
 
    bvasprintf(&message, format, args);
76
 
    va_end(args);
77
 
    if (k5_msg == NULL)
78
 
        bail("%s", message);
79
 
    else
80
 
        bail("%s: %s", message, k5_msg);
81
 
}
82
 
 
83
 
 
84
 
/*
85
 
 * Report a Kerberos error as a diagnostic to stderr.
86
 
 */
87
 
void
88
 
diag_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
89
 
{
90
 
    const char *k5_msg = NULL;
91
 
    char *message;
92
 
    va_list args;
93
 
 
94
 
    if (ctx != NULL)
95
 
        k5_msg = krb5_get_error_message(ctx, code);
96
 
    va_start(args, format);
97
 
    bvasprintf(&message, format, args);
98
 
    va_end(args);
99
 
    if (k5_msg == NULL)
100
 
        diag("%s", message);
101
 
    else
102
 
        diag("%s: %s", message, k5_msg);
103
 
    free(message);
104
 
    if (k5_msg != NULL)
105
 
        krb5_free_error_message(ctx, k5_msg);
106
 
}
107
 
 
108
 
 
109
 
/*
110
 
 * Clean up at the end of a test.  This removes the ticket cache and resets
111
 
 * and frees the memory allocated for the environment variables so that
112
 
 * valgrind output on test suites is cleaner.
113
 
 */
114
 
void
115
 
kerberos_cleanup(void)
116
 
{
117
 
    char *path;
118
 
 
119
 
    if (tmpdir_ticket != NULL) {
120
 
        basprintf(&path, "%s/krb5cc_test", tmpdir_ticket);
121
 
        unlink(path);
122
 
        free(path);
123
 
        test_tmpdir_free(tmpdir_ticket);
124
 
        tmpdir_ticket = NULL;
125
 
    }
126
 
    if (principal != NULL) {
127
 
        free(principal);
128
 
        principal = NULL;
129
 
    }
130
 
    putenv((char *) "KRB5CCNAME=");
131
 
    putenv((char *) "KRB5_KTNAME=");
132
 
    if (krb5ccname != NULL) {
133
 
        free(krb5ccname);
134
 
        krb5ccname = NULL;
135
 
    }
136
 
    if (krb5_ktname != NULL) {
137
 
        free(krb5_ktname);
138
 
        krb5_ktname = NULL;
139
 
    }
140
 
}
141
 
 
142
 
 
143
 
/*
144
 
 * Obtain Kerberos tickets for the principal specified in config/principal
145
 
 * using the keytab specified in config/keytab, both of which are presumed to
146
 
 * be in tests in either the build or the source tree.  Also sets KRB5_KTNAME
147
 
 * and KRB5CCNAME.
 
76
 * Obtain Kerberos tickets and fill in the principal config entry.
148
77
 *
149
 
 * Returns the contents of config/principal in newly allocated memory or NULL
150
 
 * if Kerberos tests are apparently not configured.  If Kerberos tests are
151
 
 * configured but something else fails, calls bail.
 
78
 * There are two implementations of this function, one if we have native
 
79
 * Kerberos libraries available and one if we don't.  Uses keytab to obtain
 
80
 * credentials, and fills in the cache member of the provided config struct.
152
81
 */
153
 
const char *
154
 
kerberos_setup(void)
 
82
#ifdef HAVE_KERBEROS
 
83
 
 
84
static void
 
85
kerberos_kinit(void)
155
86
{
156
 
    char *path, *name, *krbtgt;
157
 
    const char *realm;
 
87
    char *name, *krbtgt;
158
88
    krb5_error_code code;
159
89
    krb5_context ctx;
160
90
    krb5_ccache ccache;
162
92
    krb5_keytab keytab;
163
93
    krb5_get_init_creds_opt *opts;
164
94
    krb5_creds creds;
165
 
 
166
 
    /* If we were called before, clean up after the previous run. */
167
 
    if (principal != NULL)
168
 
        kerberos_cleanup();
169
 
 
170
 
    /* Find the keytab file. */
171
 
    path = test_file_path("config/keytab");
172
 
    if (path == NULL)
173
 
        return NULL;
 
95
    const char *realm;
174
96
 
175
97
    /*
176
98
     * Determine the principal corresponding to that keytab.  We copy the
180
102
    code = krb5_init_context(&ctx);
181
103
    if (code != 0)
182
104
        bail_krb5(ctx, code, "error initializing Kerberos");
183
 
    kprinc = kerberos_keytab_principal(ctx, path);
 
105
    kprinc = kerberos_keytab_principal(ctx, config->keytab);
184
106
    code = krb5_unparse_name(ctx, kprinc, &name);
185
107
    if (code != 0)
186
108
        bail_krb5(ctx, code, "error unparsing name");
187
109
    krb5_free_principal(ctx, kprinc);
188
 
    principal = bstrdup(name);
 
110
    config->principal = bstrdup(name);
189
111
    krb5_free_unparsed_name(ctx, name);
190
112
 
191
 
    /* Set the KRB5CCNAME and KRB5_KTNAME environment variables. */
192
 
    tmpdir_ticket = test_tmpdir();
193
 
    basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket);
194
 
    basprintf(&krb5_ktname, "KRB5_KTNAME=%s", path);
195
 
    putenv(krb5ccname);
196
 
    putenv(krb5_ktname);
197
 
 
198
113
    /* Now do the Kerberos initialization. */
199
114
    code = krb5_cc_default(ctx, &ccache);
200
115
    if (code != 0)
201
116
        bail_krb5(ctx, code, "error setting ticket cache");
202
 
    code = krb5_parse_name(ctx, principal, &kprinc);
 
117
    code = krb5_parse_name(ctx, config->principal, &kprinc);
203
118
    if (code != 0)
204
 
        bail_krb5(ctx, code, "error parsing principal %s", principal);
 
119
        bail_krb5(ctx, code, "error parsing principal %s", config->principal);
205
120
    realm = krb5_principal_get_realm(ctx, kprinc);
206
121
    basprintf(&krbtgt, "krbtgt/%s@%s", realm, realm);
207
 
    code = krb5_kt_resolve(ctx, path, &keytab);
 
122
    code = krb5_kt_resolve(ctx, config->keytab, &keytab);
208
123
    if (code != 0)
209
 
        bail_krb5(ctx, code, "cannot open keytab %s", path);
 
124
        bail_krb5(ctx, code, "cannot open keytab %s", config->keytab);
210
125
    code = krb5_get_init_creds_opt_alloc(ctx, &opts);
211
126
    if (code != 0)
212
127
        bail_krb5(ctx, code, "cannot allocate credential options");
230
145
    krb5_get_init_creds_opt_free(ctx, opts);
231
146
    krb5_free_context(ctx);
232
147
    free(krbtgt);
233
 
    test_file_path_free(path);
234
 
 
235
 
    /*
236
 
     * Register the cleanup function as an atexit handler so that the caller
237
 
     * doesn't have to worry about cleanup.
238
 
     */
239
 
    if (atexit(kerberos_cleanup) != 0)
240
 
        sysdiag("cannot register cleanup function");
241
 
 
242
 
    /* Return the principal. */
243
 
    return principal;
244
148
}
245
149
 
 
150
#else /* !HAVE_KERBEROS */
246
151
 
247
 
/*
248
 
 * Read a principal and password from config/password in the test suite
249
 
 * configuration and return it as a newly allocated kerberos_password struct.
250
 
 * Returns NULL if no configuration is present, and calls bail if there are
251
 
 * errors reading the configuration.  Free the result with
252
 
 * kerberos_config_password_free.
253
 
 */
254
 
struct kerberos_password *
255
 
kerberos_config_password(void)
 
152
static void
 
153
kerberos_kinit(void)
256
154
{
257
 
    char *path;
258
 
    char buffer[BUFSIZ];
259
 
    struct kerberos_password *config;
 
155
    static const char * const format[] = {
 
156
        "kinit --no-afslog -k -t %s %s >/dev/null 2>&1 </dev/null",
 
157
        "kinit -k -t %s %s >/dev/null 2>&1 </dev/null",
 
158
        "kinit -t %s %s >/dev/null 2>&1 </dev/null",
 
159
        "kinit -k -K %s %s >/dev/null 2>&1 </dev/null"
 
160
    };
260
161
    FILE *file;
 
162
    char *path;
 
163
    char principal[BUFSIZ], *command;
 
164
    size_t i;
 
165
    int status;
261
166
 
262
 
    config = bmalloc(sizeof(struct kerberos_password));
263
 
    path = test_file_path("config/password");
264
 
    if (path == NULL)
265
 
        return NULL;
 
167
    /* Read the principal corresponding to the keytab. */
 
168
    path = test_file_path("config/principal");
 
169
    if (path == NULL) {
 
170
        test_file_path_free(config->keytab);
 
171
        config->keytab = NULL;
 
172
        return;
 
173
    }
266
174
    file = fopen(path, "r");
267
 
    if (file == NULL)
268
 
        sysbail("cannot open %s", path);
269
 
    if (fgets(buffer, sizeof(buffer), file) == NULL)
 
175
    if (file == NULL) {
 
176
        test_file_path_free(path);
 
177
        return;
 
178
    }
 
179
    test_file_path_free(path);
 
180
    if (fgets(principal, sizeof(principal), file) == NULL)
270
181
        bail("cannot read %s", path);
271
 
    if (buffer[strlen(buffer) - 1] != '\n')
 
182
    fclose(file);
 
183
    if (principal[strlen(principal) - 1] != '\n')
272
184
        bail("no newline in %s", path);
273
 
    buffer[strlen(buffer) - 1] = '\0';
274
 
    config->principal = bstrdup(buffer);
275
 
    if (fgets(buffer, sizeof(buffer), file) == NULL)
276
 
        bail("cannot read password from %s", path);
277
 
    fclose(file);
278
 
    if (buffer[strlen(buffer) - 1] != '\n')
279
 
        bail("password too long in %s", path);
280
 
    buffer[strlen(buffer) - 1] = '\0';
281
 
    config->password = bstrdup(buffer);
282
 
    test_file_path_free(path);
283
 
 
284
 
    /*
285
 
     * Strip the realm from the principal and set realm and username.  This
286
 
     * is not strictly correct; it doesn't cope with escaped @-signs.  But
287
 
     * it's rather unlikely someone would use such a thing as a test
288
 
     * principal.
289
 
     */
290
 
    config->username = bstrdup(config->principal);
291
 
    config->realm = strchr(config->username, '@');
292
 
    if (config->realm == NULL)
293
 
        bail("test principal has no realm");
294
 
    *config->realm = '\0';
295
 
    config->realm++;
 
185
    principal[strlen(principal) - 1] = '\0';
 
186
    config->principal = bstrdup(principal);
 
187
 
 
188
    /* Now do the Kerberos initialization. */
 
189
    for (i = 0; i < ARRAY_SIZE(format); i++) {
 
190
        basprintf(&command, format[i], config->keytab, principal);
 
191
        status = system(command);
 
192
        free(command);
 
193
        if (status != -1 && WEXITSTATUS(status) == 0)
 
194
            break;
 
195
    }
 
196
    if (status == -1 || WEXITSTATUS(status) != 0)
 
197
        bail("cannot get Kerberos tickets");
 
198
}
 
199
 
 
200
#endif /* !HAVE_KERBEROS */
 
201
 
 
202
 
 
203
/*
 
204
 * Clean up at the end of a test.  This removes the ticket cache and resets
 
205
 * and frees the memory allocated for the environment variables so that
 
206
 * valgrind output on test suites is cleaner.
 
207
 */
 
208
void
 
209
kerberos_cleanup(void)
 
210
{
 
211
    char *path;
 
212
 
 
213
    if (tmpdir_ticket != NULL) {
 
214
        basprintf(&path, "%s/krb5cc_test", tmpdir_ticket);
 
215
        unlink(path);
 
216
        free(path);
 
217
        test_tmpdir_free(tmpdir_ticket);
 
218
        tmpdir_ticket = NULL;
 
219
    }
 
220
    if (config != NULL) {
 
221
        if (config->keytab != NULL) {
 
222
            test_file_path_free(config->keytab);
 
223
            free(config->principal);
 
224
            free(config->cache);
 
225
        }
 
226
        if (config->userprinc != NULL) {
 
227
            free(config->userprinc);
 
228
            free(config->username);
 
229
            free(config->password);
 
230
        }
 
231
        free(config);
 
232
        config = NULL;
 
233
    }
 
234
    if (krb5ccname != NULL) {
 
235
        putenv((char *) "KRB5CCNAME=");
 
236
        free(krb5ccname);
 
237
        krb5ccname = NULL;
 
238
    }
 
239
    if (krb5_ktname != NULL) {
 
240
        putenv((char *) "KRB5_KTNAME=");
 
241
        free(krb5_ktname);
 
242
        krb5_ktname = NULL;
 
243
    }
 
244
}
 
245
 
 
246
 
 
247
/*
 
248
 * Obtain Kerberos tickets for the principal specified in config/principal
 
249
 * using the keytab specified in config/keytab, both of which are presumed to
 
250
 * be in tests in either the build or the source tree.  Also sets KRB5_KTNAME
 
251
 * and KRB5CCNAME.
 
252
 *
 
253
 * Returns the contents of config/principal in newly allocated memory or NULL
 
254
 * if Kerberos tests are apparently not configured.  If Kerberos tests are
 
255
 * configured but something else fails, calls bail.
 
256
 */
 
257
struct kerberos_config *
 
258
kerberos_setup(enum kerberos_needs needs)
 
259
{
 
260
    char *path;
 
261
    char buffer[BUFSIZ];
 
262
    FILE *file = NULL;
 
263
 
 
264
    /* If we were called before, clean up after the previous run. */
 
265
    if (config != NULL)
 
266
        kerberos_cleanup();
 
267
    config = bcalloc(1, sizeof(struct kerberos_config));
 
268
 
 
269
    /*
 
270
     * If we have a config/keytab file, set the KRB5CCNAME and KRB5_KTNAME
 
271
     * environment variables and obtain initial tickets.
 
272
     */
 
273
    config->keytab = test_file_path("config/keytab");
 
274
    if (config->keytab == NULL) {
 
275
        if (needs == TAP_KRB_NEEDS_KEYTAB || needs == TAP_KRB_NEEDS_BOTH)
 
276
            skip_all("Kerberos tests not configured");
 
277
    } else {
 
278
        tmpdir_ticket = test_tmpdir();
 
279
        basprintf(&config->cache, "%s/krb5cc_test", tmpdir_ticket);
 
280
        basprintf(&krb5ccname, "KRB5CCNAME=%s/krb5cc_test", tmpdir_ticket);
 
281
        basprintf(&krb5_ktname, "KRB5_KTNAME=%s", config->keytab);
 
282
        putenv(krb5ccname);
 
283
        putenv(krb5_ktname);
 
284
        kerberos_kinit();
 
285
    }
 
286
 
 
287
    /*
 
288
     * If we have a config/password file, read it and fill out the relevant
 
289
     * members of our config struct.
 
290
     */
 
291
    path = test_file_path("config/password");
 
292
    if (path != NULL)
 
293
        file = fopen(path, "r");
 
294
    if (file == NULL) {
 
295
        if (needs == TAP_KRB_NEEDS_PASSWORD || needs == TAP_KRB_NEEDS_BOTH)
 
296
            skip_all("Kerberos tests not configured");
 
297
    } else {
 
298
        if (fgets(buffer, sizeof(buffer), file) == NULL)
 
299
            bail("cannot read %s", path);
 
300
        if (buffer[strlen(buffer) - 1] != '\n')
 
301
            bail("no newline in %s", path);
 
302
        buffer[strlen(buffer) - 1] = '\0';
 
303
        config->userprinc = bstrdup(buffer);
 
304
        if (fgets(buffer, sizeof(buffer), file) == NULL)
 
305
            bail("cannot read password from %s", path);
 
306
        fclose(file);
 
307
        if (buffer[strlen(buffer) - 1] != '\n')
 
308
            bail("password too long in %s", path);
 
309
        buffer[strlen(buffer) - 1] = '\0';
 
310
        config->password = bstrdup(buffer);
 
311
 
 
312
        /*
 
313
         * Strip the realm from the principal and set realm and username.
 
314
         * This is not strictly correct; it doesn't cope with escaped @-signs
 
315
         * or enterprise names.
 
316
         */
 
317
        config->username = bstrdup(config->userprinc);
 
318
        config->realm = strchr(config->username, '@');
 
319
        if (config->realm == NULL)
 
320
            bail("test principal has no realm");
 
321
        *config->realm = '\0';
 
322
        config->realm++;
 
323
    }
 
324
    if (path != NULL)
 
325
        test_file_path_free(path);
 
326
 
 
327
    /*
 
328
     * Register the cleanup function as an atexit handler so that the caller
 
329
     * doesn't have to worry about cleanup.
 
330
     */
 
331
    if (atexit(kerberos_cleanup) != 0)
 
332
        sysdiag("cannot register cleanup function");
 
333
 
 
334
    /* Return the configuration. */
296
335
    return config;
297
336
}
298
337
 
299
338
 
300
339
/*
301
 
 * Free a kerberos_password struct.  This knows about the allocation strategy
302
 
 * used by kerberos_config_password and frees accordingly.
303
 
 */
304
 
void
305
 
kerberos_config_password_free(struct kerberos_password *config)
306
 
{
307
 
    free(config->principal);
308
 
    free(config->username);
309
 
    free(config->password);
310
 
    free(config);
311
 
}
312
 
 
313
 
 
314
 
/*
315
 
 * Find the principal of the first entry of a keytab and return it.  The
316
 
 * caller is responsible for freeing the result with krb5_free_principal.
317
 
 * Exit on error.
318
 
 */
319
 
krb5_principal
320
 
kerberos_keytab_principal(krb5_context ctx, const char *path)
321
 
{
322
 
    krb5_keytab keytab;
323
 
    krb5_kt_cursor cursor;
324
 
    krb5_keytab_entry entry;
325
 
    krb5_principal princ;
326
 
    krb5_error_code status;
327
 
 
328
 
    status = krb5_kt_resolve(ctx, path, &keytab);
329
 
    if (status != 0)
330
 
        bail_krb5(ctx, status, "error opening %s", path);
331
 
    status = krb5_kt_start_seq_get(ctx, keytab, &cursor);
332
 
    if (status != 0)
333
 
        bail_krb5(ctx, status, "error reading %s", path);
334
 
    status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor);
335
 
    if (status == 0) {
336
 
        status = krb5_copy_principal(ctx, entry.principal, &princ);
337
 
        if (status != 0)
338
 
            bail_krb5(ctx, status, "error copying principal from %s", path);
339
 
        krb5_kt_free_entry(ctx, &entry);
340
 
    }
341
 
    if (status != 0)
342
 
        bail("no principal found in keytab file %s", path);
343
 
    krb5_kt_end_seq_get(ctx, keytab, &cursor);
344
 
    krb5_kt_close(ctx, keytab);
345
 
    return princ;
346
 
}
347
 
 
348
 
 
349
 
/*
350
340
 * Clean up the krb5.conf file generated by kerberos_generate_conf and free
351
341
 * the memory used to set the environment variable.  This doesn't fail if the
352
342
 * file and variable are already gone, allowing it to be harmlessly run
389
379
    char *path;
390
380
    const char *argv[3];
391
381
 
 
382
    if (tmpdir_conf != NULL)
 
383
        kerberos_cleanup_conf();
392
384
    path = test_file_path("data/generate-krb5-conf");
393
385
    if (path == NULL)
394
386
        bail("cannot find generate-krb5-conf");
403
395
    if (atexit(kerberos_cleanup_conf) != 0)
404
396
        sysdiag("cannot register cleanup function");
405
397
}
 
398
 
 
399
 
 
400
/*
 
401
 * The remaining functions in this file are only available if Kerberos
 
402
 * libraries are available.
 
403
 */
 
404
#ifdef HAVE_KERBEROS
 
405
 
 
406
 
 
407
/*
 
408
 * Report a Kerberos error and bail out.
 
409
 */
 
410
void
 
411
bail_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
 
412
{
 
413
    const char *k5_msg = NULL;
 
414
    char *message;
 
415
    va_list args;
 
416
 
 
417
    if (ctx != NULL)
 
418
        k5_msg = krb5_get_error_message(ctx, code);
 
419
    va_start(args, format);
 
420
    bvasprintf(&message, format, args);
 
421
    va_end(args);
 
422
    if (k5_msg == NULL)
 
423
        bail("%s", message);
 
424
    else
 
425
        bail("%s: %s", message, k5_msg);
 
426
}
 
427
 
 
428
 
 
429
/*
 
430
 * Report a Kerberos error as a diagnostic to stderr.
 
431
 */
 
432
void
 
433
diag_krb5(krb5_context ctx, krb5_error_code code, const char *format, ...)
 
434
{
 
435
    const char *k5_msg = NULL;
 
436
    char *message;
 
437
    va_list args;
 
438
 
 
439
    if (ctx != NULL)
 
440
        k5_msg = krb5_get_error_message(ctx, code);
 
441
    va_start(args, format);
 
442
    bvasprintf(&message, format, args);
 
443
    va_end(args);
 
444
    if (k5_msg == NULL)
 
445
        diag("%s", message);
 
446
    else
 
447
        diag("%s: %s", message, k5_msg);
 
448
    free(message);
 
449
    if (k5_msg != NULL)
 
450
        krb5_free_error_message(ctx, k5_msg);
 
451
}
 
452
 
 
453
 
 
454
/*
 
455
 * Find the principal of the first entry of a keytab and return it.  The
 
456
 * caller is responsible for freeing the result with krb5_free_principal.
 
457
 * Exit on error.
 
458
 */
 
459
krb5_principal
 
460
kerberos_keytab_principal(krb5_context ctx, const char *path)
 
461
{
 
462
    krb5_keytab keytab;
 
463
    krb5_kt_cursor cursor;
 
464
    krb5_keytab_entry entry;
 
465
    krb5_principal princ;
 
466
    krb5_error_code status;
 
467
 
 
468
    status = krb5_kt_resolve(ctx, path, &keytab);
 
469
    if (status != 0)
 
470
        bail_krb5(ctx, status, "error opening %s", path);
 
471
    status = krb5_kt_start_seq_get(ctx, keytab, &cursor);
 
472
    if (status != 0)
 
473
        bail_krb5(ctx, status, "error reading %s", path);
 
474
    status = krb5_kt_next_entry(ctx, keytab, &entry, &cursor);
 
475
    if (status == 0) {
 
476
        status = krb5_copy_principal(ctx, entry.principal, &princ);
 
477
        if (status != 0)
 
478
            bail_krb5(ctx, status, "error copying principal from %s", path);
 
479
        krb5_kt_free_entry(ctx, &entry);
 
480
    }
 
481
    if (status != 0)
 
482
        bail("no principal found in keytab file %s", path);
 
483
    krb5_kt_end_seq_get(ctx, keytab, &cursor);
 
484
    krb5_kt_close(ctx, keytab);
 
485
    return princ;
 
486
}
 
487
 
 
488
#endif /* HAVE_KERBEROS */