2
Unix SMB/CIFS implementation.
3
kerberos keytab utility library
4
Copyright (C) Andrew Tridgell 2001
5
Copyright (C) Remus Koos 2001
6
Copyright (C) Luke Howard 2003
7
Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
8
Copyright (C) Guenther Deschner 2003
9
Copyright (C) Rakesh Patel 2004
10
Copyright (C) Dan Perry 2004
11
Copyright (C) Jeremy Allison 2004
12
Copyright (C) Gerald Carter 2006
14
This program is free software; you can redistribute it and/or modify
15
it under the terms of the GNU General Public License as published by
16
the Free Software Foundation; either version 2 of the License, or
17
(at your option) any later version.
19
This program is distributed in the hope that it will be useful,
20
but WITHOUT ANY WARRANTY; without even the implied warranty of
21
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
GNU General Public License for more details.
24
You should have received a copy of the GNU General Public License
25
along with this program; if not, write to the Free Software
26
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33
/* This MAX_NAME_LEN is a constant defined in krb5.h */
34
#ifndef MAX_KEYTAB_NAME_LEN
35
#define MAX_KEYTAB_NAME_LEN 1100
39
/**********************************************************************
40
**********************************************************************/
42
static int smb_krb5_kt_add_entry( krb5_context context, krb5_keytab keytab,
43
krb5_kvno kvno, const char *princ_s,
44
krb5_enctype *enctypes, krb5_data password )
46
krb5_error_code ret = 0;
47
krb5_kt_cursor cursor;
48
krb5_keytab_entry kt_entry;
49
krb5_principal princ = NULL;
53
ZERO_STRUCT(kt_entry);
56
ret = smb_krb5_parse_name(context, princ_s, &princ);
58
DEBUG(1,("ads_keytab_add_entry: smb_krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
62
/* Seek and delete old keytab entries */
63
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
64
if (ret != KRB5_KT_END && ret != ENOENT ) {
65
DEBUG(3,("ads_keytab_add_entry: Will try to delete old keytab entries\n"));
66
while(!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
67
BOOL compare_name_ok = False;
69
ret = smb_krb5_unparse_name(context, kt_entry.principal, &ktprinc);
71
DEBUG(1,("ads_keytab_add_entry: smb_krb5_unparse_name failed (%s)\n",
76
/*---------------------------------------------------------------------------
77
* Save the entries with kvno - 1. This is what microsoft does
78
* to allow people with existing sessions that have kvno - 1 to still
79
* work. Otherwise, when the password for the machine changes, all
80
* kerberizied sessions will 'break' until either the client reboots or
81
* the client's session key expires and they get a new session ticket
85
#ifdef HAVE_KRB5_KT_COMPARE
86
compare_name_ok = (krb5_kt_compare(context, &kt_entry, princ, 0, 0) == True);
88
compare_name_ok = (strcmp(ktprinc, princ_s) == 0);
91
if (!compare_name_ok) {
92
DEBUG(10,("ads_keytab_add_entry: ignoring keytab entry principal %s, kvno = %d\n",
93
ktprinc, kt_entry.vno));
98
if (compare_name_ok) {
99
if (kt_entry.vno == kvno - 1) {
100
DEBUG(5,("ads_keytab_add_entry: Saving previous (kvno %d) entry for principal: %s.\n",
104
DEBUG(5,("ads_keytab_add_entry: Found old entry for principal: %s (kvno %d) - trying to remove it.\n",
105
princ_s, kt_entry.vno));
106
ret = krb5_kt_end_seq_get(context, keytab, &cursor);
109
DEBUG(1,("ads_keytab_add_entry: krb5_kt_end_seq_get() failed (%s)\n",
110
error_message(ret)));
113
ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
115
DEBUG(1,("ads_keytab_add_entry: krb5_kt_remove_entry failed (%s)\n",
116
error_message(ret)));
120
DEBUG(5,("ads_keytab_add_entry: removed old entry for principal: %s (kvno %d).\n",
121
princ_s, kt_entry.vno));
123
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
125
DEBUG(1,("ads_keytab_add_entry: krb5_kt_start_seq failed (%s)\n",
126
error_message(ret)));
129
ret = smb_krb5_kt_free_entry(context, &kt_entry);
130
ZERO_STRUCT(kt_entry);
132
DEBUG(1,("ads_keytab_add_entry: krb5_kt_remove_entry failed (%s)\n",
133
error_message(ret)));
140
/* Not a match, just free this entry and continue. */
141
ret = smb_krb5_kt_free_entry(context, &kt_entry);
142
ZERO_STRUCT(kt_entry);
144
DEBUG(1,("ads_keytab_add_entry: smb_krb5_kt_free_entry failed (%s)\n", error_message(ret)));
149
ret = krb5_kt_end_seq_get(context, keytab, &cursor);
152
DEBUG(1,("ads_keytab_add_entry: krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
157
/* Ensure we don't double free. */
158
ZERO_STRUCT(kt_entry);
161
/* If we get here, we have deleted all the old entries with kvno's not equal to the current kvno-1. */
163
/* Now add keytab entries for all encryption types */
164
for (i = 0; enctypes[i]; i++) {
167
#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
168
#error krb5_keytab_entry has no key or keyblock member
170
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY /* MIT */
171
keyp = &kt_entry.key;
173
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */
174
keyp = &kt_entry.keyblock;
176
if (create_kerberos_key_from_string(context, princ, &password, keyp, enctypes[i])) {
180
kt_entry.principal = princ;
183
DEBUG(3,("ads_keytab_add_entry: adding keytab entry for (%s) with encryption type (%d) and version (%d)\n",
184
princ_s, enctypes[i], kt_entry.vno));
185
ret = krb5_kt_add_entry(context, keytab, &kt_entry);
186
krb5_free_keyblock_contents(context, keyp);
187
ZERO_STRUCT(kt_entry);
189
DEBUG(1,("ads_keytab_add_entry: adding entry to keytab failed (%s)\n", error_message(ret)));
197
krb5_keytab_entry zero_kt_entry;
198
ZERO_STRUCT(zero_kt_entry);
199
if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
200
smb_krb5_kt_free_entry(context, &kt_entry);
204
krb5_free_principal(context, princ);
208
krb5_kt_cursor zero_csr;
209
ZERO_STRUCT(zero_csr);
210
if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
211
krb5_kt_end_seq_get(context, keytab, &cursor);
219
/**********************************************************************
220
Adds a single service principal, i.e. 'host' to the system keytab
221
***********************************************************************/
223
int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
225
krb5_error_code ret = 0;
226
krb5_context context = NULL;
227
krb5_keytab keytab = NULL;
230
krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 0, 0 };
231
char *princ_s = NULL, *short_princ_s = NULL;
232
char *password_s = NULL;
234
char keytab_name[MAX_KEYTAB_NAME_LEN];
235
TALLOC_CTX *ctx = NULL;
238
#if defined(ENCTYPE_ARCFOUR_HMAC)
239
enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
242
initialize_krb5_error_table();
243
ret = krb5_init_context(&context);
245
DEBUG(1,("ads_keytab_add_entry: could not krb5_init_context: %s\n",error_message(ret)));
249
#ifdef HAVE_WRFILE_KEYTAB /* MIT */
250
keytab_name[0] = 'W';
251
keytab_name[1] = 'R';
252
ret = krb5_kt_default_name(context, (char *) &keytab_name[2], MAX_KEYTAB_NAME_LEN - 4);
254
ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
257
DEBUG(1,("ads_keytab_add_entry: krb5_kt_default_name failed (%s)\n", error_message(ret)));
260
DEBUG(2,("ads_keytab_add_entry: Using default system keytab: %s\n", (char *) &keytab_name));
261
ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
263
DEBUG(1,("ads_keytab_add_entry: krb5_kt_resolve failed (%s)\n", error_message(ret)));
267
/* retrieve the password */
268
if (!secrets_init()) {
269
DEBUG(1,("ads_keytab_add_entry: secrets_init failed\n"));
273
password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
275
DEBUG(1,("ads_keytab_add_entry: failed to fetch machine password\n"));
279
password.data = password_s;
280
password.length = strlen(password_s);
282
/* we need the dNSHostName value here */
284
if ( (ctx = talloc_init("ads_keytab_add_entry")) == NULL ) {
285
DEBUG(0,("ads_keytab_add_entry: talloc() failed!\n"));
290
if ( (my_fqdn = ads_get_dnshostname( ads, ctx, global_myname())) == NULL ) {
291
DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's dns name in AD!\n"));
296
if ( (machine_name = ads_get_samaccountname( ads, ctx, global_myname())) == NULL ) {
297
DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's short name in AD!\n"));
301
/*strip the trailing '$' */
302
machine_name[strlen(machine_name)-1] = '\0';
304
/* Construct our principal */
306
if (strchr_m(srvPrinc, '@')) {
307
/* It's a fully-named principal. */
308
asprintf(&princ_s, "%s", srvPrinc);
309
} else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
310
/* It's the machine account, as used by smbclient clients. */
311
asprintf(&princ_s, "%s@%s", srvPrinc, lp_realm());
313
/* It's a normal service principal. Add the SPN now so that we
314
* can obtain credentials for it and double-check the salt value
315
* used to generate the service's keys. */
317
asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm());
318
asprintf(&short_princ_s, "%s/%s@%s", srvPrinc, machine_name, lp_realm());
320
/* According to http://support.microsoft.com/kb/326985/en-us,
321
certain principal names are automatically mapped to the host/...
322
principal in the AD account. So only create these in the
323
keytab, not in AD. --jerry */
325
if ( !strequal( srvPrinc, "cifs" ) && !strequal(srvPrinc, "host" ) ) {
326
DEBUG(3,("ads_keytab_add_entry: Attempting to add/update '%s'\n", princ_s));
328
if (!ADS_ERR_OK(ads_add_service_principal_name(ads, global_myname(), my_fqdn, srvPrinc))) {
329
DEBUG(1,("ads_keytab_add_entry: ads_add_service_principal_name failed.\n"));
335
kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
336
if (kvno == -1) { /* -1 indicates failure, everything else is OK */
337
DEBUG(1,("ads_keytab_add_entry: ads_get_kvno failed to determine the system's kvno.\n"));
342
/* add the fqdn principal to the keytab */
344
ret = smb_krb5_kt_add_entry( context, keytab, kvno, princ_s, enctypes, password );
346
DEBUG(1,("ads_keytab_add_entry: Failed to add entry to keytab file\n"));
350
/* add the short principal name if we have one */
352
if ( short_princ_s ) {
353
ret = smb_krb5_kt_add_entry( context, keytab, kvno, short_princ_s, enctypes, password );
355
DEBUG(1,("ads_keytab_add_entry: Failed to add short entry to keytab file\n"));
361
SAFE_FREE( princ_s );
362
SAFE_FREE( short_princ_s );
366
krb5_kt_close(context, keytab);
369
krb5_free_context(context);
374
/**********************************************************************
375
Flushes all entries from the system keytab.
376
***********************************************************************/
378
int ads_keytab_flush(ADS_STRUCT *ads)
380
krb5_error_code ret = 0;
381
krb5_context context = NULL;
382
krb5_keytab keytab = NULL;
383
krb5_kt_cursor cursor;
384
krb5_keytab_entry kt_entry;
386
char keytab_name[MAX_KEYTAB_NAME_LEN];
388
ZERO_STRUCT(kt_entry);
391
initialize_krb5_error_table();
392
ret = krb5_init_context(&context);
394
DEBUG(1,("ads_keytab_flush: could not krb5_init_context: %s\n",error_message(ret)));
397
#ifdef HAVE_WRFILE_KEYTAB
398
keytab_name[0] = 'W';
399
keytab_name[1] = 'R';
400
ret = krb5_kt_default_name(context, (char *) &keytab_name[2], MAX_KEYTAB_NAME_LEN - 4);
402
ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
405
DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
408
DEBUG(3,("ads_keytab_flush: Using default keytab: %s\n", (char *) &keytab_name));
409
ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
411
DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
414
ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
416
DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
420
kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
421
if (kvno == -1) { /* -1 indicates a failure */
422
DEBUG(1,("ads_keytab_flush: Error determining the system's kvno.\n"));
426
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
427
if (ret != KRB5_KT_END && ret != ENOENT) {
428
while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
429
ret = krb5_kt_end_seq_get(context, keytab, &cursor);
432
DEBUG(1,("ads_keytab_flush: krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
435
ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
437
DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
440
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
442
DEBUG(1,("ads_keytab_flush: krb5_kt_start_seq failed (%s)\n",error_message(ret)));
445
ret = smb_krb5_kt_free_entry(context, &kt_entry);
446
ZERO_STRUCT(kt_entry);
448
DEBUG(1,("ads_keytab_flush: krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
454
/* Ensure we don't double free. */
455
ZERO_STRUCT(kt_entry);
458
if (!ADS_ERR_OK(ads_clear_service_principal_names(ads, global_myname()))) {
459
DEBUG(1,("ads_keytab_flush: Error while clearing service principal listings in LDAP.\n"));
466
krb5_keytab_entry zero_kt_entry;
467
ZERO_STRUCT(zero_kt_entry);
468
if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
469
smb_krb5_kt_free_entry(context, &kt_entry);
473
krb5_kt_cursor zero_csr;
474
ZERO_STRUCT(zero_csr);
475
if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
476
krb5_kt_end_seq_get(context, keytab, &cursor);
480
krb5_kt_close(context, keytab);
483
krb5_free_context(context);
488
/**********************************************************************
489
Adds all the required service principals to the system keytab.
490
***********************************************************************/
492
int ads_keytab_create_default(ADS_STRUCT *ads)
494
krb5_error_code ret = 0;
495
krb5_context context = NULL;
496
krb5_keytab keytab = NULL;
497
krb5_kt_cursor cursor;
498
krb5_keytab_entry kt_entry;
501
char *sam_account_name, *upn;
502
char **oldEntries = NULL, *princ_s[26];
503
TALLOC_CTX *ctx = NULL;
504
fstring machine_name;
506
memset(princ_s, '\0', sizeof(princ_s));
508
fstrcpy( machine_name, global_myname() );
510
/* these are the main ones we need */
512
if ( (ret = ads_keytab_add_entry(ads, "host") ) != 0 ) {
513
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'host'.\n"));
518
#if 0 /* don't create the CIFS/... keytab entries since no one except smbd
519
really needs them and we will fall back to verifying against secrets.tdb */
521
if ( (ret = ads_keytab_add_entry(ads, "cifs")) != 0 ) {
522
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'cifs'.\n"));
527
if ( (ctx = talloc_init("ads_keytab_create_default")) == NULL ) {
528
DEBUG(0,("ads_keytab_create_default: talloc() failed!\n"));
532
/* now add the userPrincipalName and sAMAccountName entries */
534
if ( (sam_account_name = ads_get_samaccountname( ads, ctx, machine_name)) == NULL ) {
535
DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's name in AD!\n"));
540
if ( (ret = ads_keytab_add_entry(ads, sam_account_name )) != 0 ) {
541
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding sAMAccountName (%s)\n",
546
/* remember that not every machine account will have a upn */
548
upn = ads_get_upn( ads, ctx, machine_name);
550
if ( (ret = ads_keytab_add_entry(ads, upn)) != 0 ) {
551
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding UPN (%s)\n",
560
/* Now loop through the keytab and update any other existing entries... */
562
kvno = (krb5_kvno) ads_get_kvno(ads, machine_name);
564
DEBUG(1,("ads_keytab_create_default: ads_get_kvno failed to determine the system's kvno.\n"));
568
DEBUG(3,("ads_keytab_create_default: Searching for keytab entries to "
569
"preserve and update.\n"));
571
ZERO_STRUCT(kt_entry);
574
initialize_krb5_error_table();
575
ret = krb5_init_context(&context);
577
DEBUG(1,("ads_keytab_create_default: could not krb5_init_context: %s\n",error_message(ret)));
580
ret = krb5_kt_default(context, &keytab);
582
DEBUG(1,("ads_keytab_create_default: krb5_kt_default failed (%s)\n",error_message(ret)));
586
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
587
if (ret != KRB5_KT_END && ret != ENOENT ) {
588
while ((ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) == 0) {
589
smb_krb5_kt_free_entry(context, &kt_entry);
590
ZERO_STRUCT(kt_entry);
594
krb5_kt_end_seq_get(context, keytab, &cursor);
598
* Hmmm. There is no "rewind" function for the keytab. This means we have a race condition
599
* where someone else could add entries after we've counted them. Re-open asap to minimise
603
DEBUG(3, ("ads_keytab_create_default: Found %d entries in the keytab.\n", found));
607
oldEntries = SMB_MALLOC_ARRAY(char *, found );
609
DEBUG(1,("ads_keytab_create_default: Failed to allocate space to store the old keytab entries (malloc failed?).\n"));
613
memset(oldEntries, '\0', found * sizeof(char *));
615
ret = krb5_kt_start_seq_get(context, keytab, &cursor);
616
if (ret != KRB5_KT_END && ret != ENOENT ) {
617
while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
618
if (kt_entry.vno != kvno) {
619
char *ktprinc = NULL;
622
/* This returns a malloc'ed string in ktprinc. */
623
ret = smb_krb5_unparse_name(context, kt_entry.principal, &ktprinc);
625
DEBUG(1,("smb_krb5_unparse_name failed (%s)\n", error_message(ret)));
629
* From looking at the krb5 source they don't seem to take locale
630
* or mb strings into account. Maybe this is because they assume utf8 ?
631
* In this case we may need to convert from utf8 to mb charset here ? JRA.
633
p = strchr_m(ktprinc, '@');
638
p = strchr_m(ktprinc, '/');
642
for (i = 0; i < found; i++) {
643
if (!oldEntries[i]) {
644
oldEntries[i] = ktprinc;
647
if (!strcmp(oldEntries[i], ktprinc)) {
656
smb_krb5_kt_free_entry(context, &kt_entry);
657
ZERO_STRUCT(kt_entry);
660
for (i = 0; oldEntries[i]; i++) {
661
ret |= ads_keytab_add_entry(ads, oldEntries[i]);
662
SAFE_FREE(oldEntries[i]);
664
krb5_kt_end_seq_get(context, keytab, &cursor);
670
SAFE_FREE(oldEntries);
673
krb5_keytab_entry zero_kt_entry;
674
ZERO_STRUCT(zero_kt_entry);
675
if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
676
smb_krb5_kt_free_entry(context, &kt_entry);
680
krb5_kt_cursor zero_csr;
681
ZERO_STRUCT(zero_csr);
682
if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
683
krb5_kt_end_seq_get(context, keytab, &cursor);
687
krb5_kt_close(context, keytab);
690
krb5_free_context(context);
694
#endif /* HAVE_KRB5 */