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 52 : 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 : const char **error_string)
40 : {
41 52 : struct sdb_entry sentry = {};
42 0 : krb5_keytab keytab;
43 52 : krb5_error_code code = 0;
44 52 : NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
45 52 : char *entry_principal = NULL;
46 52 : bool copy_one_principal = (principal != NULL);
47 52 : bool keys_exported = false;
48 52 : krb5_context context = smb_krb5_context->krb5_context;
49 52 : TALLOC_CTX *tmp_ctx = NULL;
50 :
51 52 : code = smb_krb5_kt_open_relative(context,
52 : keytab_name,
53 : true, /* write_access */
54 : &keytab);
55 52 : if (code != 0) {
56 0 : *error_string = talloc_asprintf(mem_ctx,
57 : "Failed to open keytab: %s",
58 : keytab_name);
59 0 : status = NT_STATUS_NO_SUCH_FILE;
60 0 : goto done;
61 : }
62 :
63 52 : if (copy_one_principal) {
64 0 : krb5_principal k5_princ;
65 :
66 42 : code = smb_krb5_parse_name(context, principal, &k5_princ);
67 42 : if (code != 0) {
68 0 : *error_string = smb_get_krb5_error_message(context,
69 : code,
70 : mem_ctx);
71 0 : status = NT_STATUS_UNSUCCESSFUL;
72 0 : goto done;
73 : }
74 :
75 42 : code = samba_kdc_fetch(context, db_ctx, k5_princ,
76 : SDB_F_GET_ANY | SDB_F_ADMIN_DATA,
77 : 0, &sentry);
78 :
79 42 : krb5_free_principal(context, k5_princ);
80 : } else {
81 10 : code = samba_kdc_firstkey(context, db_ctx, &sentry);
82 : }
83 :
84 178 : for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, &sentry)) {
85 0 : int i;
86 168 : bool found_previous = false;
87 168 : tmp_ctx = talloc_new(mem_ctx);
88 168 : if (tmp_ctx == NULL) {
89 0 : status = NT_STATUS_NO_MEMORY;
90 0 : goto done;
91 : }
92 :
93 168 : code = krb5_unparse_name(context,
94 168 : sentry.principal,
95 : &entry_principal);
96 168 : if (code != 0) {
97 0 : *error_string = smb_get_krb5_error_message(context,
98 : code,
99 : mem_ctx);
100 0 : status = NT_STATUS_UNSUCCESSFUL;
101 0 : goto done;
102 : }
103 :
104 168 : if (!keep_stale_entries) {
105 36 : code = smb_krb5_remove_obsolete_keytab_entries(mem_ctx,
106 : context,
107 : keytab,
108 : 1, &sentry.principal,
109 18 : sentry.kvno,
110 : &found_previous,
111 : error_string);
112 36 : if (code != 0) {
113 0 : *error_string = talloc_asprintf(mem_ctx,
114 : "Failed to remove old principals from keytab: %s\n",
115 : *error_string);
116 0 : status = NT_STATUS_UNSUCCESSFUL;
117 0 : goto done;
118 : }
119 : }
120 :
121 : /*
122 : * If this was a gMSA and we did not just read the
123 : * keys directly, then generate them
124 : */
125 168 : if (sentry.skdc_entry->group_managed_service_account
126 6 : && sentry.keys.len == 0) {
127 2 : struct ldb_dn *dn = sentry.skdc_entry->msg->dn;
128 : /*
129 : * for error message only, but we are about to
130 : * destroy the string name, so write this out
131 : * now
132 : */
133 0 : const char *extended_dn =
134 2 : ldb_dn_get_extended_linearized(mem_ctx,
135 : dn,
136 : 1);
137 :
138 : /*
139 : * Modify the DN in the entry (not needed by
140 : * the KDC code any longer) to be minimal, so
141 : * we can search on it over LDAP.
142 : */
143 2 : ldb_dn_minimise(dn);
144 :
145 2 : status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
146 : smb_krb5_context,
147 : keytab,
148 : sentry.principal,
149 : db_ctx->samdb,
150 : dn,
151 : error_string);
152 2 : if (NT_STATUS_IS_OK(status)) {
153 2 : keys_exported = true;
154 0 : } else if (copy_one_principal) {
155 0 : *error_string = talloc_asprintf(mem_ctx,
156 : "Failed to write gMSA password for %s to keytab: %s\n",
157 : principal,
158 : *error_string);
159 0 : goto done;
160 0 : } else if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_USER_KEYS)) {
161 0 : *error_string = talloc_asprintf(mem_ctx,
162 : "Failed to write gMSA password for %s to keytab: %s\n",
163 : extended_dn,
164 : *error_string);
165 0 : goto done;
166 : }
167 : } else {
168 0 : krb5_keytab_entry kt_entry;
169 166 : ZERO_STRUCT(kt_entry);
170 166 : kt_entry.principal = sentry.principal;
171 166 : kt_entry.vno = sentry.kvno;
172 :
173 620 : for (i = 0; i < sentry.keys.len; i++) {
174 454 : struct sdb_key *s = &(sentry.keys.val[i]);
175 0 : krb5_keyblock *keyp;
176 0 : bool found;
177 :
178 454 : keyp = KRB5_KT_KEY(&kt_entry);
179 :
180 454 : *keyp = s->key;
181 :
182 454 : code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
183 : context,
184 : keytab,
185 : &kt_entry,
186 : &found,
187 : error_string);
188 454 : if (code != 0) {
189 0 : status = NT_STATUS_UNSUCCESSFUL;
190 0 : *error_string = smb_get_krb5_error_message(context,
191 : code,
192 : mem_ctx);
193 0 : DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
194 : code, *error_string));
195 0 : goto done;
196 : }
197 :
198 454 : if (found) {
199 134 : continue;
200 : }
201 :
202 320 : code = krb5_kt_add_entry(context, keytab, &kt_entry);
203 320 : if (code != 0) {
204 0 : status = NT_STATUS_UNSUCCESSFUL;
205 0 : *error_string = smb_get_krb5_error_message(context,
206 : code,
207 : mem_ctx);
208 0 : DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
209 : code, *error_string));
210 0 : goto done;
211 : }
212 320 : keys_exported = true;
213 : }
214 : }
215 :
216 168 : if (copy_one_principal) {
217 42 : break;
218 : }
219 :
220 126 : TALLOC_FREE(tmp_ctx);
221 126 : SAFE_FREE(entry_principal);
222 126 : sdb_entry_free(&sentry);
223 : }
224 :
225 52 : if (code != 0 && code != SDB_ERR_NOENTRY) {
226 0 : *error_string = smb_get_krb5_error_message(context,
227 : code,
228 : mem_ctx);
229 0 : status = NT_STATUS_NO_SUCH_USER;
230 0 : goto done;
231 : }
232 :
233 52 : if (keys_exported == false) {
234 2 : if (keep_stale_entries == false) {
235 0 : *error_string = talloc_asprintf(mem_ctx,
236 : "No keys found while exporting %s. "
237 : "Consider connecting to a local sam.ldb, "
238 : "only gMSA accounts can be exported over "
239 : "LDAP and connecting user needs to be authorized",
240 : principal ? principal : "all users in domain");
241 0 : status = NT_STATUS_NO_USER_KEYS;
242 : } else {
243 2 : DBG_NOTICE("No new keys found while exporting %s. "
244 : "If new keys were expected, consider connecting "
245 : "to a local sam.ldb, only gMSA accounts can be exported over "
246 : "LDAP and connecting user needs to be authorized\n",
247 : principal ? principal : "all users in domain");
248 2 : status = NT_STATUS_OK;
249 : }
250 : } else {
251 50 : status = NT_STATUS_OK;
252 : }
253 :
254 52 : done:
255 52 : TALLOC_FREE(tmp_ctx);
256 52 : SAFE_FREE(entry_principal);
257 52 : sdb_entry_free(&sentry);
258 :
259 52 : return status;
260 : }
261 :
262 56 : NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
263 : {
264 0 : krb5_error_code ret;
265 0 : struct smb_krb5_context *smb_krb5_context;
266 0 : struct samba_kdc_base_context *base_ctx;
267 56 : struct samba_kdc_db_context *db_ctx = NULL;
268 56 : const char *error_string = NULL;
269 0 : NTSTATUS status;
270 :
271 56 : bool keep_stale_entries = r->in.keep_stale_entries;
272 :
273 56 : ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
274 56 : if (ret) {
275 0 : return NT_STATUS_NO_MEMORY;
276 : }
277 :
278 56 : base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
279 56 : if (base_ctx == NULL) {
280 0 : return NT_STATUS_NO_MEMORY;
281 : }
282 :
283 56 : base_ctx->ev_ctx = ctx->event_ctx;
284 56 : base_ctx->lp_ctx = ctx->lp_ctx;
285 56 : base_ctx->samdb = r->in.samdb;
286 :
287 56 : status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
288 56 : if (!NT_STATUS_IS_OK(status)) {
289 0 : return status;
290 : }
291 :
292 56 : if (r->in.principal != NULL) {
293 42 : DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
294 : } else {
295 14 : DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
296 14 : if (!keep_stale_entries) {
297 0 : struct stat st;
298 10 : int stat_ret = stat(r->in.keytab_name, &st);
299 10 : if (stat_ret == -1 && errno == ENOENT) {
300 : /* continue */
301 4 : } else if (stat_ret == -1) {
302 2 : int errno_save = errno;
303 0 : r->out.error_string
304 2 : = talloc_asprintf(mem_ctx,
305 : "Failure checking if keytab export location %s is an existing file: %s",
306 : r->in.keytab_name,
307 : strerror(errno_save));
308 4 : return map_nt_error_from_unix_common(errno_save);
309 : } else {
310 0 : r->out.error_string
311 2 : = talloc_asprintf(mem_ctx,
312 : "Refusing to export keytab to existing file %s",
313 : r->in.keytab_name);
314 2 : return NT_STATUS_OBJECT_NAME_EXISTS;
315 : }
316 :
317 : /*
318 : * No point looking for old
319 : * keys in a empty file
320 : */
321 6 : keep_stale_entries = true;
322 : }
323 : }
324 :
325 :
326 52 : status = sdb_kt_copy(mem_ctx,
327 : smb_krb5_context,
328 : db_ctx,
329 : r->in.keytab_name,
330 : r->in.principal,
331 : keep_stale_entries,
332 : &error_string);
333 :
334 52 : talloc_free(db_ctx);
335 52 : talloc_free(base_ctx);
336 :
337 52 : if (!NT_STATUS_IS_OK(status)) {
338 0 : r->out.error_string = error_string;
339 : }
340 :
341 52 : return status;
342 : }
|