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