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