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 "smb_krb5.h"
24 #include "ads.h"
25 #include "secrets.h"
26 #include "libnet/libnet_keytab.h"
27 
28 #ifdef HAVE_KRB5
29 
30 /****************************************************************
31 ****************************************************************/
32 
keytab_close(struct libnet_keytab_context * ctx)33 static int keytab_close(struct libnet_keytab_context *ctx)
34 {
35 	if (!ctx) {
36 		return 0;
37 	}
38 
39 	if (ctx->keytab && ctx->context) {
40 		krb5_kt_close(ctx->context, ctx->keytab);
41 	}
42 
43 	if (ctx->context) {
44 		krb5_free_context(ctx->context);
45 	}
46 
47 	if (ctx->ads) {
48 		ads_destroy(&ctx->ads);
49 	}
50 
51 	TALLOC_FREE(ctx);
52 
53 	return 0;
54 }
55 
56 /****************************************************************
57 ****************************************************************/
58 
libnet_keytab_init(TALLOC_CTX * mem_ctx,const char * keytab_name,struct libnet_keytab_context ** ctx)59 krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
60 				   const char *keytab_name,
61 				   struct libnet_keytab_context **ctx)
62 {
63 	krb5_error_code ret = 0;
64 	krb5_context context = NULL;
65 	krb5_keytab keytab = NULL;
66 	const char *keytab_string = NULL;
67 
68 	struct libnet_keytab_context *r;
69 
70 	r = talloc_zero(mem_ctx, struct libnet_keytab_context);
71 	if (!r) {
72 		return ENOMEM;
73 	}
74 
75 	talloc_set_destructor(r, keytab_close);
76 
77 	ret = smb_krb5_init_context_common(&context);
78 	if (ret) {
79 		DBG_ERR("kerberos init context failed (%s)\n",
80 			error_message(ret));
81 		return ret;
82 	}
83 
84 	ret = smb_krb5_kt_open_relative(context,
85 					keytab_name,
86 					true, /* write_access */
87 					&keytab);
88 	if (ret) {
89 		DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
90 			error_message(ret)));
91 		krb5_free_context(context);
92 		return ret;
93 	}
94 
95 	ret = smb_krb5_kt_get_name(mem_ctx, context, keytab, &keytab_string);
96 	if (ret) {
97 		krb5_kt_close(context, keytab);
98 		krb5_free_context(context);
99 		return ret;
100 	}
101 
102 	r->context = context;
103 	r->keytab = keytab;
104 	r->keytab_name = keytab_string;
105 	r->clean_old_entries = false;
106 
107 	*ctx = r;
108 
109 	return 0;
110 }
111 
112 /****************************************************************
113 ****************************************************************/
114 
115 /**
116  * Remove all entries that have the given principal, kvno and enctype.
117  */
libnet_keytab_remove_entries(krb5_context context,krb5_keytab keytab,const char * principal,int kvno,const krb5_enctype enctype,bool ignore_kvno)118 static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
119 						    krb5_keytab keytab,
120 						    const char *principal,
121 						    int kvno,
122 						    const krb5_enctype enctype,
123 						    bool ignore_kvno)
124 {
125 	krb5_error_code ret;
126 	krb5_kt_cursor cursor;
127 	krb5_keytab_entry kt_entry;
128 
129 	ZERO_STRUCT(kt_entry);
130 	ZERO_STRUCT(cursor);
131 
132 	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
133 	if (ret) {
134 		return 0;
135 	}
136 
137 	while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
138 	{
139 		krb5_keyblock *keyp;
140 		char *princ_s = NULL;
141 
142 		if (kt_entry.vno != kvno && !ignore_kvno) {
143 			goto cont;
144 		}
145 
146 		keyp = KRB5_KT_KEY(&kt_entry);
147 
148 		if (KRB5_KEY_TYPE(keyp) != enctype) {
149 			goto cont;
150 		}
151 
152 		ret = smb_krb5_unparse_name(talloc_tos(), context, kt_entry.principal,
153 					    &princ_s);
154 		if (ret) {
155 			DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
156 				  error_message(ret)));
157 			goto cont;
158 		}
159 
160 		if (strcmp(principal, princ_s) != 0) {
161 			goto cont;
162 		}
163 
164 		/* match found - remove */
165 
166 		DEBUG(10, ("found entry for principal %s, kvno %d, "
167 			   "enctype %d - trying to remove it\n",
168 			   princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
169 
170 		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
171 		ZERO_STRUCT(cursor);
172 		if (ret) {
173 			DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
174 				  error_message(ret)));
175 			goto cont;
176 		}
177 
178 		ret = krb5_kt_remove_entry(context, keytab,
179 					   &kt_entry);
180 		if (ret) {
181 			DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
182 				  error_message(ret)));
183 			goto cont;
184 		}
185 		DEBUG(10, ("removed entry for principal %s, kvno %d, "
186 			   "enctype %d\n", princ_s, kt_entry.vno,
187 			   KRB5_KEY_TYPE(keyp)));
188 
189 		ret = krb5_kt_start_seq_get(context, keytab, &cursor);
190 		if (ret) {
191 			DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
192 				  error_message(ret)));
193 			goto cont;
194 		}
195 
196 cont:
197 		smb_krb5_kt_free_entry(context, &kt_entry);
198 		TALLOC_FREE(princ_s);
199 	}
200 
201 	ret = krb5_kt_end_seq_get(context, keytab, &cursor);
202 	if (ret) {
203 		DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
204 			  error_message(ret)));
205 	}
206 
207 	return ret;
208 }
209 
libnet_keytab_add_entry(krb5_context context,krb5_keytab keytab,krb5_kvno kvno,const char * princ_s,krb5_enctype enctype,krb5_data password)210 static krb5_error_code libnet_keytab_add_entry(krb5_context context,
211 					       krb5_keytab keytab,
212 					       krb5_kvno kvno,
213 					       const char *princ_s,
214 					       krb5_enctype enctype,
215 					       krb5_data password)
216 {
217 	krb5_keyblock *keyp;
218 	krb5_keytab_entry kt_entry;
219 	krb5_error_code ret;
220 	krb5_principal salt_princ = NULL;
221 	char *salt_princ_s;
222 
223 	/* remove duplicates first ... */
224 	ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
225 					   enctype, false);
226 	if (ret) {
227 		DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
228 			  error_message(ret)));
229 	}
230 
231 	ZERO_STRUCT(kt_entry);
232 
233 	kt_entry.vno = kvno;
234 
235 	ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
236 	if (ret) {
237 		DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
238 			  princ_s, error_message(ret)));
239 		return ret;
240 	}
241 
242 	keyp = KRB5_KT_KEY(&kt_entry);
243 
244 	salt_princ_s = kerberos_secrets_fetch_salt_princ();
245 	if (salt_princ_s == NULL) {
246 		ret = KRB5KRB_ERR_GENERIC;
247 		goto done;
248 	}
249 
250 	ret = krb5_parse_name(context, salt_princ_s, &salt_princ);
251 	SAFE_FREE(salt_princ_s);
252 	if (ret != 0) {
253 		ret = KRB5KRB_ERR_GENERIC;
254 		goto done;
255 	}
256 
257 	ret = create_kerberos_key_from_string(context,
258 					      kt_entry.principal,
259 					      salt_princ,
260 					      &password,
261 					      keyp,
262 					      enctype,
263 					      true);
264 	krb5_free_principal(context, salt_princ);
265 	if (ret != 0) {
266 		ret = KRB5KRB_ERR_GENERIC;
267 		goto done;
268 	}
269 
270 	ret = krb5_kt_add_entry(context, keytab, &kt_entry);
271 	if (ret) {
272 		DEBUG(1, ("adding entry to keytab failed (%s)\n",
273 			  error_message(ret)));
274 	}
275 
276 done:
277 	krb5_free_keyblock_contents(context, keyp);
278 	krb5_free_principal(context, kt_entry.principal);
279 	ZERO_STRUCT(kt_entry);
280 	smb_krb5_kt_free_entry(context, &kt_entry);
281 
282 	return ret;
283 }
284 
libnet_keytab_add(struct libnet_keytab_context * ctx)285 krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
286 {
287 	krb5_error_code ret = 0;
288 	uint32_t i;
289 
290 
291 	if (ctx->clean_old_entries) {
292 		DEBUG(0, ("cleaning old entries...\n"));
293 		for (i=0; i < ctx->count; i++) {
294 			struct libnet_keytab_entry *entry = &ctx->entries[i];
295 
296 			ret = libnet_keytab_remove_entries(ctx->context,
297 							   ctx->keytab,
298 							   entry->principal,
299 							   0,
300 							   entry->enctype,
301 							   true);
302 			if (ret) {
303 				DEBUG(1,("libnet_keytab_add: Failed to remove "
304 					 "old entries for %s (enctype %u): %s\n",
305 					 entry->principal, entry->enctype,
306 					 error_message(ret)));
307 				return ret;
308 			}
309 		}
310 	}
311 
312 	for (i=0; i<ctx->count; i++) {
313 
314 		struct libnet_keytab_entry *entry = &ctx->entries[i];
315 		krb5_data password;
316 
317 		ZERO_STRUCT(password);
318 		password.data = (char *)entry->password.data;
319 		password.length = entry->password.length;
320 
321 		ret = libnet_keytab_add_entry(ctx->context,
322 					      ctx->keytab,
323 					      entry->kvno,
324 					      entry->principal,
325 					      entry->enctype,
326 					      password);
327 		if (ret) {
328 			DEBUG(1,("libnet_keytab_add: "
329 				"Failed to add entry to keytab file\n"));
330 			return ret;
331 		}
332 	}
333 
334 	return ret;
335 }
336 
libnet_keytab_search(struct libnet_keytab_context * ctx,const char * principal,int kvno,const krb5_enctype enctype,TALLOC_CTX * mem_ctx)337 struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
338 						 const char *principal,
339 						 int kvno,
340 						 const krb5_enctype enctype,
341 						 TALLOC_CTX *mem_ctx)
342 {
343 	krb5_error_code ret = 0;
344 	krb5_kt_cursor cursor;
345 	krb5_keytab_entry kt_entry;
346 	struct libnet_keytab_entry *entry = NULL;
347 
348 	ZERO_STRUCT(kt_entry);
349 	ZERO_STRUCT(cursor);
350 
351 	ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
352 	if (ret) {
353 		DEBUG(10, ("krb5_kt_start_seq_get failed: %s\n",
354 			  error_message(ret)));
355 		return NULL;
356 	}
357 
358 	while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
359 	{
360 		krb5_keyblock *keyp;
361 		char *princ_s = NULL;
362 
363 		entry = NULL;
364 
365 		if (kt_entry.vno != kvno) {
366 			goto cont;
367 		}
368 
369 		keyp = KRB5_KT_KEY(&kt_entry);
370 
371 		if (KRB5_KEY_TYPE(keyp) != enctype) {
372 			goto cont;
373 		}
374 
375 		entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
376 		if (!entry) {
377 			DEBUG(3, ("talloc failed\n"));
378 			goto fail;
379 		}
380 
381 		ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal,
382 					    &princ_s);
383 		if (ret) {
384 			goto cont;
385 		}
386 
387 		if (strcmp(principal, princ_s) != 0) {
388 			goto cont;
389 		}
390 
391 		entry->principal = talloc_strdup(entry, princ_s);
392 		if (!entry->principal) {
393 			DEBUG(3, ("talloc_strdup_failed\n"));
394 			goto fail;
395 		}
396 
397 		entry->name = talloc_move(entry, &princ_s);
398 
399 		entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
400 						   KRB5_KEY_LENGTH(keyp));
401 		if (!entry->password.data) {
402 			DEBUG(3, ("data_blob_talloc failed\n"));
403 			goto fail;
404 		}
405 
406 		DEBUG(10, ("found entry\n"));
407 
408 		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
409 		break;
410 
411 fail:
412 		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
413 		TALLOC_FREE(entry);
414 		break;
415 
416 cont:
417 		smb_krb5_kt_free_entry(ctx->context, &kt_entry);
418 		TALLOC_FREE(entry);
419 		continue;
420 	}
421 
422 	krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
423 	return entry;
424 }
425 
426 /**
427  * Helper function to add data to the list
428  * of keytab entries. It builds the prefix from the input.
429  */
libnet_keytab_add_to_keytab_entries(TALLOC_CTX * mem_ctx,struct libnet_keytab_context * ctx,uint32_t kvno,const char * name,const char * prefix,const krb5_enctype enctype,DATA_BLOB blob)430 NTSTATUS libnet_keytab_add_to_keytab_entries(TALLOC_CTX *mem_ctx,
431 					     struct libnet_keytab_context *ctx,
432 					     uint32_t kvno,
433 					     const char *name,
434 					     const char *prefix,
435 					     const krb5_enctype enctype,
436 					     DATA_BLOB blob)
437 {
438 	struct libnet_keytab_entry entry;
439 
440 	entry.kvno = kvno;
441 	entry.name = talloc_strdup(mem_ctx, name);
442 	entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
443 					  prefix ? prefix : "",
444 					  prefix ? "/" : "",
445 					  name, ctx->dns_domain_name);
446 	entry.enctype = enctype;
447 	entry.password = blob;
448 	NT_STATUS_HAVE_NO_MEMORY(entry.name);
449 	NT_STATUS_HAVE_NO_MEMORY(entry.principal);
450 	NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
451 
452 	ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
453 		     &ctx->entries, &ctx->count);
454 	NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
455 
456 	return NT_STATUS_OK;
457 }
458 
459 #endif /* HAVE_KRB5 */
460