172616ca3dc220c59a3b5016ec069b6f3c9052a9
[metze/samba/wip.git] / source3 / librpc / crypto / gse_krb5.c
1 /*
2  *  GSSAPI Security Extensions
3  *  Krb5 helpers
4  *  Copyright (C) Simo Sorce 2010.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "smb_krb5.h"
22 #include "secrets.h"
23 #include "librpc/gen_ndr/secrets.h"
24 #include "gse_krb5.h"
25 #include "lib/param/loadparm.h"
26 #include "libads/kerberos_proto.h"
27
28 #ifdef HAVE_KRB5
29
30 static krb5_error_code flush_keytab(krb5_context krbctx, krb5_keytab keytab)
31 {
32         krb5_error_code ret;
33         krb5_kt_cursor kt_cursor;
34         krb5_keytab_entry kt_entry;
35
36         ZERO_STRUCT(kt_entry);
37
38         ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
39         if (ret == KRB5_KT_END || ret == ENOENT ) {
40                 /* no entries */
41                 return 0;
42         }
43
44         ret = krb5_kt_next_entry(krbctx, keytab, &kt_entry, &kt_cursor);
45         while (ret == 0) {
46
47                 /* we need to close and reopen enumeration because we modify
48                  * the keytab */
49                 ret = krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
50                 if (ret) {
51                         DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
52                                   "failed (%s)\n", error_message(ret)));
53                         goto out;
54                 }
55
56                 /* remove the entry */
57                 ret = krb5_kt_remove_entry(krbctx, keytab, &kt_entry);
58                 if (ret) {
59                         DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
60                                   "failed (%s)\n", error_message(ret)));
61                         goto out;
62                 }
63                 ret = smb_krb5_kt_free_entry(krbctx, &kt_entry);
64                 ZERO_STRUCT(kt_entry);
65
66                 /* now reopen */
67                 ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
68                 if (ret) {
69                         DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
70                                   "(%s)\n", error_message(ret)));
71                         goto out;
72                 }
73
74                 ret = krb5_kt_next_entry(krbctx, keytab,
75                                          &kt_entry, &kt_cursor);
76         }
77
78         if (ret != KRB5_KT_END && ret != ENOENT) {
79                 DEBUG(1, (__location__ ": flushing keytab we got [%s]!\n",
80                           error_message(ret)));
81         }
82
83         ret = 0;
84
85 out:
86         return ret;
87 }
88
89 static krb5_error_code fill_keytab_from_password(krb5_context krbctx,
90                                                  krb5_keytab keytab,
91                                                  krb5_principal princ,
92                                                  krb5_kvno vno,
93                                                  struct secrets_domain_info1_password *pw)
94 {
95         krb5_error_code ret;
96         krb5_enctype *enctypes;
97         uint16_t i;
98
99         ret = smb_krb5_get_allowed_etypes(krbctx, &enctypes);
100         if (ret) {
101                 DEBUG(1, (__location__
102                           ": Can't determine permitted enctypes!\n"));
103                 return ret;
104         }
105
106         for (i = 0; i < pw->num_keys; i++) {
107                 krb5_keytab_entry kt_entry;
108                 krb5_keyblock *key = NULL;
109                 unsigned int ei;
110                 bool found_etype = false;
111
112                 for (ei=0; enctypes[ei] != 0; ei++) {
113                         if ((uint32_t)enctypes[ei] != pw->keys[i].keytype) {
114                                 continue;
115                         }
116
117                         found_etype = true;
118                         break;
119                 }
120
121                 if (!found_etype) {
122                         continue;
123                 }
124
125                 ZERO_STRUCT(kt_entry);
126                 kt_entry.principal = princ;
127                 kt_entry.vno = vno;
128
129                 key = KRB5_KT_KEY(&kt_entry);
130                 KRB5_KEY_TYPE(key) = pw->keys[i].keytype;
131                 KRB5_KEY_DATA(key) = pw->keys[i].value.data;
132                 KRB5_KEY_LENGTH(key) = pw->keys[i].value.length;
133
134                 ret = krb5_kt_add_entry(krbctx, keytab, &kt_entry);
135                 if (ret) {
136                         DEBUG(1, (__location__ ": Failed to add entry to "
137                                   "keytab for enctype %d (error: %s)\n",
138                                   (unsigned)pw->keys[i].keytype,
139                                   error_message(ret)));
140                         goto out;
141                 }
142         }
143
144         ret = 0;
145
146 out:
147         SAFE_FREE(enctypes);
148         return ret;
149 }
150
151 #define SRV_MEM_KEYTAB_NAME "MEMORY:cifs_srv_keytab"
152 #define CLEARTEXT_PRIV_ENCTYPE -99
153
154 static krb5_error_code fill_mem_keytab_from_secrets(krb5_context krbctx,
155                                                     krb5_keytab *keytab)
156 {
157         TALLOC_CTX *frame = talloc_stackframe();
158         krb5_error_code ret;
159         const char *domain = lp_workgroup();
160         struct secrets_domain_info1 *info = NULL;
161         const char *realm = NULL;
162         const DATA_BLOB *ct = NULL;
163         krb5_kt_cursor kt_cursor;
164         krb5_keytab_entry kt_entry;
165         krb5_principal princ = NULL;
166         krb5_kvno kvno = 0; /* FIXME: fetch current vno from KDC ? */
167         NTSTATUS status;
168
169         if (!secrets_init()) {
170                 DEBUG(1, (__location__ ": secrets_init failed\n"));
171                 TALLOC_FREE(frame);
172                 return KRB5_CONFIG_CANTOPEN;
173         }
174
175         status = secrets_fetch_or_upgrade_domain_info(domain,
176                                                       frame,
177                                                       &info);
178         if (!NT_STATUS_IS_OK(status)) {
179                 DBG_WARNING("secrets_fetch_or_upgrade_domain_info(%s) - %s\n",
180                             domain, nt_errstr(status));
181                 TALLOC_FREE(frame);
182                 return KRB5_LIBOS_CANTREADPWD;
183         }
184         ct = &info->password->cleartext_blob;
185
186         if (info->domain_info.dns_domain.string != NULL) {
187                 realm = strupper_talloc(frame,
188                                 info->domain_info.dns_domain.string);
189                 if (realm == NULL) {
190                         TALLOC_FREE(frame);
191                         return ENOMEM;
192                 }
193         }
194
195         ZERO_STRUCT(kt_entry);
196         ZERO_STRUCT(kt_cursor);
197
198         /* check if the keytab already has any entry */
199         ret = krb5_kt_start_seq_get(krbctx, *keytab, &kt_cursor);
200         if (ret != KRB5_KT_END && ret != ENOENT ) {
201                 /* check if we have our special enctype used to hold
202                  * the clear text password. If so, check it out so that
203                  * we can verify if the keytab needs to be upgraded */
204                 while ((ret = krb5_kt_next_entry(krbctx, *keytab,
205                                            &kt_entry, &kt_cursor)) == 0) {
206                         if (smb_krb5_kt_get_enctype_from_entry(&kt_entry) ==
207                             CLEARTEXT_PRIV_ENCTYPE) {
208                                 break;
209                         }
210                         smb_krb5_kt_free_entry(krbctx, &kt_entry);
211                         ZERO_STRUCT(kt_entry);
212                 }
213
214                 if (ret != 0 && ret != KRB5_KT_END && ret != ENOENT ) {
215                         /* Error parsing keytab */
216                         DEBUG(1, (__location__ ": Failed to parse memory "
217                                   "keytab!\n"));
218                         goto out;
219                 }
220
221                 if (ret == 0) {
222                         /* found private entry,
223                          * check if keytab is up to date */
224
225                         if ((ct->length == KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry))) &&
226                             (memcmp(KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)),
227                                                 ct->data, ct->length) == 0)) {
228                                 /* keytab is already up to date, return */
229                                 smb_krb5_kt_free_entry(krbctx, &kt_entry);
230                                 goto out;
231                         }
232
233                         smb_krb5_kt_free_entry(krbctx, &kt_entry);
234                         ZERO_STRUCT(kt_entry);
235
236
237                         /* flush keytab, we need to regen it */
238                         ret = flush_keytab(krbctx, *keytab);
239                         if (ret) {
240                                 DEBUG(1, (__location__ ": Failed to flush "
241                                           "memory keytab!\n"));
242                                 goto out;
243                         }
244                 }
245         }
246
247         if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
248                 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
249         }
250
251         /* keytab is not up to date, fill it up */
252
253         ret = smb_krb5_make_principal(krbctx, &princ, realm,
254                                       info->account_name, NULL);
255         if (ret) {
256                 DEBUG(1, (__location__ ": Failed to get host principal!\n"));
257                 goto out;
258         }
259
260         ret = fill_keytab_from_password(krbctx, *keytab,
261                                         princ, kvno,
262                                         info->password);
263         if (ret) {
264                 DBG_WARNING("fill_keytab_from_password() failed for "
265                             "info->password.\n.");
266                 goto out;
267         }
268
269         if (info->old_password != NULL) {
270                 ret = fill_keytab_from_password(krbctx, *keytab,
271                                                 princ, kvno - 1,
272                                                 info->old_password);
273                 if (ret) {
274                         DBG_WARNING("fill_keytab_from_password() failed for "
275                                     "info->old_password.\n.");
276                         goto out;
277                 }
278         }
279
280         if (info->older_password != NULL) {
281                 ret = fill_keytab_from_password(krbctx, *keytab,
282                                                 princ, kvno - 2,
283                                                 info->older_password);
284                 if (ret) {
285                         DBG_WARNING("fill_keytab_from_password() failed for "
286                                     "info->older_password.\n.");
287                         goto out;
288                 }
289         }
290
291         if (info->next_change != NULL) {
292                 ret = fill_keytab_from_password(krbctx, *keytab,
293                                                 princ, kvno - 3,
294                                                 info->next_change->password);
295                 if (ret) {
296                         DBG_WARNING("fill_keytab_from_password() failed for "
297                                     "info->next_change->password.\n.");
298                         goto out;
299                 }
300         }
301
302         /* add our private enctype + cleartext password so that we can
303          * update the keytab if secrets change later on */
304         ZERO_STRUCT(kt_entry);
305         kt_entry.principal = princ;
306         kt_entry.vno = 0;
307
308         KRB5_KEY_TYPE(KRB5_KT_KEY(&kt_entry)) = CLEARTEXT_PRIV_ENCTYPE;
309         KRB5_KEY_LENGTH(KRB5_KT_KEY(&kt_entry)) = ct->length;
310         KRB5_KEY_DATA(KRB5_KT_KEY(&kt_entry)) = ct->data;
311
312         ret = krb5_kt_add_entry(krbctx, *keytab, &kt_entry);
313         if (ret) {
314                 DEBUG(1, (__location__ ": Failed to add entry to "
315                           "keytab for private enctype (%d) (error: %s)\n",
316                            CLEARTEXT_PRIV_ENCTYPE, error_message(ret)));
317                 goto out;
318         }
319
320         ret = 0;
321
322 out:
323         if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && *keytab) {
324                 krb5_kt_end_seq_get(krbctx, *keytab, &kt_cursor);
325         }
326
327         if (princ) {
328                 krb5_free_principal(krbctx, princ);
329         }
330
331         TALLOC_FREE(frame);
332         return ret;
333 }
334
335 static krb5_error_code fill_mem_keytab_from_system_keytab(krb5_context krbctx,
336                                                           krb5_keytab *mkeytab)
337 {
338         krb5_error_code ret = 0;
339         krb5_keytab keytab = NULL;
340         krb5_kt_cursor kt_cursor = { 0, };
341         krb5_keytab_entry kt_entry = { 0, };
342         char *valid_princ_formats[7] = { NULL, NULL, NULL,
343                                          NULL, NULL, NULL, NULL };
344         char *entry_princ_s = NULL;
345         fstring my_name, my_fqdn;
346         unsigned i;
347         int err;
348
349         /* Generate the list of principal names which we expect
350          * clients might want to use for authenticating to the file
351          * service.  We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */
352
353         fstrcpy(my_name, lp_netbios_name());
354
355         my_fqdn[0] = '\0';
356         name_to_fqdn(my_fqdn, lp_netbios_name());
357
358         err = asprintf(&valid_princ_formats[0],
359                         "%s$@%s", my_name, lp_realm());
360         if (err == -1) {
361                 ret = ENOMEM;
362                 goto out;
363         }
364         err = asprintf(&valid_princ_formats[1],
365                         "host/%s@%s", my_name, lp_realm());
366         if (err == -1) {
367                 ret = ENOMEM;
368                 goto out;
369         }
370         err = asprintf(&valid_princ_formats[2],
371                         "host/%s@%s", my_fqdn, lp_realm());
372         if (err == -1) {
373                 ret = ENOMEM;
374                 goto out;
375         }
376         err = asprintf(&valid_princ_formats[3],
377                         "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
378         if (err == -1) {
379                 ret = ENOMEM;
380                 goto out;
381         }
382         err = asprintf(&valid_princ_formats[4],
383                         "cifs/%s@%s", my_name, lp_realm());
384         if (err == -1) {
385                 ret = ENOMEM;
386                 goto out;
387         }
388         err = asprintf(&valid_princ_formats[5],
389                         "cifs/%s@%s", my_fqdn, lp_realm());
390         if (err == -1) {
391                 ret = ENOMEM;
392                 goto out;
393         }
394         err = asprintf(&valid_princ_formats[6],
395                         "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
396         if (err == -1) {
397                 ret = ENOMEM;
398                 goto out;
399         }
400
401         ret = smb_krb5_kt_open_relative(krbctx, NULL, false, &keytab);
402         if (ret) {
403                 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
404                           error_message(ret)));
405                 goto out;
406         }
407
408         /*
409          * Iterate through the keytab.  For each key, if the principal
410          * name case-insensitively matches one of the allowed formats,
411          * copy it to the memory keytab.
412          */
413
414         ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
415         if (ret) {
416                 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
417                           error_message(ret)));
418                 /*
419                  * krb5_kt_start_seq_get() may leaves bogus data
420                  * in kt_cursor. And we want to use the all_zero()
421                  * logic below.
422                  *
423                  * See bug #10490
424                  */
425                 ZERO_STRUCT(kt_cursor);
426                 goto out;
427         }
428
429         while ((krb5_kt_next_entry(krbctx, keytab,
430                                    &kt_entry, &kt_cursor) == 0)) {
431                 ret = smb_krb5_unparse_name(talloc_tos(), krbctx,
432                                             kt_entry.principal,
433                                             &entry_princ_s);
434                 if (ret) {
435                         DEBUG(1, (__location__ ": smb_krb5_unparse_name "
436                                   "failed (%s)\n", error_message(ret)));
437                         goto out;
438                 }
439
440                 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
441
442                         if (!strequal(entry_princ_s, valid_princ_formats[i])) {
443                                 continue;
444                         }
445
446                         ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
447                         if (ret) {
448                                 DEBUG(1, (__location__ ": smb_krb5_unparse_name "
449                                           "failed (%s)\n", error_message(ret)));
450                                 goto out;
451                         }
452                 }
453
454                 /* Free the name we parsed. */
455                 TALLOC_FREE(entry_princ_s);
456
457                 /* Free the entry we just read. */
458                 smb_krb5_kt_free_entry(krbctx, &kt_entry);
459                 ZERO_STRUCT(kt_entry);
460         }
461         krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
462
463         ZERO_STRUCT(kt_cursor);
464
465 out:
466
467         for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
468                 SAFE_FREE(valid_princ_formats[i]);
469         }
470
471         TALLOC_FREE(entry_princ_s);
472
473         if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
474                 smb_krb5_kt_free_entry(krbctx, &kt_entry);
475         }
476
477         if (!all_zero((uint8_t *)&kt_cursor, sizeof(kt_cursor)) && keytab) {
478                 krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
479         }
480
481         if (keytab) {
482                 krb5_kt_close(krbctx, keytab);
483         }
484
485         return ret;
486 }
487
488 static krb5_error_code fill_mem_keytab_from_dedicated_keytab(krb5_context krbctx,
489                                                              krb5_keytab *mkeytab)
490 {
491         krb5_error_code ret = 0;
492         krb5_keytab keytab = NULL;
493         krb5_kt_cursor kt_cursor;
494         krb5_keytab_entry kt_entry;
495
496         ret = smb_krb5_kt_open(krbctx, lp_dedicated_keytab_file(),
497                                    false, &keytab);
498         if (ret) {
499                 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
500                           error_message(ret)));
501                 return ret;
502         }
503
504         /*
505          * Copy the dedicated keyab to our in-memory keytab.
506          */
507
508         ret = krb5_kt_start_seq_get(krbctx, keytab, &kt_cursor);
509         if (ret) {
510                 DEBUG(1, (__location__ ": krb5_kt_start_seq_get failed (%s)\n",
511                           error_message(ret)));
512                 goto out;
513         }
514
515         while ((krb5_kt_next_entry(krbctx, keytab,
516                                    &kt_entry, &kt_cursor) == 0)) {
517
518                 ret = krb5_kt_add_entry(krbctx, *mkeytab, &kt_entry);
519
520                 /* Free the entry we just read. */
521                 smb_krb5_kt_free_entry(krbctx, &kt_entry);
522
523                 if (ret) {
524                         DEBUG(1, (__location__ ": smb_krb5_unparse_name "
525                                   "failed (%s)\n", error_message(ret)));
526                         break;
527                 }
528         }
529         krb5_kt_end_seq_get(krbctx, keytab, &kt_cursor);
530
531 out:
532         
533         krb5_kt_close(krbctx, keytab);
534
535         return ret;
536 }
537
538 krb5_error_code gse_krb5_get_server_keytab(krb5_context krbctx,
539                                            krb5_keytab *keytab)
540 {
541         krb5_error_code ret = 0;
542         krb5_error_code ret1 = 0;
543         krb5_error_code ret2 = 0;
544
545         *keytab = NULL;
546
547         /* create memory keytab */
548         ret = krb5_kt_resolve(krbctx, SRV_MEM_KEYTAB_NAME, keytab);
549         if (ret) {
550                 DEBUG(1, (__location__ ": Failed to get memory "
551                           "keytab!\n"));
552                 return ret;
553         }
554
555         switch (lp_kerberos_method()) {
556         default:
557         case KERBEROS_VERIFY_SECRETS:
558                 ret = fill_mem_keytab_from_secrets(krbctx, keytab);
559                 break;
560         case KERBEROS_VERIFY_SYSTEM_KEYTAB:
561                 ret = fill_mem_keytab_from_system_keytab(krbctx, keytab);
562                 break;
563         case KERBEROS_VERIFY_DEDICATED_KEYTAB:
564                 /* just use whatever keytab is configured */
565                 ret = fill_mem_keytab_from_dedicated_keytab(krbctx, keytab);
566                 break;
567         case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
568                 ret1 = fill_mem_keytab_from_secrets(krbctx, keytab);
569                 if (ret1) {
570                         DEBUG(3, (__location__ ": Warning! Unable to set mem "
571                                   "keytab from secrets!\n"));
572                 }
573                 /* Now append system keytab keys too */
574                 ret2 = fill_mem_keytab_from_system_keytab(krbctx, keytab);
575                 if (ret2) {
576                         DEBUG(3, (__location__ ": Warning! Unable to set mem "
577                                   "keytab from system keytab!\n"));
578                 }
579                 if (ret1 == 0 || ret2 == 0) {
580                         ret = 0;
581                 } else {
582                         ret = ret1;
583                 }
584                 break;
585         }
586
587         if (ret) {
588                 krb5_kt_close(krbctx, *keytab);
589                 *keytab = NULL;
590                 DEBUG(1,("%s: Error! Unable to set mem keytab - %d\n",
591                          __location__, ret));
592         }
593
594         return ret;
595 }
596
597 #endif /* HAVE_KRB5 */