1/*
2   Unix SMB/CIFS implementation.
3   dump the remote SAM using rpc samsync operations
4
5   Copyright (C) Guenther Deschner 2008.
6   Copyright (C) Michael Adam 2008
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#include "libnet/libnet.h"
24
25#ifdef HAVE_KRB5
26
27/****************************************************************
28****************************************************************/
29
30static int keytab_close(struct libnet_keytab_context *ctx)
31{
32	if (!ctx) {
33		return 0;
34	}
35
36	if (ctx->keytab && ctx->context) {
37		krb5_kt_close(ctx->context, ctx->keytab);
38	}
39
40	if (ctx->context) {
41		krb5_free_context(ctx->context);
42	}
43
44	if (ctx->ads) {
45		ads_destroy(&ctx->ads);
46	}
47
48	TALLOC_FREE(ctx);
49
50	return 0;
51}
52
53/****************************************************************
54****************************************************************/
55
56krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
57				   const char *keytab_name,
58				   struct libnet_keytab_context **ctx)
59{
60	krb5_error_code ret = 0;
61	krb5_context context = NULL;
62	krb5_keytab keytab = NULL;
63	const char *keytab_string = NULL;
64
65	struct libnet_keytab_context *r;
66
67	r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context);
68	if (!r) {
69		return ENOMEM;
70	}
71
72	talloc_set_destructor(r, keytab_close);
73
74	initialize_krb5_error_table();
75	ret = krb5_init_context(&context);
76	if (ret) {
77		DEBUG(1,("keytab_init: could not krb5_init_context: %s\n",
78			error_message(ret)));
79		return ret;
80	}
81
82	ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
83	if (ret) {
84		DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
85			error_message(ret)));
86		krb5_free_context(context);
87		return ret;
88	}
89
90	ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
91	if (ret) {
92		krb5_kt_close(context, keytab);
93		krb5_free_context(context);
94		return ret;
95	}
96
97	r->context = context;
98	r->keytab = keytab;
99	r->keytab_name = keytab_string;
100	r->clean_old_entries = false;
101
102	*ctx = r;
103
104	return 0;
105}
106
107/****************************************************************
108****************************************************************/
109
110/**
111 * Remove all entries that have the given principal, kvno and enctype.
112 */
113static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
114						    krb5_keytab keytab,
115						    const char *principal,
116						    int kvno,
117						    const krb5_enctype enctype,
118						    bool ignore_kvno)
119{
120	krb5_error_code ret;
121	krb5_kt_cursor cursor;
122	krb5_keytab_entry kt_entry;
123
124	ZERO_STRUCT(kt_entry);
125	ZERO_STRUCT(cursor);
126
127	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
128	if (ret) {
129		return 0;
130	}
131
132	while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
133	{
134		krb5_keyblock *keyp;
135		char *princ_s = NULL;
136
137		if (kt_entry.vno != kvno && !ignore_kvno) {
138			goto cont;
139		}
140
141		keyp = KRB5_KT_KEY(&kt_entry);
142
143		if (KRB5_KEY_TYPE(keyp) != enctype) {
144			goto cont;
145		}
146
147		ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal,
148					    &princ_s);
149		if (ret) {
150			DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
151				  error_message(ret)));
152			goto cont;
153		}
154
155		if (strcmp(principal, princ_s) != 0) {
156			goto cont;
157		}
158
159		/* match found - remove */
160
161		DEBUG(10, ("found entry for principal %s, kvno %d, "
162			   "enctype %d - trying to remove it\n",
163			   princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
164
165		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
166		ZERO_STRUCT(cursor);
167		if (ret) {
168			DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
169				  error_message(ret)));
170			goto cont;
171		}
172
173		ret = krb5_kt_remove_entry(context, keytab,
174					   &kt_entry);
175		if (ret) {
176			DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
177				  error_message(ret)));
178			goto cont;
179		}
180		DEBUG(10, ("removed entry for principal %s, kvno %d, "
181			   "enctype %d\n", princ_s, kt_entry.vno,
182			   KRB5_KEY_TYPE(keyp)));
183
184		ret = krb5_kt_start_seq_get(context, keytab, &cursor);
185		if (ret) {
186			DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
187				  error_message(ret)));
188			goto cont;
189		}
190
191cont:
192		smb_krb5_kt_free_entry(context, &kt_entry);
193		TALLOC_FREE(princ_s);
194	}
195
196	ret = krb5_kt_end_seq_get(context, keytab, &cursor);
197	if (ret) {
198		DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
199			  error_message(ret)));
200	}
201
202	return ret;
203}
204
205static krb5_error_code libnet_keytab_add_entry(krb5_context context,
206					       krb5_keytab keytab,
207					       krb5_kvno kvno,
208					       const char *princ_s,
209					       krb5_enctype enctype,
210					       krb5_data password)
211{
212	krb5_keyblock *keyp;
213	krb5_keytab_entry kt_entry;
214	krb5_error_code ret;
215
216	/* remove duplicates first ... */
217	ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
218					   enctype, false);
219	if (ret) {
220		DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
221			  error_message(ret)));
222	}
223
224	ZERO_STRUCT(kt_entry);
225
226	kt_entry.vno = kvno;
227
228	ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
229	if (ret) {
230		DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
231			  princ_s, error_message(ret)));
232		return ret;
233	}
234
235	keyp = KRB5_KT_KEY(&kt_entry);
236
237	if (create_kerberos_key_from_string(context, kt_entry.principal,
238					    &password, keyp, enctype, true))
239	{
240		ret = KRB5KRB_ERR_GENERIC;
241		goto done;
242	}
243
244	ret = krb5_kt_add_entry(context, keytab, &kt_entry);
245	if (ret) {
246		DEBUG(1, ("adding entry to keytab failed (%s)\n",
247			  error_message(ret)));
248	}
249
250done:
251	krb5_free_keyblock_contents(context, keyp);
252	krb5_free_principal(context, kt_entry.principal);
253	ZERO_STRUCT(kt_entry);
254	smb_krb5_kt_free_entry(context, &kt_entry);
255
256	return ret;
257}
258
259krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
260{
261	krb5_error_code ret = 0;
262	uint32_t i;
263
264
265	if (ctx->clean_old_entries) {
266		DEBUG(0, ("cleaning old entries...\n"));
267		for (i=0; i < ctx->count; i++) {
268			struct libnet_keytab_entry *entry = &ctx->entries[i];
269
270			ret = libnet_keytab_remove_entries(ctx->context,
271							   ctx->keytab,
272							   entry->principal,
273							   0,
274							   entry->enctype,
275							   true);
276			if (ret) {
277				DEBUG(1,("libnet_keytab_add: Failed to remove "
278					 "old entries for %s (enctype %u): %s\n",
279					 entry->principal, entry->enctype,
280					 error_message(ret)));
281				return ret;
282			}
283		}
284	}
285
286	for (i=0; i<ctx->count; i++) {
287
288		struct libnet_keytab_entry *entry = &ctx->entries[i];
289		krb5_data password;
290
291		ZERO_STRUCT(password);
292		password.data = (char *)entry->password.data;
293		password.length = entry->password.length;
294
295		ret = libnet_keytab_add_entry(ctx->context,
296					      ctx->keytab,
297					      entry->kvno,
298					      entry->principal,
299					      entry->enctype,
300					      password);
301		if (ret) {
302			DEBUG(1,("libnet_keytab_add: "
303				"Failed to add entry to keytab file\n"));
304			return ret;
305		}
306	}
307
308	return ret;
309}
310
311struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
312						 const char *principal,
313						 int kvno,
314						 const krb5_enctype enctype,
315						 TALLOC_CTX *mem_ctx)
316{
317	krb5_error_code ret = 0;
318	krb5_kt_cursor cursor;
319	krb5_keytab_entry kt_entry;
320	struct libnet_keytab_entry *entry = NULL;
321
322	ZERO_STRUCT(kt_entry);
323	ZERO_STRUCT(cursor);
324
325	ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
326	if (ret) {
327		DEBUG(10, ("krb5_kt_start_seq_get failed: %s\n",
328			  error_message(ret)));
329		return NULL;
330	}
331
332	while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
333	{
334		krb5_keyblock *keyp;
335		char *princ_s = NULL;
336
337		entry = NULL;
338
339		if (kt_entry.vno != kvno) {
340			goto cont;
341		}
342
343		keyp = KRB5_KT_KEY(&kt_entry);
344
345		if (KRB5_KEY_TYPE(keyp) != enctype) {
346			goto cont;
347		}
348
349		entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
350		if (!entry) {
351			DEBUG(3, ("talloc failed\n"));
352			goto fail;
353		}
354
355		ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal,
356					    &princ_s);
357		if (ret) {
358			goto cont;
359		}
360
361		if (strcmp(principal, princ_s) != 0) {
362			goto cont;
363		}
364
365		entry->principal = talloc_strdup(entry, princ_s);
366		if (!entry->principal) {
367			DEBUG(3, ("talloc_strdup_failed\n"));
368			goto fail;
369		}
370
371		entry->name = talloc_move(entry, &princ_s);
372
373		entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
374						   KRB5_KEY_LENGTH(keyp));
375		if (!entry->password.data) {
376			DEBUG(3, ("data_blob_talloc failed\n"));
377			goto fail;
378		}
379
380		DEBUG(10, ("found entry\n"));
381
382		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
383		break;
384
385fail:
386		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
387		TALLOC_FREE(entry);
388		break;
389
390cont:
391		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
392		TALLOC_FREE(entry);
393		continue;
394	}
395
396	krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
397	return entry;
398}
399
400/**
401 * Helper function to add data to the list
402 * of keytab entries. It builds the prefix from the input.
403 */
404NTSTATUS libnet_keytab_add_to_keytab_entries(TALLOC_CTX *mem_ctx,
405					     struct libnet_keytab_context *ctx,
406					     uint32_t kvno,
407					     const char *name,
408					     const char *prefix,
409					     const krb5_enctype enctype,
410					     DATA_BLOB blob)
411{
412	struct libnet_keytab_entry entry;
413
414	entry.kvno = kvno;
415	entry.name = talloc_strdup(mem_ctx, name);
416	entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
417					  prefix ? prefix : "",
418					  prefix ? "/" : "",
419					  name, ctx->dns_domain_name);
420	entry.enctype = enctype;
421	entry.password = blob;
422	NT_STATUS_HAVE_NO_MEMORY(entry.name);
423	NT_STATUS_HAVE_NO_MEMORY(entry.principal);
424	NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
425
426	ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
427		     &ctx->entries, &ctx->count);
428	NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
429
430	return NT_STATUS_OK;
431}
432
433#endif /* HAVE_KRB5 */
434