smbd: trigger NOTIFY_ACTION_DIRLEASE_BREAK when creating hardlink
[samba4-gss.git] / source4 / libnet / libnet_export_keytab.c
blob7db14cea12812b4f073dfc4aea6a222d4025ff0b
1 /*
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/>.
21 #include "includes.h"
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"
31 #include "kdc/sdb.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 = {};
45 krb5_keytab keytab;
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,
55 keytab_name,
56 true, /* write_access */
57 &keytab);
58 if (code != 0) {
59 *error_string = talloc_asprintf(mem_ctx,
60 "Failed to open keytab: %s",
61 keytab_name);
62 status = NT_STATUS_NO_SUCH_FILE;
63 goto done;
66 if (copy_one_principal) {
67 krb5_principal k5_princ;
69 code = smb_krb5_parse_name(context, principal, &k5_princ);
70 if (code != 0) {
71 *error_string = smb_get_krb5_error_message(context,
72 code,
73 mem_ctx);
74 status = NT_STATUS_UNSUCCESSFUL;
75 goto done;
78 code = samba_kdc_fetch(context, db_ctx, k5_princ,
79 SDB_F_GET_ANY | sdb_flags,
80 0, &sentry);
82 krb5_free_principal(context, k5_princ);
83 } else {
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)) {
88 int i;
89 bool found_previous = false;
90 tmp_ctx = talloc_new(mem_ctx);
91 if (tmp_ctx == NULL) {
92 status = NT_STATUS_NO_MEMORY;
93 goto done;
96 code = krb5_unparse_name(context,
97 sentry.principal,
98 &entry_principal);
99 if (code != 0) {
100 *error_string = smb_get_krb5_error_message(context,
101 code,
102 mem_ctx);
103 status = NT_STATUS_UNSUCCESSFUL;
104 goto done;
107 if (!keep_stale_entries) {
108 code = smb_krb5_remove_obsolete_keytab_entries(mem_ctx,
109 context,
110 keytab,
111 1, &sentry.principal,
112 sentry.kvno,
113 &found_previous,
114 error_string);
115 if (code != 0) {
116 *error_string = talloc_asprintf(mem_ctx,
117 "Failed to remove old principals from keytab: %s\n",
118 *error_string);
119 status = NT_STATUS_UNSUCCESSFUL;
120 goto done;
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
134 * now
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.
146 ldb_dn_minimise(dn);
148 status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
149 smb_krb5_context,
150 keytab,
151 sentry.principal,
152 db_ctx->samdb,
154 include_historic_keys,
155 error_string);
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",
161 principal,
162 *error_string);
163 goto done;
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",
167 extended_dn,
168 *error_string);
169 goto done;
171 } else {
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]);
179 krb5_keyblock *keyp;
180 bool found;
182 keyp = KRB5_KT_KEY(&kt_entry);
184 *keyp = s->key;
186 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
187 context,
188 keytab,
189 &kt_entry,
190 &found,
191 error_string);
192 if (code != 0) {
193 status = NT_STATUS_UNSUCCESSFUL;
194 *error_string = smb_get_krb5_error_message(context,
195 code,
196 mem_ctx);
197 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
198 code, *error_string));
199 goto done;
202 if (found) {
203 continue;
206 code = krb5_kt_add_entry(context, keytab, &kt_entry);
207 if (code != 0) {
208 status = NT_STATUS_UNSUCCESSFUL;
209 *error_string = smb_get_krb5_error_message(context,
210 code,
211 mem_ctx);
212 DBG_ERR("krb5_kt_add_entry failed "
213 "code=%d, error = %s\n",
214 code, *error_string);
215 goto done;
217 keys_exported = true;
219 kt_entry.vno -= 1;
220 for (i = 0; include_historic_keys && i < sentry.old_keys.len; i++) {
221 struct sdb_key *s = &(sentry.old_keys.val[i]);
222 krb5_keyblock *keyp;
223 bool found;
225 keyp = KRB5_KT_KEY(&kt_entry);
227 *keyp = s->key;
229 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
230 context,
231 keytab,
232 &kt_entry,
233 &found,
234 error_string);
235 if (code != 0) {
236 status = NT_STATUS_UNSUCCESSFUL;
237 *error_string = smb_get_krb5_error_message(context,
238 code,
239 mem_ctx);
240 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
241 code, *error_string));
242 goto done;
245 if (found) {
246 continue;
249 code = krb5_kt_add_entry(context, keytab, &kt_entry);
250 if (code != 0) {
251 status = NT_STATUS_UNSUCCESSFUL;
252 *error_string = smb_get_krb5_error_message(context,
253 code,
254 mem_ctx);
255 DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
256 code, *error_string));
257 goto done;
259 keys_exported = true;
261 kt_entry.vno -= 1;
262 for (i = 0; include_historic_keys && i < sentry.older_keys.len; i++) {
263 struct sdb_key *s = &(sentry.older_keys.val[i]);
264 krb5_keyblock *keyp;
265 bool found;
267 keyp = KRB5_KT_KEY(&kt_entry);
269 *keyp = s->key;
271 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
272 context,
273 keytab,
274 &kt_entry,
275 &found,
276 error_string);
277 if (code != 0) {
278 status = NT_STATUS_UNSUCCESSFUL;
279 *error_string = smb_get_krb5_error_message(context,
280 code,
281 mem_ctx);
282 DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
283 code, *error_string));
284 goto done;
287 if (found) {
288 continue;
291 code = krb5_kt_add_entry(context, keytab, &kt_entry);
292 if (code != 0) {
293 status = NT_STATUS_UNSUCCESSFUL;
294 *error_string = smb_get_krb5_error_message(context,
295 code,
296 mem_ctx);
297 DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
298 code, *error_string));
299 goto done;
301 keys_exported = true;
305 if (copy_one_principal) {
306 break;
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,
316 code,
317 mem_ctx);
318 status = NT_STATUS_NO_SUCH_USER;
319 goto done;
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;
331 } else {
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;
339 } else {
340 status = NT_STATUS_OK;
343 done:
344 TALLOC_FREE(tmp_ctx);
345 SAFE_FREE(entry_principal);
346 sdb_entry_free(&sentry);
348 return status;
351 NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
353 krb5_error_code ret;
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;
358 unsigned sdb_flags;
359 NTSTATUS status;
361 bool keep_stale_entries = r->in.keep_stale_entries;
363 ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
364 if (ret) {
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) {
386 bool time_ok;
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(&current_nttime);
397 if (!time_ok) {
398 /* This is really quite unlikely */
399 r->out.error_string
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)) {
410 return status;
413 if (r->in.principal != NULL) {
414 DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
415 } else {
416 DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
417 if (!keep_stale_entries) {
418 struct stat st;
419 int stat_ret = stat(r->in.keytab_name, &st);
420 if (stat_ret == -1 && errno == ENOENT) {
421 /* continue */
422 } else if (stat_ret == -1) {
423 int errno_save = errno;
424 r->out.error_string
425 = talloc_asprintf(mem_ctx,
426 "Failure checking if keytab export location %s is an existing file: %s",
427 r->in.keytab_name,
428 strerror(errno_save));
429 return map_nt_error_from_unix_common(errno_save);
430 } else {
431 r->out.error_string
432 = talloc_asprintf(mem_ctx,
433 "Refusing to export keytab to existing file %s",
434 r->in.keytab_name);
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,
449 smb_krb5_context,
450 db_ctx,
451 r->in.keytab_name,
452 r->in.principal,
453 keep_stale_entries,
454 !r->in.only_current_keys,
455 sdb_flags,
456 &error_string);
458 talloc_free(db_ctx);
459 talloc_free(base_ctx);
461 if (!NT_STATUS_IS_OK(status)) {
462 r->out.error_string = error_string;
465 return status;