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