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