Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/read_keytab_file.c
Line
Count
Source
1
/* read_keytab_file.c
2
 * Routines for reading Kerberos keytab files
3
 * Copyright 2007, Anders Broman <anders.broman@ericsson.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
#include "config.h"
12
#include <wireshark.h>
13
14
#include "read_keytab_file.h"
15
#include "keytab_krb5_ctx.h"
16
#include "wmem_scopes.h"
17
18
#if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)
19
20
#if defined(_WIN32) && !defined(SSIZE_T_DEFINED)
21
 /* prevent redefinition warnings in krb5's win-mac.h */
22
 /* XXX - should we define this in include/ws_posix_compat.h
23
  * where we typedef SSIZE_T to ssize_t? */
24
#define SSIZE_T_DEFINED
25
#endif /* _WIN32 */
26
#include <krb5.h>
27
28
#ifdef HAVE_MIT_KERBEROS
29
#define KRB5_KT_KEY(k)          (&(k)->key)
30
#define KRB5_KEY_TYPE(k)        ((k)->enctype)
31
#define KRB5_KEY_LENGTH(k)      ((k)->length)
32
#define KRB5_KEY_DATA(k)        ((k)->contents)
33
#else
34
#define KRB5_KT_KEY(k)          (&(k)->keyblock)
35
#define KRB5_KEY_TYPE(k)        ((k)->keytype)
36
#define KRB5_KEY_LENGTH(k)      ((k)->keyvalue.length)
37
#define KRB5_KEY_DATA(k)        ((k)->keyvalue.data)
38
#endif
39
40
krb5_context keytab_krb5_ctx;
41
42
static enc_key_t* enc_key_list = NULL;
43
static unsigned kerberos_longterm_ids;
44
static wmem_map_t* keytab_file_longterm_keys;
45
static wmem_map_t* keytab_file_all_keys;
46
static wmem_map_t* keytab_file_session_keys;
47
48
const enc_key_t* keytab_get_enc_key_list(void)
49
{
50
    return enc_key_list;
51
}
52
53
void keytab_set_enc_key_list(enc_key_t* list)
54
{
55
    enc_key_list = list;
56
}
57
58
const wmem_map_t* keytab_get_file_longterm_keys(void)
59
{
60
    return keytab_file_longterm_keys;
61
}
62
63
wmem_map_t* keytab_get_file_all_keys(void)
64
{
65
    return keytab_file_all_keys;
66
}
67
68
wmem_map_t* keytab_get_file_session_keys(void)
69
{
70
    return keytab_file_session_keys;
71
}
72
73
static bool
74
enc_key_list_cb(wmem_allocator_t* allocator _U_, wmem_cb_event_t event _U_, void* user_data _U_)
75
{
76
    enc_key_list = NULL;
77
    kerberos_longterm_ids = 0;
78
    /* keep the callback registered */
79
    return true;
80
}
81
82
static int
83
enc_key_cmp_id(const void* k1, const void* k2)
84
{
85
    const enc_key_t* key1 = (const enc_key_t*)k1;
86
    const enc_key_t* key2 = (const enc_key_t*)k2;
87
88
    if (key1->fd_num < key2->fd_num) {
89
        return -1;
90
    }
91
    if (key1->fd_num > key2->fd_num) {
92
        return 1;
93
    }
94
95
    if (key1->id < key2->id) {
96
        return -1;
97
    }
98
    if (key1->id > key2->id) {
99
        return 1;
100
    }
101
102
    return 0;
103
}
104
105
static gboolean
106
enc_key_content_equal(const void* k1, const void* k2)
107
{
108
    const enc_key_t* key1 = (const enc_key_t*)k1;
109
    const enc_key_t* key2 = (const enc_key_t*)k2;
110
    int cmp;
111
112
    if (key1->keytype != key2->keytype) {
113
        return false;
114
    }
115
116
    if (key1->keylength != key2->keylength) {
117
        return false;
118
    }
119
120
    cmp = memcmp(key1->keyvalue, key2->keyvalue, key1->keylength);
121
    if (cmp != 0) {
122
        return false;
123
    }
124
125
    return true;
126
}
127
128
static unsigned
129
enc_key_content_hash(const void* k)
130
{
131
    const enc_key_t* key = (const enc_key_t*)k;
132
    unsigned ret = 0;
133
134
    ret += wmem_strong_hash((const uint8_t*)&key->keytype,
135
        sizeof(key->keytype));
136
    ret += wmem_strong_hash((const uint8_t*)&key->keylength,
137
        sizeof(key->keylength));
138
    ret += wmem_strong_hash((const uint8_t*)key->keyvalue,
139
        key->keylength);
140
141
    return ret;
142
}
143
144
void
145
keytab_file_key_map_insert(wmem_map_t* key_map, enc_key_t* new_key)
146
{
147
    enc_key_t* existing = NULL;
148
    enc_key_t* cur = NULL;
149
    int cmp;
150
151
    existing = (enc_key_t*)wmem_map_lookup(key_map, new_key);
152
    if (existing == NULL) {
153
        wmem_map_insert(key_map, new_key, new_key);
154
        return;
155
    }
156
157
    if (key_map != keytab_file_all_keys) {
158
        /*
159
         * It should already be linked to the existing key...
160
         */
161
        return;
162
    }
163
164
    if (existing->fd_num == -1 && new_key->fd_num != -1) {
165
        /*
166
         * We can't reference a learnt key
167
         * from a longterm key. As they have
168
         * a shorter lifetime.
169
         *
170
         * So just let the learnt key remember the
171
         * match.
172
         */
173
        new_key->same_list = existing;
174
        new_key->num_same = existing->num_same + 1;
175
        return;
176
    }
177
178
    /*
179
     * If a key with the same content (keytype,keylength,keyvalue)
180
     * already exists, we want the earliest key to be
181
     * in the list.
182
     */
183
    cmp = enc_key_cmp_id(new_key, existing);
184
    if (cmp == 0) {
185
        /*
186
         * It's the same, nothing to do...
187
         */
188
        return;
189
    }
190
    if (cmp < 0) {
191
        /* The new key has should be added to the list. */
192
        new_key->same_list = existing;
193
        new_key->num_same = existing->num_same + 1;
194
        wmem_map_insert(key_map, new_key, new_key);
195
        return;
196
    }
197
198
    /*
199
     * We want to link the new_key to the existing one.
200
     *
201
     * But we want keep the list sorted, so we need to forward
202
     * to the correct spot.
203
     */
204
    for (cur = existing; cur->same_list != NULL; cur = cur->same_list) {
205
        cmp = enc_key_cmp_id(new_key, cur->same_list);
206
        if (cmp == 0) {
207
            /*
208
             * It's the same, nothing to do...
209
             */
210
            return;
211
        }
212
213
        if (cmp < 0) {
214
            /*
215
             * We found the correct spot,
216
             * the new_key should added
217
             * between existing and existing->same_list
218
             */
219
            new_key->same_list = cur->same_list;
220
            new_key->num_same = cur->num_same;
221
            break;
222
        }
223
    }
224
225
    /*
226
     * finally link new_key to existing
227
     * and fix up the numbers
228
     */
229
    cur->same_list = new_key;
230
    for (cur = existing; cur != new_key; cur = cur->same_list) {
231
        cur->num_same += 1;
232
    }
233
234
    return;
235
}
236
237
#endif
238
239
#if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)
240
241
USES_APPLE_DEPRECATED_API
242
243
bool
244
keytab_krb5_ctx_init(void)
245
{
246
    static bool tried;
247
    static bool ok;
248
    krb5_error_code ret;
249
250
    if (tried) {
251
        return ok;
252
    }
253
    tried = true;
254
    ret = krb5_init_context(&keytab_krb5_ctx);
255
    ok = (ret == 0
256
#ifdef HAVE_MIT_KERBEROS
257
          || ret == KRB5_CONFIG_CANTOPEN
258
#endif
259
          );
260
    return ok;
261
}
262
263
void
264
keytab_file_read(const char* filename)
265
{
266
    krb5_keytab keytab;
267
    krb5_error_code ret;
268
    krb5_keytab_entry key;
269
    krb5_kt_cursor cursor;
270
271
    if (filename == NULL || filename[0] == 0) {
272
        return;
273
    }
274
275
    if (!keytab_krb5_ctx_init()) {
276
        return;
277
    }
278
279
    /* should use a file in the wireshark users dir */
280
    ret = krb5_kt_resolve(keytab_krb5_ctx, filename, &keytab);
281
    if (ret) {
282
        ws_critical("KERBEROS ERROR: Badly formatted keytab filename: %s", filename);
283
284
        return;
285
    }
286
287
    ret = krb5_kt_start_seq_get(keytab_krb5_ctx, keytab, &cursor);
288
    if (ret) {
289
        ws_critical("KERBEROS ERROR: Could not open or could not read from keytab file: %s", filename);
290
        return;
291
    }
292
293
    do {
294
        ret = krb5_kt_next_entry(keytab_krb5_ctx, keytab, &key, &cursor);
295
        if (ret == 0) {
296
            enc_key_t* new_key;
297
            wmem_strbuf_t* str_principal = wmem_strbuf_new(wmem_epan_scope(), "keytab principal ");
298
299
            new_key = wmem_new0(wmem_epan_scope(), enc_key_t);
300
            new_key->fd_num = -1;
301
            new_key->id = ++kerberos_longterm_ids;
302
            new_key->id_str = wmem_strdup_printf(wmem_epan_scope(), "keytab.%u", new_key->id);
303
            new_key->next = enc_key_list;
304
305
            /* generate origin string, describing where this key came from */
306
            char* name;
307
            krb5_unparse_name(keytab_krb5_ctx, key.principal, &name);
308
            wmem_strbuf_append(str_principal, name);
309
            new_key->key_origin = (char*)wmem_strbuf_finalize(str_principal);
310
#ifdef HAVE_MIT_KERBEROS
311
            krb5_free_unparsed_name(keytab_krb5_ctx, name);
312
#else
313
            /* Heimdal has the other function but it is deprecated, we could
314
             * test for this function's presence instead. */
315
            krb5_xfree(name);
316
#endif
317
            new_key->keytype = KRB5_KEY_TYPE(KRB5_KT_KEY(&key));
318
            new_key->keylength = MIN(KRB5_KEY_LENGTH(KRB5_KT_KEY(&key)), KRB_MAX_KEY_LENGTH);
319
            memcpy(new_key->keyvalue,
320
                KRB5_KEY_DATA(KRB5_KT_KEY(&key)),
321
                new_key->keylength);
322
323
            enc_key_list = new_key;
324
#ifdef HAVE_MIT_KERBEROS
325
            /* MIT has the other function but it is deprecated, we could
326
             * test for this function's presence instead. */
327
            ret = krb5_free_keytab_entry_contents(keytab_krb5_ctx, &key);
328
#else
329
            ret = krb5_kt_free_entry(keytab_krb5_ctx, &key);
330
#endif
331
            if (ret) {
332
                ws_critical("KERBEROS ERROR: Could not release the entry: %d", ret);
333
                ret = 0; /* try to continue with the next entry */
334
            }
335
            keytab_file_key_map_insert(keytab_file_longterm_keys, new_key);
336
        }
337
    } while (ret == 0);
338
339
    ret = krb5_kt_end_seq_get(keytab_krb5_ctx, keytab, &cursor);
340
    if (ret) {
341
        ws_critical("KERBEROS ERROR: Could not release the keytab cursor: %d", ret);
342
    }
343
    ret = krb5_kt_close(keytab_krb5_ctx, keytab);
344
    if (ret) {
345
        ws_critical("KERBEROS ERROR: Could not close the key table handle: %d", ret);
346
    }
347
}
348
349
USES_APPLE_RST
350
351
#endif
352
353
void keytab_file_data_init(void)
354
16
{
355
#if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)
356
    wmem_register_callback(wmem_epan_scope(), enc_key_list_cb, NULL);
357
358
    keytab_file_longterm_keys = wmem_map_new(wmem_epan_scope(),
359
        enc_key_content_hash,
360
        enc_key_content_equal);
361
    keytab_file_all_keys = wmem_map_new_autoreset(wmem_epan_scope(),
362
        wmem_file_scope(),
363
        enc_key_content_hash,
364
        enc_key_content_equal);
365
    keytab_file_session_keys = wmem_map_new_autoreset(wmem_epan_scope(),
366
        wmem_file_scope(),
367
        enc_key_content_hash,
368
        enc_key_content_equal);
369
370
#endif
371
16
}