2 * GSSAPI Security Extensions
4 * Copyright (C) Simo Sorce 2010.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 #include "librpc/gen_ndr/secrets.h"
25 #include "lib/param/loadparm.h"
26 #include "libads/kerberos_proto.h"
27 #include "lib/util/string_wrappers.h"
31 static krb5_error_code
flush_keytab(krb5_context krbctx
, krb5_keytab keytab
)
34 krb5_kt_cursor kt_cursor
;
35 krb5_keytab_entry kt_entry
;
37 ZERO_STRUCT(kt_entry
);
39 ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &kt_cursor
);
44 ret
= krb5_kt_next_entry(krbctx
, keytab
, &kt_entry
, &kt_cursor
);
47 /* we need to close and reopen enumeration because we modify
49 ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &kt_cursor
);
51 DEBUG(1, (__location__
": krb5_kt_end_seq_get() "
52 "failed (%s)\n", error_message(ret
)));
56 /* remove the entry */
57 ret
= krb5_kt_remove_entry(krbctx
, keytab
, &kt_entry
);
59 DEBUG(1, (__location__
": krb5_kt_remove_entry() "
60 "failed (%s)\n", error_message(ret
)));
63 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
64 ZERO_STRUCT(kt_entry
);
67 ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &kt_cursor
);
69 DEBUG(1, (__location__
": krb5_kt_start_seq() failed "
70 "(%s)\n", error_message(ret
)));
74 ret
= krb5_kt_next_entry(krbctx
, keytab
,
75 &kt_entry
, &kt_cursor
);
78 if (ret
!= KRB5_KT_END
&& ret
!= ENOENT
) {
79 DEBUG(1, (__location__
": flushing keytab we got [%s]!\n",
83 ret
= krb5_kt_end_seq_get(krbctx
, keytab
, &kt_cursor
);
85 DEBUG(1, (__location__
": krb5_kt_end_seq_get() "
86 "failed (%s)\n", error_message(ret
)));
95 static krb5_error_code
fill_keytab_from_password(krb5_context krbctx
,
99 struct secrets_domain_info1_password
*pw
)
102 krb5_enctype
*enctypes
;
105 ret
= smb_krb5_get_allowed_etypes(krbctx
, &enctypes
);
107 DEBUG(1, (__location__
108 ": Can't determine permitted enctypes!\n"));
112 for (i
= 0; i
< pw
->num_keys
; i
++) {
113 krb5_keytab_entry kt_entry
;
114 krb5_keyblock
*key
= NULL
;
116 bool found_etype
= false;
118 for (ei
=0; enctypes
[ei
] != 0; ei
++) {
119 if ((uint32_t)enctypes
[ei
] != pw
->keys
[i
].keytype
) {
131 ZERO_STRUCT(kt_entry
);
132 kt_entry
.principal
= princ
;
135 key
= KRB5_KT_KEY(&kt_entry
);
136 KRB5_KEY_TYPE(key
) = pw
->keys
[i
].keytype
;
137 KRB5_KEY_DATA(key
) = pw
->keys
[i
].value
.data
;
138 KRB5_KEY_LENGTH(key
) = pw
->keys
[i
].value
.length
;
140 ret
= krb5_kt_add_entry(krbctx
, keytab
, &kt_entry
);
142 DEBUG(1, (__location__
": Failed to add entry to "
143 "keytab for enctype %d (error: %s)\n",
144 (unsigned)pw
->keys
[i
].keytype
,
145 error_message(ret
)));
153 krb5_free_enctypes(krbctx
, enctypes
);
157 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
158 #define CLEARTEXT_PRIV_ENCTYPE -99
160 static krb5_error_code
fill_mem_keytab_from_secrets(krb5_context krbctx
,
163 TALLOC_CTX
*frame
= talloc_stackframe();
164 krb5_error_code ret
, ret2
;
165 const char *domain
= lp_workgroup();
166 struct secrets_domain_info1
*info
= NULL
;
167 const char *realm
= NULL
;
168 const DATA_BLOB
*ct
= NULL
;
169 krb5_kt_cursor kt_cursor
;
170 krb5_keytab_entry kt_entry
;
171 krb5_principal princ
= NULL
;
172 krb5_kvno kvno
= 0; /* FIXME: fetch current vno from KDC ? */
175 if (!secrets_init()) {
176 DEBUG(1, (__location__
": secrets_init failed\n"));
178 return KRB5_CONFIG_CANTOPEN
;
181 status
= secrets_fetch_or_upgrade_domain_info(domain
,
184 if (!NT_STATUS_IS_OK(status
)) {
185 DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
186 domain
, nt_errstr(status
));
188 return KRB5_LIBOS_CANTREADPWD
;
190 ct
= &info
->password
->cleartext_blob
;
192 if (info
->domain_info
.dns_domain
.string
!= NULL
) {
193 realm
= strupper_talloc(frame
,
194 info
->domain_info
.dns_domain
.string
);
201 ZERO_STRUCT(kt_entry
);
202 ZERO_STRUCT(kt_cursor
);
204 /* check if the keytab already has any entry */
205 ret
= krb5_kt_start_seq_get(krbctx
, *keytab
, &kt_cursor
);
210 /* check if we have our special enctype used to hold
211 * the clear text password. If so, check it out so that
212 * we can verify if the keytab needs to be upgraded */
213 while ((ret
= krb5_kt_next_entry(krbctx
, *keytab
,
214 &kt_entry
, &kt_cursor
)) == 0) {
215 if (smb_krb5_kt_get_enctype_from_entry(&kt_entry
) ==
216 CLEARTEXT_PRIV_ENCTYPE
) {
219 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
220 ZERO_STRUCT(kt_entry
);
223 ret2
= krb5_kt_end_seq_get(krbctx
, *keytab
, &kt_cursor
);
226 DEBUG(1, (__location__
": krb5_kt_end_seq_get() "
227 "failed (%s)\n", error_message(ret
)));
231 if (ret
!= 0 && ret
!= KRB5_KT_END
&& ret
!= ENOENT
) {
232 /* Error parsing keytab */
233 DEBUG(1, (__location__
": Failed to parse memory "
239 /* found private entry,
240 * check if keytab is up to date */
242 if ((ct
->length
== KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry
))) &&
243 (mem_equal_const_time(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry
)),
244 ct
->data
, ct
->length
))) {
245 /* keytab is already up to date, return */
246 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
250 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
251 ZERO_STRUCT(kt_entry
);
254 /* flush keytab, we need to regen it */
255 ret
= flush_keytab(krbctx
, *keytab
);
257 DEBUG(1, (__location__
": Failed to flush "
258 "memory keytab!\n"));
263 /* keytab is not up to date, fill it up */
265 ret
= smb_krb5_make_principal(krbctx
, &princ
, realm
,
266 info
->account_name
, NULL
);
268 DEBUG(1, (__location__
": Failed to get host principal!\n"));
272 ret
= fill_keytab_from_password(krbctx
, *keytab
,
276 DBG_WARNING("fill_keytab_from_password() failed for "
277 "info->password.\n.");
281 if (info
->old_password
!= NULL
) {
282 ret
= fill_keytab_from_password(krbctx
, *keytab
,
286 DBG_WARNING("fill_keytab_from_password() failed for "
287 "info->old_password.\n.");
292 if (info
->older_password
!= NULL
) {
293 ret
= fill_keytab_from_password(krbctx
, *keytab
,
295 info
->older_password
);
297 DBG_WARNING("fill_keytab_from_password() failed for "
298 "info->older_password.\n.");
303 if (info
->next_change
!= NULL
) {
304 ret
= fill_keytab_from_password(krbctx
, *keytab
,
306 info
->next_change
->password
);
308 DBG_WARNING("fill_keytab_from_password() failed for "
309 "info->next_change->password.\n.");
314 /* add our private enctype + cleartext password so that we can
315 * update the keytab if secrets change later on */
316 ZERO_STRUCT(kt_entry
);
317 kt_entry
.principal
= princ
;
320 KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry
)) = CLEARTEXT_PRIV_ENCTYPE
;
321 KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry
)) = ct
->length
;
322 KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry
)) = ct
->data
;
324 ret
= krb5_kt_add_entry(krbctx
, *keytab
, &kt_entry
);
326 DEBUG(1, (__location__
": Failed to add entry to "
327 "keytab for private enctype (%d) (error: %s)\n",
328 CLEARTEXT_PRIV_ENCTYPE
, error_message(ret
)));
337 krb5_free_principal(krbctx
, princ
);
344 static krb5_error_code
fill_mem_keytab_from_system_keytab(krb5_context krbctx
,
345 krb5_keytab
*mkeytab
)
347 krb5_error_code ret
= 0;
348 krb5_keytab keytab
= NULL
;
349 krb5_kt_cursor kt_cursor
= { 0, };
350 krb5_keytab_entry kt_entry
= { 0, };
351 char *valid_princ_formats
[7] = { NULL
, NULL
, NULL
,
352 NULL
, NULL
, NULL
, NULL
};
353 char *entry_princ_s
= NULL
;
354 fstring my_name
, my_fqdn
;
357 const char *dns_hostname
= NULL
;
359 /* Generate the list of principal names which we expect
360 * clients might want to use for authenticating to the file
361 * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
363 fstrcpy(my_name
, lp_netbios_name());
364 dns_hostname
= lp_dns_hostname();
365 if (dns_hostname
== NULL
) {
369 fstrcpy(my_fqdn
, dns_hostname
);
371 err
= asprintf(&valid_princ_formats
[0],
372 "%s$@%s", my_name
, lp_realm());
377 err
= asprintf(&valid_princ_formats
[1],
378 "host/%s@%s", my_name
, lp_realm());
383 err
= asprintf(&valid_princ_formats
[2],
384 "host/%s@%s", my_fqdn
, lp_realm());
389 err
= asprintf(&valid_princ_formats
[3],
390 "host/%s.%s@%s", my_name
, lp_realm(), lp_realm());
395 err
= asprintf(&valid_princ_formats
[4],
396 "cifs/%s@%s", my_name
, lp_realm());
401 err
= asprintf(&valid_princ_formats
[5],
402 "cifs/%s@%s", my_fqdn
, lp_realm());
407 err
= asprintf(&valid_princ_formats
[6],
408 "cifs/%s.%s@%s", my_name
, lp_realm(), lp_realm());
414 ret
= smb_krb5_kt_open_relative(krbctx
, NULL
, false, &keytab
);
416 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
417 error_message(ret
)));
422 * Iterate through the keytab. For each key, if the principal
423 * name case-insensitively matches one of the allowed formats,
424 * copy it to the memory keytab.
427 ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &kt_cursor
);
429 DEBUG(1, (__location__
": krb5_kt_start_seq_get failed (%s)\n",
430 error_message(ret
)));
432 * krb5_kt_start_seq_get() may leaves bogus data
433 * in kt_cursor. And we want to use the all_zero()
438 ZERO_STRUCT(kt_cursor
);
442 while ((krb5_kt_next_entry(krbctx
, keytab
,
443 &kt_entry
, &kt_cursor
) == 0)) {
444 ret
= smb_krb5_unparse_name(talloc_tos(), krbctx
,
448 DEBUG(1, (__location__
": smb_krb5_unparse_name "
449 "failed (%s)\n", error_message(ret
)));
453 for (i
= 0; i
< ARRAY_SIZE(valid_princ_formats
); i
++) {
455 if (!strequal(entry_princ_s
, valid_princ_formats
[i
])) {
459 ret
= krb5_kt_add_entry(krbctx
, *mkeytab
, &kt_entry
);
461 DBG_WARNING("krb5_kt_add_entry failed (%s)\n",
467 /* Free the name we parsed. */
468 TALLOC_FREE(entry_princ_s
);
470 /* Free the entry we just read. */
471 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
472 ZERO_STRUCT(kt_entry
);
474 krb5_kt_end_seq_get(krbctx
, keytab
, &kt_cursor
);
476 ZERO_STRUCT(kt_cursor
);
480 for (i
= 0; i
< ARRAY_SIZE(valid_princ_formats
); i
++) {
481 SAFE_FREE(valid_princ_formats
[i
]);
484 TALLOC_FREE(entry_princ_s
);
486 if (!all_zero((uint8_t *)&kt_entry
, sizeof(kt_entry
))) {
487 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
490 if (!all_zero((uint8_t *)&kt_cursor
, sizeof(kt_cursor
)) && keytab
) {
491 krb5_kt_end_seq_get(krbctx
, keytab
, &kt_cursor
);
495 krb5_kt_close(krbctx
, keytab
);
501 static krb5_error_code
fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx
,
502 krb5_keytab
*mkeytab
)
504 krb5_error_code ret
= 0;
505 krb5_keytab keytab
= NULL
;
506 krb5_kt_cursor kt_cursor
;
507 krb5_keytab_entry kt_entry
;
509 ret
= smb_krb5_kt_open(krbctx
, lp_dedicated_keytab_file(),
512 DEBUG(1, ("smb_krb5_kt_open of %s failed (%s)\n",
513 lp_dedicated_keytab_file(),
514 error_message(ret
)));
519 * Copy the dedicated keyab to our in-memory keytab.
522 ret
= krb5_kt_start_seq_get(krbctx
, keytab
, &kt_cursor
);
524 DEBUG(1, (__location__
": krb5_kt_start_seq_get on %s "
526 lp_dedicated_keytab_file(),
527 error_message(ret
)));
531 while ((krb5_kt_next_entry(krbctx
, keytab
,
532 &kt_entry
, &kt_cursor
) == 0)) {
534 ret
= krb5_kt_add_entry(krbctx
, *mkeytab
, &kt_entry
);
536 /* Free the entry we just read. */
537 smb_krb5_kt_free_entry(krbctx
, &kt_entry
);
540 DEBUG(1, (__location__
": smb_krb5_unparse_name "
541 "failed (%s)\n", error_message(ret
)));
545 krb5_kt_end_seq_get(krbctx
, keytab
, &kt_cursor
);
549 krb5_kt_close(krbctx
, keytab
);
554 krb5_error_code
gse_krb5_get_server_keytab(krb5_context krbctx
,
557 krb5_error_code ret
= 0;
558 krb5_error_code ret1
= 0;
559 krb5_error_code ret2
= 0;
563 /* create memory keytab */
564 ret
= krb5_kt_resolve(krbctx
, SRV_MEM_KEYTAB_NAME
, keytab
);
566 DEBUG(1, (__location__
": Failed to get memory "
571 switch (lp_kerberos_method()) {
573 case KERBEROS_VERIFY_SECRETS
:
574 ret
= fill_mem_keytab_from_secrets(krbctx
, keytab
);
576 case KERBEROS_VERIFY_SYSTEM_KEYTAB
:
577 ret
= fill_mem_keytab_from_system_keytab(krbctx
, keytab
);
579 case KERBEROS_VERIFY_DEDICATED_KEYTAB
:
580 /* just use whatever keytab is configured */
581 ret
= fill_mem_keytab_from_dedicated_keytab(krbctx
, keytab
);
583 case KERBEROS_VERIFY_SECRETS_AND_KEYTAB
:
584 ret1
= fill_mem_keytab_from_secrets(krbctx
, keytab
);
586 DEBUG(3, (__location__
": Warning! Unable to set mem "
587 "keytab from secrets!\n"));
589 /* Now append system keytab keys too */
590 ret2
= fill_mem_keytab_from_system_keytab(krbctx
, keytab
);
592 DEBUG(3, (__location__
": Warning! Unable to set mem "
593 "keytab from system keytab!\n"));
595 if (ret1
== 0 || ret2
== 0) {
604 krb5_kt_close(krbctx
, *keytab
);
606 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
613 #endif /* HAVE_KRB5 */