~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/lib/krb5/keytab_keyfile.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
 
3
 * (Royal Institute of Technology, Stockholm, Sweden).
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
10
 * 1. Redistributions of source code must retain the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer.
 
12
 *
 
13
 * 2. Redistributions in binary form must reproduce the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer in the
 
15
 *    documentation and/or other materials provided with the distribution.
 
16
 *
 
17
 * 3. Neither the name of the Institute nor the names of its contributors
 
18
 *    may be used to endorse or promote products derived from this software
 
19
 *    without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
31
 * SUCH DAMAGE.
 
32
 */
 
33
 
 
34
#include "krb5_locl.h"
 
35
 
 
36
RCSID("$Id$");
 
37
 
 
38
#ifndef HEIMDAL_SMALLER
 
39
 
 
40
/* afs keyfile operations --------------------------------------- */
 
41
 
 
42
/*
 
43
 * Minimum tools to handle the AFS KeyFile.
 
44
 *
 
45
 * Format of the KeyFile is:
 
46
 * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * numkeys}
 
47
 *
 
48
 * It just adds to the end of the keyfile, deleting isn't implemented.
 
49
 * Use your favorite text/hex editor to delete keys.
 
50
 *
 
51
 */
 
52
 
 
53
#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell"
 
54
#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf"
 
55
 
 
56
struct akf_data {
 
57
    uint32_t num_entries;
 
58
    char *filename;
 
59
    char *cell;
 
60
    char *realm;
 
61
};
 
62
 
 
63
/*
 
64
 * set `d->cell' and `d->realm'
 
65
 */
 
66
 
 
67
static int
 
68
get_cell_and_realm (krb5_context context, struct akf_data *d)
 
69
{
 
70
    FILE *f;
 
71
    char buf[BUFSIZ], *cp;
 
72
    int ret;
 
73
 
 
74
    f = fopen (AFS_SERVERTHISCELL, "r");
 
75
    if (f == NULL) {
 
76
        ret = errno;
 
77
        krb5_set_error_message (context, ret,
 
78
                                N_("Open ThisCell %s: %s", ""),
 
79
                                AFS_SERVERTHISCELL,
 
80
                                strerror(ret));
 
81
        return ret;
 
82
    }
 
83
    if (fgets (buf, sizeof(buf), f) == NULL) {
 
84
        fclose (f);
 
85
        krb5_set_error_message (context, EINVAL,
 
86
                                N_("No cell in ThisCell file %s", ""),
 
87
                                AFS_SERVERTHISCELL);
 
88
        return EINVAL;
 
89
    }
 
90
    buf[strcspn(buf, "\n")] = '\0';
 
91
    fclose(f);
 
92
 
 
93
    d->cell = strdup (buf);
 
94
    if (d->cell == NULL) {
 
95
        krb5_set_error_message(context, ENOMEM,
 
96
                               N_("malloc: out of memory", ""));
 
97
        return ENOMEM;
 
98
    }
 
99
 
 
100
    f = fopen (AFS_SERVERMAGICKRBCONF, "r");
 
101
    if (f != NULL) {
 
102
        if (fgets (buf, sizeof(buf), f) == NULL) {
 
103
            free (d->cell);
 
104
            d->cell = NULL;
 
105
            fclose (f);
 
106
            krb5_set_error_message (context, EINVAL,
 
107
                                    N_("No realm in ThisCell file %s", ""),
 
108
                                    AFS_SERVERMAGICKRBCONF);
 
109
            return EINVAL;
 
110
        }
 
111
        buf[strcspn(buf, "\n")] = '\0';
 
112
        fclose(f);
 
113
    }
 
114
    /* uppercase */
 
115
    for (cp = buf; *cp != '\0'; cp++)
 
116
        *cp = toupper((unsigned char)*cp);
 
117
 
 
118
    d->realm = strdup (buf);
 
119
    if (d->realm == NULL) {
 
120
        free (d->cell);
 
121
        d->cell = NULL;
 
122
        krb5_set_error_message(context, ENOMEM,
 
123
                               N_("malloc: out of memory", ""));
 
124
        return ENOMEM;
 
125
    }
 
126
    return 0;
 
127
}
 
128
 
 
129
/*
 
130
 * init and get filename
 
131
 */
 
132
 
 
133
static krb5_error_code
 
134
akf_resolve(krb5_context context, const char *name, krb5_keytab id)
 
135
{
 
136
    int ret;
 
137
    struct akf_data *d = malloc(sizeof (struct akf_data));
 
138
 
 
139
    if (d == NULL) {
 
140
        krb5_set_error_message(context, ENOMEM,
 
141
                               N_("malloc: out of memory", ""));
 
142
        return ENOMEM;
 
143
    }
 
144
 
 
145
    d->num_entries = 0;
 
146
    ret = get_cell_and_realm (context, d);
 
147
    if (ret) {
 
148
        free (d);
 
149
        return ret;
 
150
    }
 
151
    d->filename = strdup (name);
 
152
    if (d->filename == NULL) {
 
153
        free (d->cell);
 
154
        free (d->realm);
 
155
        free (d);
 
156
        krb5_set_error_message(context, ENOMEM,
 
157
                               N_("malloc: out of memory", ""));
 
158
        return ENOMEM;
 
159
    }
 
160
    id->data = d;
 
161
 
 
162
    return 0;
 
163
}
 
