s3: piddir creation fix part 2.
[ira/wip.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
218         /* remove duplicates first ... */
219         ret = libnet_keytab_remove_entries(context, keytab, princ_s, kvno,
220                                            enctype, false);
221         if (ret) {
222                 DEBUG(1, ("libnet_keytab_remove_entries failed: %s\n",
223                           error_message(ret)));
224         }
225
226         ZERO_STRUCT(kt_entry);
227
228         kt_entry.vno = kvno;
229
230         ret = smb_krb5_parse_name(context, princ_s, &kt_entry.principal);
231         if (ret) {
232                 DEBUG(1, ("smb_krb5_parse_name(%s) failed (%s)\n",
233                           princ_s, error_message(ret)));
234                 return ret;
235         }
236
237         keyp = KRB5_KT_KEY(&kt_entry);
238
239         if (create_kerberos_key_from_string(context, kt_entry.principal,
240                                             &password, keyp, enctype, true))
241         {
242                 ret = KRB5KRB_ERR_GENERIC;
243                 goto done;
244         }
245
246         ret = krb5_kt_add_entry(context, keytab, &kt_entry);
247         if (ret) {
248                 DEBUG(1, ("adding entry to keytab failed (%s)\n",
249                           error_message(ret)));
250         }
251
252 done:
253         krb5_free_keyblock_contents(context, keyp);
254         krb5_free_principal(context, kt_entry.principal);
255         ZERO_STRUCT(kt_entry);
256         smb_krb5_kt_free_entry(context, &kt_entry);
257
258         return ret;
259 }
260
261 krb5_error_code libnet_keytab_add(struct libnet_keytab_context *ctx)
262 {
263         krb5_error_code ret = 0;
264         uint32_t i;
265
266
267         if (ctx->clean_old_entries) {
268                 DEBUG(0, ("cleaning old entries...\n"));
269                 for (i=0; i < ctx->count; i++) {
270                         struct libnet_keytab_entry *entry = &ctx->entries[i];
271
272                         ret = libnet_keytab_remove_entries(ctx->context,
273                                                            ctx->keytab,
274                                                            entry->principal,
275                                                            0,
276                                                            entry->enctype,
277                                                            true);
278                         if (ret) {
279                                 DEBUG(1,("libnet_keytab_add: Failed to remove "
280                                          "old entries for %s (enctype %u): %s\n",
281                                          entry->principal, entry->enctype,
282                                          error_message(ret)));
283                                 return ret;
284                         }
285                 }
286         }
287
288         for (i=0; i<ctx->count; i++) {
289
290                 struct libnet_keytab_entry *entry = &ctx->entries[i];
291                 krb5_data password;
292
293                 ZERO_STRUCT(password);
294                 password.data = (char *)entry->password.data;
295                 password.length = entry->password.length;
296
297                 ret = libnet_keytab_add_entry(ctx->context,
298                                               ctx->keytab,
299                                               entry->kvno,
300                                               entry->principal,
301                                               entry->enctype,
302                                               password);
303                 if (ret) {
304                         DEBUG(1,("libnet_keytab_add: "
305                                 "Failed to add entry to keytab file\n"));
306                         return ret;
307                 }
308         }
309
310         return ret;
311 }
312
313 struct libnet_keytab_entry *libnet_keytab_search(struct libnet_keytab_context *ctx,
314                                                  const char *principal,
315                                                  int kvno,
316                                                  const krb5_enctype enctype,
317                                                  TALLOC_CTX *mem_ctx)
318 {
319         krb5_error_code ret = 0;
320         krb5_kt_cursor cursor;
321         krb5_keytab_entry kt_entry;
322         struct libnet_keytab_entry *entry = NULL;
323
324         ZERO_STRUCT(kt_entry);
325         ZERO_STRUCT(cursor);
326
327         ret = krb5_kt_start_seq_get(ctx->context, ctx->keytab, &cursor);
328         if (ret) {
329                 DEBUG(10, ("krb5_kt_start_seq_get failed: %s\n",
330                           error_message(ret)));
331                 return NULL;
332         }
333
334         while (krb5_kt_next_entry(ctx->context, ctx->keytab, &kt_entry, &cursor) == 0)
335         {
336                 krb5_keyblock *keyp;
337                 char *princ_s = NULL;
338
339                 entry = NULL;
340
341                 if (kt_entry.vno != kvno) {
342                         goto cont;
343                 }
344
345                 keyp = KRB5_KT_KEY(&kt_entry);
346
347                 if (KRB5_KEY_TYPE(keyp) != enctype) {
348                         goto cont;
349                 }
350
351                 entry = talloc_zero(mem_ctx, struct libnet_keytab_entry);
352                 if (!entry) {
353                         DEBUG(3, ("talloc failed\n"));
354                         goto fail;
355                 }
356
357                 ret = smb_krb5_unparse_name(entry, ctx->context, kt_entry.principal,
358                                             &princ_s);
359                 if (ret) {
360                         goto cont;
361                 }
362
363                 if (strcmp(principal, princ_s) != 0) {
364                         goto cont;
365                 }
366
367                 entry->principal = talloc_strdup(entry, princ_s);
368                 if (!entry->principal) {
369                         DEBUG(3, ("talloc_strdup_failed\n"));
370                         goto fail;
371                 }
372
373                 entry->name = talloc_move(entry, &princ_s);
374
375                 entry->password = data_blob_talloc(entry, KRB5_KEY_DATA(keyp),
376                                                    KRB5_KEY_LENGTH(keyp));
377                 if (!entry->password.data) {
378                         DEBUG(3, ("data_blob_talloc failed\n"));
379                         goto fail;
380                 }
381
382                 DEBUG(10, ("found entry\n"));
383
384                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
385                 break;
386
387 fail:
388                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
389                 TALLOC_FREE(entry);
390                 break;
391
392 cont:
393                 smb_krb5_kt_free_entry(ctx->context, &kt_entry);
394                 TALLOC_FREE(entry);
395                 continue;
396         }
397
398         krb5_kt_end_seq_get(ctx->context, ctx->keytab, &cursor);
399         return entry;
400 }
401
402 /**
403  * Helper function to add data to the list
404  * of keytab entries. It builds the prefix from the input.
405  */
406 NTSTATUS libnet_keytab_add_to_keytab_entries(TALLOC_CTX *mem_ctx,
407                                              struct libnet_keytab_context *ctx,
408                                              uint32_t kvno,
409                                              const char *name,
410                                              const char *prefix,
411                                              const krb5_enctype enctype,
412                                              DATA_BLOB blob)
413 {
414         struct libnet_keytab_entry entry;
415
416         entry.kvno = kvno;
417         entry.name = talloc_strdup(mem_ctx, name);
418         entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
419                                           prefix ? prefix : "",
420                                           prefix ? "/" : "",
421                                           name, ctx->dns_domain_name);
422         entry.enctype = enctype;
423         entry.password = blob;
424         NT_STATUS_HAVE_NO_MEMORY(entry.name);
425         NT_STATUS_HAVE_NO_MEMORY(entry.principal);
426         NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
427
428         ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
429                      &ctx->entries, &ctx->count);
430         NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
431
432         return NT_STATUS_OK;
433 }
434
435 #endif /* HAVE_KRB5 */