836cf6ed23459fb4a1483f89fb971f85f51fea79
[samba.git] / source3 / libnet / libnet_keytab.c
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 #ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE /* Heimdal */
28 #define KRB5_KEY_TYPE(k)        ((k)->keytype)
29 #define KRB5_KEY_LENGTH(k)      ((k)->keyvalue.length)
30 #define KRB5_KEY_DATA(k)        ((k)->keyvalue.data)
31 #else /* MIT */
32 #define KRB5_KEY_TYPE(k)        ((k)->enctype)
33 #define KRB5_KEY_LENGTH(k)      ((k)->length)
34 #define KRB5_KEY_DATA(k)        ((k)->contents)
35 #endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
36
37 /****************************************************************
38 ****************************************************************/
39
40 static int keytab_close(struct libnet_keytab_context *ctx)
41 {
42         if (!ctx) {
43                 return 0;
44         }
45
46         if (ctx->keytab && ctx->context) {
47                 krb5_kt_close(ctx->context, ctx->keytab);
48         }
49
50         if (ctx->context) {
51                 krb5_free_context(ctx->context);
52         }
53
54         if (ctx->ads) {
55                 ads_destroy(&ctx->ads);
56         }
57
58         TALLOC_FREE(ctx);
59
60         return 0;
61 }
62
63 /****************************************************************
64 ****************************************************************/
65
66 krb5_error_code libnet_keytab_init(TALLOC_CTX *mem_ctx,
67                                    const char *keytab_name,
68                                    struct libnet_keytab_context **ctx)
69 {
70         krb5_error_code ret = 0;
71         krb5_context context = NULL;
72         krb5_keytab keytab = NULL;
73         const char *keytab_string = NULL;
74
75         struct libnet_keytab_context *r;
76
77         r = TALLOC_ZERO_P(mem_ctx, struct libnet_keytab_context);
78         if (!r) {
79                 return ENOMEM;
80         }
81
82         talloc_set_destructor(r, keytab_close);
83
84         initialize_krb5_error_table();
85         ret = krb5_init_context(&context);
86         if (ret) {
87                 DEBUG(1,("keytab_init: could not krb5_init_context: %s\n",
88                         error_message(ret)));
89                 return ret;
90         }
91
92         ret = smb_krb5_open_keytab(context, keytab_name, true, &keytab);
93         if (ret) {
94                 DEBUG(1,("keytab_init: smb_krb5_open_keytab failed (%s)\n",
95                         error_message(ret)));
96                 krb5_free_context(context);
97                 return ret;
98         }
99
100         ret = smb_krb5_keytab_name(mem_ctx, context, keytab, &keytab_string);
101         if (ret) {
102                 krb5_kt_close(context, keytab);
103                 krb5_free_context(context);
104                 return ret;
105         }
106
107         r->context = context;
108         r->keytab = keytab;
109         r->keytab_name = keytab_string;
110         r->clean_old_entries = false;
111
112         *ctx = r;
113
114         return 0;
115 }
116
117 /****************************************************************
118 ****************************************************************/
119
120 /**
121  * Remove all entries that have the given principal, kvno and enctype.
122  */
123 static krb5_error_code libnet_keytab_remove_entries(krb5_context context,
124                                                     krb5_keytab keytab,
125                                                     const char *principal,
126                                                     int kvno,
127                                                     const krb5_enctype enctype,
128                                                     bool ignore_kvno)
129 {
130         krb5_error_code ret;
131         krb5_kt_cursor cursor;
132         krb5_keytab_entry kt_entry;
133
134         ZERO_STRUCT(kt_entry);
135         ZERO_STRUCT(cursor);
136
137         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
138         if (ret) {
139                 return 0;
140         }
141
142         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0)
143         {
144                 krb5_keyblock *keyp;
145                 char *princ_s = NULL;
146
147                 if (kt_entry.vno != kvno && !ignore_kvno) {
148                         goto cont;
149                 }
150
151 #if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
152 #error krb5_keytab_entry has no key or keyblock member
153 #endif
154 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
155         keyp = &kt_entry.key;
156 #endif
157 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
158         keyp = &kt_entry.keyblock;
159 #endif
160
161                 if (KRB5_KEY_TYPE(keyp) != enctype) {
162                         goto cont;
163                 }
164
165                 ret = smb_krb5_unparse_name(context, kt_entry.principal,
166                                             &princ_s);
167                 if (ret) {
168                         DEBUG(5, ("smb_krb5_unparse_name failed (%s)\n",
169                                   error_message(ret)));
170                         goto cont;
171                 }
172
173                 if (strcmp(principal, princ_s) != 0) {
174                         goto cont;
175                 }
176
177                 /* match found - remove */
178
179                 DEBUG(10, ("found entry for principal %s, kvno %d, "
180                            "enctype %d - trying to remove it\n",
181                            princ_s, kt_entry.vno, KRB5_KEY_TYPE(keyp)));
182
183                 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
184                 ZERO_STRUCT(cursor);
185                 if (ret) {
186                         DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
187                                   error_message(ret)));
188                         goto cont;
189                 }
190
191                 ret = krb5_kt_remove_entry(context, keytab,
192                                            &kt_entry);
193                 if (ret) {
194                         DEBUG(5, ("krb5_kt_remove_entry failed (%s)\n",
195                                   error_message(ret)));
196                         goto cont;
197                 }
198                 DEBUG(10, ("removed entry for principal %s, kvno %d, "
199                            "enctype %d\n", princ_s, kt_entry.vno,
200                            KRB5_KEY_TYPE(keyp)));
201
202                 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
203                 if (ret) {
204                         DEBUG(5, ("krb5_kt_start_seq_get failed (%s)\n",
205                                   error_message(ret)));
206                         goto cont;
207                 }
208
209 cont:
210                 smb_krb5_kt_free_entry(context, &kt_entry);
211                 SAFE_FREE(princ_s);
212         }
213
214         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
215         if (ret) {
216                 DEBUG(5, ("krb5_kt_end_seq_get failed (%s)\n",
217                           error_message(ret)));
218         }
219
220         return ret;
221 }
222
223 static krb5_error_code libnet_keytab_add_entry(krb5_context context,
224                                                krb5_keytab keytab,
225                                                krb5_kvno kvno,
226                                                const char *princ_s,
227                                                krb5_enctype enctype,
228                                                krb5_data password)
229 {
230         krb5_keyblock *keyp;
231         krb5_keytab_entry kt_entry;
232         krb5_error_code ret;
233
234         /* remove duplicates first ... */
235         ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
236                                            enctype, false);
237         if (ret) {
238                 DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
239                           error_message(ret)));
240         }
241
242         ZERO_STRUCT(kt_entry);
243
244         kt_entry.vno = kvno;
245
246         ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
247         if (ret) {
248                 DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
249                           princ_s, error_message(ret)));
250                 return ret;
251         }
252
253 #if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
254 #error krb5_keytab_entry has no key or keyblock member
255 #endif
256 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
257         keyp = &kt_entry.key;
258 #endif
259 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
260         keyp = &kt_entry.keyblock;
261 #endif
262
263         if (create_kerberos_key_from_string(context, kt_entry.principal,
264                                             &password, keyp, enctype, true))
265         {
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
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                 password.data = (char *)entry->password.data;
318                 password.length = entry->password.length;
319
320                 ret = libnet_keytab_add_entry(ctx->context,
321                                               ctx->keytab,
322                                               entry->kvno,
323                                               entry->principal,
324                                               entry->enctype,
325                                               password);
326                 if (ret) {
327                         DEBUG(1,("libnet_keytab_add: "
328                                 "Failed to add entry to keytab file\n"));
329                         return ret;
330                 }
331         }
332
333         return ret;
334 }
335
336 struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
337                                                  const char *principal,
338                                                  int kvno,
339                                                  const krb5_enctype enctype,
340                                                  TALLOC_CTX *mem_ctx)
341 {
342         krb5_error_code ret = 0;
343         krb5_kt_cursor cursor;
344         krb5_keytab_entry kt_entry;
345         struct libnet_keytab_entry *entry = NULL;
346
347         ZERO_STRUCT(kt_entry);
348         ZERO_STRUCT(cursor);
349
350         ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
351         if (ret) {
352                 DEBUG(10, ("krb5_kt_start_seq_get failed: %s",
353                           error_message(ret)));
354                 return NULL;
355         }
356
357         while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
358         {
359                 krb5_keyblock *keyp;
360                 char *princ_s = NULL;
361
362                 if (kt_entry.vno != kvno) {
363                         goto cont;
364                 }
365
366 #if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
367 #error krb5_keytab_entry has no key or keyblock member
368 #endif
369 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY               /* MIT */
370         keyp = &kt_entry.key;
371 #endif
372 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK          /* Heimdal */
373         keyp = &kt_entry.keyblock;
374 #endif
375
376                 if (KRB5_KEY_TYPE(keyp) != enctype) {
377                         goto cont;
378                 }
379
380                 ret = smb_krb5_unparse_name(ctx->context, kt_entry.principal,
381                                             &princ_s);
382                 if (ret) {
383                         goto cont;
384                 }
385
386                 if (strcmp(principal, princ_s) != 0) {
387                         goto cont;
388                 }
389
390                 entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
391                 if (!entry) {
392                         DEBUG(3, ("talloc failed\n"));
393                         goto fail;
394                 }
395
396                 entry->name = talloc_strdup(entry, princ_s);
397                 if (!entry->name) {
398                         DEBUG(3, ("talloc_strdup_failed\n"));
399                         goto fail;
400                 }
401
402                 entry->principal = talloc_strdup(entry, princ_s);
403                 if (!entry->principal) {
404                         DEBUG(3, ("talloc_strdup_failed\n"));
405                         goto fail;
406                 }
407
408                 entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
409                                                    KRB5_KEY_LENGTH(keyp));
410                 if (!entry->password.data) {
411                         DEBUG(3, ("data_blob_talloc failed\n"));
412                         goto fail;
413                 }
414
415                 DEBUG(10, ("found entry\n"));
416
417                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
418                 SAFE_FREE(princ_s);
419                 break;
420
421 fail:
422                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
423                 SAFE_FREE(princ_s);
424                 TALLOC_FREE(entry);
425                 break;
426
427 cont:
428                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
429                 SAFE_FREE(princ_s);
430                 continue;
431         }
432
433         krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
434         return entry;
435 }
436
437 #endif /* HAVE_KRB5 */