164
 
 
165
/*
 
166
 * cleanup
 
167
 */
 
168
 
 
169
static krb5_error_code
 
170
akf_close(krb5_context context, krb5_keytab id)
 
171
{
 
172
    struct akf_data *d = id->data;
 
173
 
 
174
    free (d->filename);
 
175
    free (d->cell);
 
176
    free (d);
 
177
    return 0;
 
178
}
 
179
 
 
180
/*
 
181
 * Return filename
 
182
 */
 
183
 
 
184
static krb5_error_code
 
185
akf_get_name(krb5_context context,
 
186
             krb5_keytab id,
 
187
             char *name,
 
188
             size_t name_sz)
 
189
{
 
190
    struct akf_data *d = id->data;
 
191
 
 
192
    strlcpy (name, d->filename, name_sz);
 
193
    return 0;
 
194
}
 
195
 
 
196
/*
 
197
 * Init
 
198
 */
 
199
 
 
200
static krb5_error_code
 
201
akf_start_seq_get(krb5_context context,
 
202
                  krb5_keytab id,
 
203
                  krb5_kt_cursor *c)
 
204
{
 
205
    int32_t ret;
 
206
    struct akf_data *d = id->data;
 
207
 
 
208
    c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600);
 
209
    if (c->fd < 0) {
 
210
        ret = errno;
 
211
        krb5_set_error_message(context, ret,
 
212
                               N_("keytab afs keyfile open %s failed: %s", ""),
 
213
                               d->filename, strerror(ret));
 
214
        return ret;
 
215
    }
 
216
 
 
217
    c->sp = krb5_storage_from_fd(c->fd);
 
218
    ret = krb5_ret_uint32(c->sp, &d->num_entries);
 
219
    if(ret) {
 
220
        krb5_storage_free(c->sp);
 
221
        close(c->fd);
 
222
        krb5_clear_error_message (context);
 
223
        if(ret == KRB5_KT_END)
 
224
            return KRB5_KT_NOTFOUND;
 
225
        return ret;
 
226
    }
 
227
 
 
228
    return 0;
 
229
}
 
230
 
 
231
static krb5_error_code
 
232
akf_next_entry(krb5_context context,
 
233
               krb5_keytab id,
 
234
               krb5_keytab_entry *entry,
 
235
               krb5_kt_cursor *cursor)
 
236
{
 
237
    struct akf_data *d = id->data;
 
238
    int32_t kvno;
 
239
    off_t pos;
 
240
    int ret;
 
241
 
 
242
    pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR);
 
243
 
 
244
    if ((pos - 4) / (4 + 8) >= d->num_entries)
 
245
        return KRB5_KT_END;
 
246
 
 
247
    ret = krb5_make_principal (context, &entry->principal,
 
248
                               d->realm, "afs", d->cell, NULL);
 
249
    if (ret)
 
250
        goto out;
 
251
 
 
252
    ret = krb5_ret_int32(cursor->sp, &kvno);
 
253
    if (ret) {
 
254
        krb5_free_principal (context, entry->principal);
 
255
        goto out;
 
256
    }
 
257
 
 
258
    entry->vno = kvno;
 
259
 
 
260
    entry->keyblock.keytype         = ETYPE_DES_CBC_MD5;
 
261
    entry->keyblock.keyvalue.length = 8;
 
262
    entry->keyblock.keyvalue.data   = malloc (8);
 
263
    if (entry->keyblock.keyvalue.data == NULL) {
 
264
        krb5_free_principal (context, entry->principal);
 
265
        krb5_set_error_message(context, ENOMEM,
 
266
                               N_("malloc: out of memory", ""));
 
267
        ret = ENOMEM;
 
268
        goto out;
 
269
    }
 
270
 
 
271
    ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8);
 
272
    if(ret != 8)
 
273
        ret = (ret < 0) ? errno : KRB5_KT_END;
 
274
    else
 
275
        ret = 0;
 
276
 
 
277
    entry->timestamp = time(NULL);
 
278
 
 
279
 out:
 
280
    krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET);
 
281
    return ret;
 
282
}
 
283
 
 
284
static krb5_error_code
 
285
akf_end_seq_get(krb5_context context,
 
286
                krb5_keytab id,
 
287
                krb5_kt_cursor *cursor)
 
288
{
 
289
    krb5_storage_free(cursor->sp);
 
290
    close(cursor->fd);
 
291
    return 0;
 
292
}
 
293
 
 
294
static krb5_error_code
 
295
akf_add_entry(krb5_context context,
 
296
              krb5_keytab id,
 
297
              krb5_keytab_entry *entry)
 
