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