Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
5 : Copyright (C) Andreas Schneider <asn@samba.org> 2016
6 :
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.
11 :
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.
16 :
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/>.
19 : */
20 :
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 :
33 60 : static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
34 : struct smb_krb5_context *smb_krb5_context,
35 : struct samba_kdc_db_context *db_ctx,
36 : const char *keytab_name,
37 : const char *principal,
38 : bool keep_stale_entries,
39 : bool include_historic_keys,
40 : const unsigned sdb_flags,
41 : const char **error_string)
42 : {
43 60 : struct sdb_entry sentry = {};
44 0 : krb5_keytab keytab;
45 60 : krb5_error_code code = 0;
46 60 : NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
47 60 : char *entry_principal = NULL;
48 60 : bool copy_one_principal = (principal != NULL);
49 60 : bool keys_exported = false;
50 60 : krb5_context context = smb_krb5_context->krb5_context;
51 60 : TALLOC_CTX *tmp_ctx = NULL;
52 :
53 60 : code = smb_krb5_kt_open_relative(context,
54 : keytab_name,
55 : true, /* write_access */
56 : &keytab);
57 60 : if (code != 0) {
58 0 : *error_string = talloc_asprintf(mem_ctx,
59 : "Failed to open keytab: %s",
60 : keytab_name);
61 0 : status = NT_STATUS_NO_SUCH_FILE;
62 0 : goto done;
63 : }
64 :
65 60 : if (copy_one_principal) {
66 0 : krb5_principal k5_princ;
67 :
68 50 : code = smb_krb5_parse_name(context, principal, &k5_princ);
69 50 : if (code != 0) {
70 0 : *error_string = smb_get_krb5_error_message(context,
71 : code,
72 : mem_ctx);
73 0 : status = NT_STATUS_UNSUCCESSFUL;
74 0 : goto done;
75 : }
76 :
77 50 : code = samba_kdc_fetch(context, db_ctx, k5_princ,
78 : SDB_F_GET_ANY | sdb_flags,
79 : 0, &sentry);
80 :
81 50 : krb5_free_principal(context, k5_princ);
82 : } else {
83 10 : code = samba_kdc_firstkey(context, db_ctx, sdb_flags, &sentry);
84 : }
85 :
86 186 : for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, sdb_flags, &sentry)) {
87 0 : int i;
88 176 : bool found_previous = false;
89 176 : tmp_ctx = talloc_new(mem_ctx);
90 176 : if (tmp_ctx == NULL) {
91 0 : status = NT_STATUS_NO_MEMORY;
92 0 : goto done;
93 : }
94 :
95 176 : code = krb5_unparse_name(context,
96 176 : sentry.principal,
97 : &entry_principal);
98 176 : if (code != 0) {
99 0 : *error_string = smb_get_krb5_error_message(context,
100 : code,
101 : mem_ctx);
102 0 : status = NT_STATUS_UNSUCCESSFUL;
103 0 : goto done;
104 : }
105 :
106 176 : if (!keep_stale_entries) {
107 42 : code = smb_krb5_remove_obsolete_keytab_entries(mem_ctx,
108 : context,
109 : keytab,
110 : 1, &sentry.principal,
111 21 : sentry.kvno,
112 : &found_previous,
113 : error_string);
114 42 : if (code != 0) {
115 0 : *error_string = talloc_asprintf(mem_ctx,
116 : "Failed to remove old principals from keytab: %s\n",
117 : *error_string);
118 0 : status = NT_STATUS_UNSUCCESSFUL;
119 0 : goto done;
120 : }
121 : }
122 :
123 : /*
124 : * If this was a gMSA and we did not just read the
125 : * keys directly, then generate them
126 : */
127 176 : if (sentry.skdc_entry->group_managed_service_account
128 10 : && sentry.keys.len == 0) {
129 2 : struct ldb_dn *dn = sentry.skdc_entry->msg->dn;
130 : /*
131 : * for error message only, but we are about to
132 : * destroy the string name, so write this out
133 : * now
134 : */
135 0 : const char *extended_dn =
136 2 : ldb_dn_get_extended_linearized(mem_ctx,
137 : dn,
138 : 1);
139 :
140 : /*
141 : * Modify the DN in the entry (not needed by
142 : * the KDC code any longer) to be minimal, so
143 : * we can search on it over LDAP.
144 : */
145 2 : ldb_dn_minimise(dn);
146 :
147 2 : status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
148 : smb_krb5_context,
149 : keytab,
150 : sentry.principal,
151 : db_ctx->samdb,
152 : dn,
153 : include_historic_keys,
154 : error_string);
155 2 : if (NT_STATUS_IS_OK(status)) {
156 2 : keys_exported = true;
157 0 : } else if (copy_one_principal) {
158 0 : *error_string = talloc_asprintf(mem_ctx,
159 : "Failed to write gMSA password for %s to keytab: %s\n",
160 : principal,
161 : *error_string);
162 0 : goto done;
163 0 : } else if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_USER_KEYS)) {
164 0 : *error_string = talloc_asprintf(mem_ctx,
165 : "Failed to write gMSA password for %s to keytab: %s\n",
166 : extended_dn,
167 : *error_string);
168 0 : goto done;
169 : }
170 : } else {
171 0 : krb5_keytab_entry kt_entry;
172 174 : ZERO_STRUCT(kt_entry);
173 174 : kt_entry.principal = sentry.principal;
174 174 : kt_entry.vno = sentry.kvno;
175 :
176 658 : for (i = 0; i < sentry.keys.len; i++) {
177 484 : struct sdb_key *s = &(sentry.keys.val[i]);
178 0 : krb5_keyblock *keyp;
179 0 : bool found;
180 :
181 484 : keyp = KRB5_KT_KEY(&kt_entry);
182 :
183 484 : *keyp = s->key;
184 :
185 484 : code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
186 : context,
187 : keytab,
188 : &kt_entry,
189 : &found,
190 : error_string);
191 484 : if (code != 0) {
192 0 : status = NT_STATUS_UNSUCCESSFUL;
193 0 : *error_string = smb_get_krb5_error_message(context,
194 : code,
195 : mem_ctx);
196 0 : DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
197 : code, *error_string));
198 0 : goto done;
199 : }
200 :
201 484 : if (found) {
202 134 : continue;
203 : }
204 :
205 350 : code = krb5_kt_add_entry(context, keytab, &kt_entry);
206 350 : if (code != 0) {
207 0 : status = NT_STATUS_UNSUCCESSFUL;
208 0 : *error_string = smb_get_krb5_error_message(context,
209 : code,
210 : mem_ctx);
211 0 : DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
212 : code, *error_string));
213 0 : goto done;
214 : }
215 350 : keys_exported = true;
216 : }
217 174 : kt_entry.vno -= 1;
218 222 : for (i = 0; include_historic_keys && i < sentry.old_keys.len; i++) {
219 48 : struct sdb_key *s = &(sentry.old_keys.val[i]);
220 0 : krb5_keyblock *keyp;
221 0 : bool found;
222 :
223 48 : keyp = KRB5_KT_KEY(&kt_entry);
224 :
225 48 : *keyp = s->key;
226 :
227 48 : code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
228 : context,
229 : keytab,
230 : &kt_entry,
231 : &found,
232 : error_string);
233 48 : if (code != 0) {
234 0 : status = NT_STATUS_UNSUCCESSFUL;
235 0 : *error_string = smb_get_krb5_error_message(context,
236 : code,
237 : mem_ctx);
238 0 : DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
239 : code, *error_string));
240 0 : goto done;
241 : }
242 :
243 48 : if (found) {
244 24 : continue;
245 : }
246 :
247 24 : code = krb5_kt_add_entry(context, keytab, &kt_entry);
248 24 : if (code != 0) {
249 0 : status = NT_STATUS_UNSUCCESSFUL;
250 0 : *error_string = smb_get_krb5_error_message(context,
251 : code,
252 : mem_ctx);
253 0 : DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
254 : code, *error_string));
255 0 : goto done;
256 : }
257 24 : keys_exported = true;
258 : }
259 174 : kt_entry.vno -= 1;
260 192 : for (i = 0; include_historic_keys && i < sentry.older_keys.len; i++) {
261 18 : struct sdb_key *s = &(sentry.older_keys.val[i]);
262 0 : krb5_keyblock *keyp;
263 0 : bool found;
264 :
265 18 : keyp = KRB5_KT_KEY(&kt_entry);
266 :
267 18 : *keyp = s->key;
268 :
269 18 : code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
270 : context,
271 : keytab,
272 : &kt_entry,
273 : &found,
274 : error_string);
275 18 : if (code != 0) {
276 0 : status = NT_STATUS_UNSUCCESSFUL;
277 0 : *error_string = smb_get_krb5_error_message(context,
278 : code,
279 : mem_ctx);
280 0 : DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
281 : code, *error_string));
282 0 : goto done;
283 : }
284 :
285 18 : if (found) {
286 6 : continue;
287 : }
288 :
289 12 : code = krb5_kt_add_entry(context, keytab, &kt_entry);
290 12 : if (code != 0) {
291 0 : status = NT_STATUS_UNSUCCESSFUL;
292 0 : *error_string = smb_get_krb5_error_message(context,
293 : code,
294 : mem_ctx);
295 0 : DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
296 : code, *error_string));
297 0 : goto done;
298 : }
299 12 : keys_exported = true;
300 : }
301 : }
302 :
303 176 : if (copy_one_principal) {
304 50 : break;
305 : }
306 :
307 126 : TALLOC_FREE(tmp_ctx);
308 126 : SAFE_FREE(entry_principal);
309 126 : sdb_entry_free(&sentry);
310 : }
311 :
312 60 : if (code != 0 && code != SDB_ERR_NOENTRY) {
313 0 : *error_string = smb_get_krb5_error_message(context,
314 : code,
315 : mem_ctx);
316 0 : status = NT_STATUS_NO_SUCH_USER;
317 0 : goto done;
318 : }
319 :
320 60 : if (keys_exported == false) {
321 2 : if (keep_stale_entries == false) {
322 0 : *error_string = talloc_asprintf(mem_ctx,
323 : "No keys found while exporting %s. "
324 : "Consider connecting to a local sam.ldb, "
325 : "only gMSA accounts can be exported over "
326 : "LDAP and connecting user needs to be authorized",
327 : principal ? principal : "all users in domain");
328 0 : status = NT_STATUS_NO_USER_KEYS;
329 : } else {
330 2 : DBG_NOTICE("No new keys found while exporting %s. "
331 : "If new keys were expected, consider connecting "
332 : "to a local sam.ldb, only gMSA accounts can be exported over "
333 : "LDAP and connecting user needs to be authorized\n",
334 : principal ? principal : "all users in domain");
335 2 : status = NT_STATUS_OK;
336 : }
337 : } else {
338 58 : status = NT_STATUS_OK;
339 : }
340 :
341 60 : done:
342 60 : TALLOC_FREE(tmp_ctx);
343 60 : SAFE_FREE(entry_principal);
344 60 : sdb_entry_free(&sentry);
345 :
346 60 : return status;
347 : }
348 :
349 64 : NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
350 : {
351 0 : krb5_error_code ret;
352 0 : struct smb_krb5_context *smb_krb5_context;
353 0 : struct samba_kdc_base_context *base_ctx;
354 64 : struct samba_kdc_db_context *db_ctx = NULL;
355 64 : const char *error_string = NULL;
356 0 : unsigned sdb_flags;
357 0 : NTSTATUS status;
358 :
359 64 : bool keep_stale_entries = r->in.keep_stale_entries;
360 :
361 64 : ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
362 64 : if (ret) {
363 0 : return NT_STATUS_NO_MEMORY;
364 : }
365 :
366 64 : base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
367 64 : if (base_ctx == NULL) {
368 0 : return NT_STATUS_NO_MEMORY;
369 : }
370 :
371 64 : base_ctx->ev_ctx = ctx->event_ctx;
372 64 : base_ctx->lp_ctx = ctx->lp_ctx;
373 64 : base_ctx->samdb = r->in.samdb;
374 :
375 64 : status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
376 64 : if (!NT_STATUS_IS_OK(status)) {
377 0 : return status;
378 : }
379 :
380 64 : if (r->in.principal != NULL) {
381 50 : DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
382 : } else {
383 14 : DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
384 14 : if (!keep_stale_entries) {
385 0 : struct stat st;
386 10 : int stat_ret = stat(r->in.keytab_name, &st);
387 10 : if (stat_ret == -1 && errno == ENOENT) {
388 : /* continue */
389 4 : } else if (stat_ret == -1) {
390 2 : int errno_save = errno;
391 0 : r->out.error_string
392 2 : = talloc_asprintf(mem_ctx,
393 : "Failure checking if keytab export location %s is an existing file: %s",
394 : r->in.keytab_name,
395 : strerror(errno_save));
396 4 : return map_nt_error_from_unix_common(errno_save);
397 : } else {
398 0 : r->out.error_string
399 2 : = talloc_asprintf(mem_ctx,
400 : "Refusing to export keytab to existing file %s",
401 : r->in.keytab_name);
402 2 : return NT_STATUS_OBJECT_NAME_EXISTS;
403 : }
404 :
405 : /*
406 : * No point looking for old
407 : * keys in a empty file
408 : */
409 6 : keep_stale_entries = true;
410 : }
411 : }
412 :
413 60 : sdb_flags = r->in.as_for_AS_REQ ? SDB_F_FOR_AS_REQ : SDB_F_ADMIN_DATA;
414 :
415 60 : status = sdb_kt_copy(mem_ctx,
416 : smb_krb5_context,
417 : db_ctx,
418 : r->in.keytab_name,
419 : r->in.principal,
420 : keep_stale_entries,
421 60 : !r->in.only_current_keys,
422 : sdb_flags,
423 60 : &error_string);
424 :
425 60 : talloc_free(db_ctx);
426 60 : talloc_free(base_ctx);
427 :
428 60 : if (!NT_STATUS_IS_OK(status)) {
429 0 : r->out.error_string = error_string;
430 : }
431 :
432 60 : return status;
433 : }
|