298
{
 
299
    struct akf_data *d = id->data;
 
300
    int fd, created = 0;
 
301
    krb5_error_code ret;
 
302
    int32_t len;
 
303
    krb5_storage *sp;
 
304
 
 
305
 
 
306
    if (entry->keyblock.keyvalue.length != 8)
 
307
        return 0;
 
308
    switch(entry->keyblock.keytype) {
 
309
    case ETYPE_DES_CBC_CRC:
 
310
    case ETYPE_DES_CBC_MD4:
 
311
    case ETYPE_DES_CBC_MD5:
 
312
        break;
 
313
    default:
 
314
        return 0;
 
315
    }
 
316
 
 
317
    fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
 
318
    if (fd < 0) {
 
319
        fd = open (d->filename,
 
320
                   O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
 
321
        if (fd < 0) {
 
322
            ret = errno;
 
323
            krb5_set_error_message(context, ret,
 
324
                                   N_("open keyfile(%s): %s", ""),
 
325
                                   d->filename,
 
326
                                   strerror(ret));
 
327
            return ret;
 
328
        }
 
329
        created = 1;
 
330
    }
 
331
 
 
332
    sp = krb5_storage_from_fd(fd);
 
333
    if(sp == NULL) {
 
334
        close(fd);
 
335
        krb5_set_error_message(context, ENOMEM,
 
336
                               N_("malloc: out of memory", ""));
 
337
        return ENOMEM;
 
338
    }
 
339
    if (created)
 
340
        len = 0;
 
341
    else {
 
342
        if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
 
343
            ret = errno;
 
344
            krb5_storage_free(sp);
 
345
            close(fd);
 
346
            krb5_set_error_message(context, ret,
 
347
                                   N_("seeking in keyfile: %s", ""),
 
348
                                   strerror(ret));
 
349
            return ret;
 
350
        }
 
351
        
 
352
        ret = krb5_ret_int32(sp, &len);
 
353
        if(ret) {
 
354
            krb5_storage_free(sp);
 
355
            close(fd);
 
356
            return ret;
 
357
        }
 
358
    }
 
359
 
 
360
    /*
 
361
     * Make sure we don't add the entry twice, assumes the DES
 
362
     * encryption types are all the same key.
 
363
     */
 
364
    if (len > 0) {
 
365
        int32_t kvno;
 
366
        int i;
 
367
 
 
368
        for (i = 0; i < len; i++) {
 
369
            ret = krb5_ret_int32(sp, &kvno);
 
370
            if (ret) {
 
371
                krb5_set_error_message (context, ret,
 
372
                                        N_("Failed getting kvno from keyfile", ""));
 
373
                goto out;
 
374
            }
 
375
            if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) {
 
376
                ret = errno;
 
377
                krb5_set_error_message (context, ret,
 
378
                                        N_("Failed seeing in keyfile: %s", ""),
 
379
                                        strerror(ret));
 
380
                goto out;
 
381
            }
 
382
            if (kvno == entry->vno) {
 
383
                ret = 0;
 
384
                goto out;
 
385
            }
 
386
        }
 
387
    }
 
388
 
 
389
    len++;
 
390
        
 
391
    if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) {
 
392
        ret = errno;
 
393
        krb5_set_error_message (context, ret,
 
394
                                N_("Failed seeing in keyfile: %s", ""),
 
395
                                strerror(ret));
 
396
        goto out;
 
397
    }
 
398
        
 
399
    ret = krb5_store_int32(sp, len);
 
400
    if(ret) {
 
401
        ret = errno;
 
402
        krb5_set_error_message (context, ret,
 
403
                                N_("keytab keyfile failed new length", ""));
 
404
        return ret;
 
405
    }
 
406
 
 
407
    if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) {
 
408
        ret = errno;
 
409
        krb5_set_error_message (context, ret,
 
410
                                N_("seek to end: %s", ""), strerror(ret));
 
411
        goto out;
 
412
    }
 
413
        
 
414
    ret = krb5_store_int32(sp, entry->vno);
 
415
    if(ret) {
 
416
        krb5_set_error_message(context, ret,
 
417
                               N_("keytab keyfile failed store kvno", ""));
 
418
        goto out;
 
419
    }
 
420
    ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data,
 
421
                             entry->keyblock.keyvalue.length);
 
422
    if(ret != entry->keyblock.keyvalue.length) {
 
423
        if (ret < 0)
 
424
            ret = errno;
 
425
        else
 
426
            ret = ENOTTY;
 
427
        krb5_set_error_message(context, ret,
 
428
                               N_("keytab keyfile failed to add key", ""));
 
429
        goto out;
 
430
    }
 
431
    ret = 0;
 
432
out:
 
433
    krb5_storage_free(sp);
 
434
    close (fd);
 
435
    return ret;
 
436
}
 
437
 
 
438
const krb5_kt_ops krb5_akf_ops = {
 
439
    "AFSKEYFILE",
 
440
    akf_resolve,
 
441
    akf_get_name,
 
442
    akf_close,
 
443
    NULL, /* get */
 
444
    akf_start_seq_get,
 
445
    akf_next_entry,
 
446
    akf_end_seq_get,
 
447
    akf_add_entry,
 
448
    NULL /* remove */
 
449
};
 
450
 
 
451
#endif /* HEIMDAL_SMALLER */