2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
5 Copyright (C) Andreas Schneider <asn@samba.org> 2016
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "system/kerberos.h"
23 #include "auth/credentials/credentials.h"
24 #include "auth/kerberos/kerberos.h"
25 #include "auth/kerberos/kerberos_credentials.h"
26 #include "auth/kerberos/kerberos_util.h"
27 #include "auth/kerberos/kerberos_srv_keytab.h"
28 #include "kdc/samba_kdc.h"
29 #include "libnet/libnet_export_keytab.h"
30 #include "kdc/db-glue.h"
32 #include "dsdb/gmsa/util.h"
34 static NTSTATUS
sdb_kt_copy(TALLOC_CTX
*mem_ctx
,
35 struct smb_krb5_context
*smb_krb5_context
,
36 struct samba_kdc_db_context
*db_ctx
,
37 const char *keytab_name
,
38 const char *principal
,
39 bool keep_stale_entries
,
40 bool include_historic_keys
,
41 const unsigned sdb_flags
,
42 const char **error_string
)
44 struct sdb_entry sentry
= {};
46 krb5_error_code code
= 0;
47 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
48 char *entry_principal
= NULL
;
49 bool copy_one_principal
= (principal
!= NULL
);
50 bool keys_exported
= false;
51 krb5_context context
= smb_krb5_context
->krb5_context
;
52 TALLOC_CTX
*tmp_ctx
= NULL
;
54 code
= smb_krb5_kt_open_relative(context
,
56 true, /* write_access */
59 *error_string
= talloc_asprintf(mem_ctx
,
60 "Failed to open keytab: %s",
62 status
= NT_STATUS_NO_SUCH_FILE
;
66 if (copy_one_principal
) {
67 krb5_principal k5_princ
;
69 code
= smb_krb5_parse_name(context
, principal
, &k5_princ
);
71 *error_string
= smb_get_krb5_error_message(context
,
74 status
= NT_STATUS_UNSUCCESSFUL
;
78 code
= samba_kdc_fetch(context
, db_ctx
, k5_princ
,
79 SDB_F_GET_ANY
| sdb_flags
,
82 krb5_free_principal(context
, k5_princ
);
84 code
= samba_kdc_firstkey(context
, db_ctx
, sdb_flags
, &sentry
);
87 for (; code
== 0; code
= samba_kdc_nextkey(context
, db_ctx
, sdb_flags
, &sentry
)) {
89 bool found_previous
= false;
90 tmp_ctx
= talloc_new(mem_ctx
);
91 if (tmp_ctx
== NULL
) {
92 status
= NT_STATUS_NO_MEMORY
;
96 code
= krb5_unparse_name(context
,
100 *error_string
= smb_get_krb5_error_message(context
,
103 status
= NT_STATUS_UNSUCCESSFUL
;
107 if (!keep_stale_entries
) {
108 code
= smb_krb5_remove_obsolete_keytab_entries(mem_ctx
,
111 1, &sentry
.principal
,
116 *error_string
= talloc_asprintf(mem_ctx
,
117 "Failed to remove old principals from keytab: %s\n",
119 status
= NT_STATUS_UNSUCCESSFUL
;
125 * If this was a gMSA and we did not just read the
126 * keys directly, then generate them
128 if (sentry
.skdc_entry
->group_managed_service_account
129 && sentry
.keys
.len
== 0) {
130 struct ldb_dn
*dn
= sentry
.skdc_entry
->msg
->dn
;
132 * for error message only, but we are about to
133 * destroy the string name, so write this out
136 const char *extended_dn
=
137 ldb_dn_get_extended_linearized(mem_ctx
,
142 * Modify the DN in the entry (not needed by
143 * the KDC code any longer) to be minimal, so
144 * we can search on it over LDAP.
148 status
= smb_krb5_fill_keytab_gmsa_keys(tmp_ctx
,
154 include_historic_keys
,
156 if (NT_STATUS_IS_OK(status
)) {
157 keys_exported
= true;
158 } else if (copy_one_principal
) {
159 *error_string
= talloc_asprintf(mem_ctx
,
160 "Failed to write gMSA password for %s to keytab: %s\n",
164 } else if (!NT_STATUS_EQUAL(status
, NT_STATUS_NO_USER_KEYS
)) {
165 *error_string
= talloc_asprintf(mem_ctx
,
166 "Failed to write gMSA password for %s to keytab: %s\n",
172 krb5_keytab_entry kt_entry
;
173 ZERO_STRUCT(kt_entry
);
174 kt_entry
.principal
= sentry
.principal
;
175 kt_entry
.vno
= sentry
.kvno
;
177 for (i
= 0; i
< sentry
.keys
.len
; i
++) {
178 struct sdb_key
*s
= &(sentry
.keys
.val
[i
]);
182 keyp
= KRB5_KT_KEY(&kt_entry
);
186 code
= smb_krb5_is_exact_entry_in_keytab(mem_ctx
,
193 status
= NT_STATUS_UNSUCCESSFUL
;
194 *error_string
= smb_get_krb5_error_message(context
,
197 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
198 code
, *error_string
));
206 code
= krb5_kt_add_entry(context
, keytab
, &kt_entry
);
208 status
= NT_STATUS_UNSUCCESSFUL
;
209 *error_string
= smb_get_krb5_error_message(context
,
212 DBG_ERR("krb5_kt_add_entry failed "
213 "code=%d, error = %s\n",
214 code
, *error_string
);
217 keys_exported
= true;
220 for (i
= 0; include_historic_keys
&& i
< sentry
.old_keys
.len
; i
++) {
221 struct sdb_key
*s
= &(sentry
.old_keys
.val
[i
]);
225 keyp
= KRB5_KT_KEY(&kt_entry
);
229 code
= smb_krb5_is_exact_entry_in_keytab(mem_ctx
,
236 status
= NT_STATUS_UNSUCCESSFUL
;
237 *error_string
= smb_get_krb5_error_message(context
,
240 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
241 code
, *error_string
));
249 code
= krb5_kt_add_entry(context
, keytab
, &kt_entry
);
251 status
= NT_STATUS_UNSUCCESSFUL
;
252 *error_string
= smb_get_krb5_error_message(context
,
255 DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
256 code
, *error_string
));
259 keys_exported
= true;
262 for (i
= 0; include_historic_keys
&& i
< sentry
.older_keys
.len
; i
++) {
263 struct sdb_key
*s
= &(sentry
.older_keys
.val
[i
]);
267 keyp
= KRB5_KT_KEY(&kt_entry
);
271 code
= smb_krb5_is_exact_entry_in_keytab(mem_ctx
,
278 status
= NT_STATUS_UNSUCCESSFUL
;
279 *error_string
= smb_get_krb5_error_message(context
,
282 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
283 code
, *error_string
));
291 code
= krb5_kt_add_entry(context
, keytab
, &kt_entry
);
293 status
= NT_STATUS_UNSUCCESSFUL
;
294 *error_string
= smb_get_krb5_error_message(context
,
297 DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
298 code
, *error_string
));
301 keys_exported
= true;
305 if (copy_one_principal
) {
309 TALLOC_FREE(tmp_ctx
);
310 SAFE_FREE(entry_principal
);
311 sdb_entry_free(&sentry
);
314 if (code
!= 0 && code
!= SDB_ERR_NOENTRY
) {
315 *error_string
= smb_get_krb5_error_message(context
,
318 status
= NT_STATUS_NO_SUCH_USER
;
322 if (keys_exported
== false) {
323 if (keep_stale_entries
== false) {
324 *error_string
= talloc_asprintf(mem_ctx
,
325 "No keys found while exporting %s. "
326 "Consider connecting to a local sam.ldb, "
327 "only gMSA accounts can be exported over "
328 "LDAP and connecting user needs to be authorized",
329 principal
? principal
: "all users in domain");
330 status
= NT_STATUS_NO_USER_KEYS
;
332 DBG_NOTICE("No new keys found while exporting %s. "
333 "If new keys were expected, consider connecting "
334 "to a local sam.ldb, only gMSA accounts can be exported over "
335 "LDAP and connecting user needs to be authorized\n",
336 principal
? principal
: "all users in domain");
337 status
= NT_STATUS_OK
;
340 status
= NT_STATUS_OK
;
344 TALLOC_FREE(tmp_ctx
);
345 SAFE_FREE(entry_principal
);
346 sdb_entry_free(&sentry
);
351 NTSTATUS
libnet_export_keytab(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_export_keytab
*r
)
354 struct smb_krb5_context
*smb_krb5_context
;
355 struct samba_kdc_base_context
*base_ctx
;
356 struct samba_kdc_db_context
*db_ctx
= NULL
;
357 const char *error_string
= NULL
;
361 bool keep_stale_entries
= r
->in
.keep_stale_entries
;
363 ret
= smb_krb5_init_context(ctx
, ctx
->lp_ctx
, &smb_krb5_context
);
365 return NT_STATUS_NO_MEMORY
;
368 base_ctx
= talloc_zero(mem_ctx
, struct samba_kdc_base_context
);
369 if (base_ctx
== NULL
) {
370 return NT_STATUS_NO_MEMORY
;
373 base_ctx
->ev_ctx
= ctx
->event_ctx
;
374 base_ctx
->lp_ctx
= ctx
->lp_ctx
;
375 base_ctx
->samdb
= r
->in
.samdb
;
376 if (base_ctx
->samdb
!= NULL
) {
377 base_ctx
->current_nttime_ull
= talloc_get_type(
378 ldb_get_opaque(base_ctx
->samdb
, DSDB_GMSA_TIME_OPAQUE
), unsigned long long);
382 * If the caller hasn't set a fixed time, or a samdb, set up
383 * the pointer for the opaque and set to the current time
385 if (base_ctx
->current_nttime_ull
== NULL
) {
387 NTTIME current_nttime
;
389 base_ctx
->current_nttime_ull
= talloc_zero(base_ctx
, unsigned long long);
390 if (base_ctx
->current_nttime_ull
== NULL
) {
391 r
->out
.error_string
= NULL
;
392 return NT_STATUS_NO_MEMORY
;
395 time_ok
= gmsa_current_time(¤t_nttime
);
398 /* This is really quite unlikely */
400 = talloc_asprintf(mem_ctx
,
401 "Failed to get current time to check "
402 "time-dependent keys against for export");
403 return NT_STATUS_UNSUCCESSFUL
;
405 *base_ctx
->current_nttime_ull
= current_nttime
;
408 status
= samba_kdc_setup_db_ctx(mem_ctx
, base_ctx
, &db_ctx
);
409 if (!NT_STATUS_IS_OK(status
)) {
413 if (r
->in
.principal
!= NULL
) {
414 DEBUG(0, ("Export one principal to %s\n", r
->in
.keytab_name
));
416 DEBUG(0, ("Export complete keytab to %s\n", r
->in
.keytab_name
));
417 if (!keep_stale_entries
) {
419 int stat_ret
= stat(r
->in
.keytab_name
, &st
);
420 if (stat_ret
== -1 && errno
== ENOENT
) {
422 } else if (stat_ret
== -1) {
423 int errno_save
= errno
;
425 = talloc_asprintf(mem_ctx
,
426 "Failure checking if keytab export location %s is an existing file: %s",
428 strerror(errno_save
));
429 return map_nt_error_from_unix_common(errno_save
);
432 = talloc_asprintf(mem_ctx
,
433 "Refusing to export keytab to existing file %s",
435 return NT_STATUS_OBJECT_NAME_EXISTS
;
439 * No point looking for old
440 * keys in a empty file
442 keep_stale_entries
= true;
446 sdb_flags
= r
->in
.as_for_AS_REQ
? SDB_F_FOR_AS_REQ
: SDB_F_ADMIN_DATA
;
448 status
= sdb_kt_copy(mem_ctx
,
454 !r
->in
.only_current_keys
,
459 talloc_free(base_ctx
);
461 if (!NT_STATUS_IS_OK(status
)) {
462 r
->out
.error_string
= error_string
;