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