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