s4-auth-krb: Remove dependency on credentials too.
[kai/samba-autobuild/.git] / source4 / auth / kerberos / srv_keytab.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Kerberos utility functions
5
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23
24 #include "includes.h"
25 #include "system/kerberos.h"
26 #include "auth/kerberos/kerberos.h"
27 #include "auth/kerberos/kerberos_srv_keytab.h"
28
29 static void keytab_principals_free(krb5_context context, krb5_principal *set)
30 {
31         int i;
32         for (i = 0; set[i] != NULL; i++) {
33                 krb5_free_principal(context, set[i]);
34         }
35 }
36
37 static krb5_error_code principals_from_list(TALLOC_CTX *parent_ctx,
38                                         const char *samAccountName,
39                                         const char *realm,
40                                         const char **SPNs, int num_SPNs,
41                                         krb5_context context,
42                                         krb5_principal **principals_out,
43                                         const char **error_string)
44 {
45         unsigned int i;
46         krb5_error_code ret;
47         char *upper_realm;
48         TALLOC_CTX *tmp_ctx;
49         krb5_principal *principals = NULL;
50         tmp_ctx = talloc_new(parent_ctx);
51         if (!tmp_ctx) {
52                 *error_string = "Cannot allocate tmp_ctx";
53                 return ENOMEM;
54         }
55
56         if (!realm) {
57                 *error_string = "Cannot have a kerberos secret in "
58                                 "secrets.ldb without a realm";
59                 ret = EINVAL;
60                 goto done;
61         }
62
63         upper_realm = strupper_talloc(tmp_ctx, realm);
64         if (!upper_realm) {
65                 *error_string = "Cannot allocate full upper case realm";
66                 ret = ENOMEM;
67                 goto done;
68         }
69
70         principals = talloc_zero_array(tmp_ctx, krb5_principal,
71                                         num_SPNs ? (num_SPNs + 2) : 2);
72
73         for (i = 0; num_SPNs && i < num_SPNs; i++) {
74                 ret = krb5_parse_name(context, SPNs[i], &principals[i]);
75
76                 if (ret) {
77                         *error_string = smb_get_krb5_error_message(context, ret,
78                                                                    parent_ctx);
79                         goto done;
80                 }
81         }
82
83         if (samAccountName) {
84                 ret = krb5_make_principal(context, &principals[i],
85                                           upper_realm, samAccountName,
86                                           NULL);
87                 if (ret) {
88                         *error_string = smb_get_krb5_error_message(context, ret,
89                                                                    parent_ctx);
90                         goto done;
91                 }
92         }
93
94 done:
95         if (ret) {
96                 keytab_principals_free(context, principals);
97         } else {
98                 *principals_out = talloc_steal(parent_ctx, principals);
99         }
100         talloc_free(tmp_ctx);
101         return ret;
102 }
103
104 static krb5_error_code salt_principal(TALLOC_CTX *parent_ctx,
105                                 const char *samAccountName,
106                                 const char *realm,
107                                 const char *saltPrincipal,
108                                 krb5_context context,
109                                 krb5_principal *salt_princ,
110                                 const char **error_string)
111 {
112
113         krb5_error_code ret;
114         char *machine_username;
115         char *salt_body;
116         char *lower_realm;
117         char *upper_realm;
118
119         TALLOC_CTX *tmp_ctx;
120
121         if (saltPrincipal) {
122                 ret = krb5_parse_name(context, saltPrincipal, salt_princ);
123                 if (ret) {
124                         *error_string = smb_get_krb5_error_message(
125                                                 context, ret, parent_ctx);
126                 }
127                 return ret;
128         }
129
130         if (!samAccountName) {
131                 (*error_string) = "Cannot determine salt principal, no "
132                                 "saltPrincipal or samAccountName specified";
133                 return EINVAL;
134         }
135
136         if (!realm) {
137                 *error_string = "Cannot have a kerberos secret in "
138                                 "secrets.ldb without a realm";
139                 return EINVAL;
140         }
141
142         tmp_ctx = talloc_new(parent_ctx);
143         if (!tmp_ctx) {
144                 *error_string = "Cannot allocate tmp_ctx";
145                 return ENOMEM;
146         }
147
148         machine_username = talloc_strdup(tmp_ctx, samAccountName);
149         if (!machine_username) {
150                 *error_string = "Cannot duplicate samAccountName";
151                 talloc_free(tmp_ctx);
152                 return ENOMEM;
153         }
154
155         if (machine_username[strlen(machine_username)-1] == '$') {
156                 machine_username[strlen(machine_username)-1] = '\0';
157         }
158
159         lower_realm = strlower_talloc(tmp_ctx, realm);
160         if (!lower_realm) {
161                 *error_string = "Cannot allocate to lower case realm";
162                 talloc_free(tmp_ctx);
163                 return ENOMEM;
164         }
165
166         upper_realm = strupper_talloc(tmp_ctx, realm);
167         if (!upper_realm) {
168                 *error_string = "Cannot allocate to upper case realm";
169                 talloc_free(tmp_ctx);
170                 return ENOMEM;
171         }
172
173         salt_body = talloc_asprintf(tmp_ctx, "%s.%s",
174                                     machine_username, lower_realm);
175         if (!salt_body) {
176                 *error_string = "Cannot form salt principal body";
177                 talloc_free(tmp_ctx);
178                 return ENOMEM;
179         }
180
181         ret = krb5_make_principal(context, salt_princ, upper_realm,
182                                                 "host", salt_body, NULL);
183         if (ret) {
184                 *error_string = smb_get_krb5_error_message(context,
185                                                            ret, parent_ctx);
186         }
187
188         talloc_free(tmp_ctx);
189         return ret;
190 }
191
192 /* Translate between the Microsoft msDS-SupportedEncryptionTypes values
193  * and the IETF encryption type values */
194 static krb5_enctype ms_suptype_to_ietf_enctype(uint32_t enctype_bitmap)
195 {
196         switch (enctype_bitmap) {
197         case ENC_CRC32:
198                 return ENCTYPE_DES_CBC_CRC;
199         case ENC_RSA_MD5:
200                 return ENCTYPE_DES_CBC_MD5;
201         case ENC_RC4_HMAC_MD5:
202                 return ENCTYPE_ARCFOUR_HMAC_MD5;
203         case ENC_HMAC_SHA1_96_AES128:
204                 return ENCTYPE_AES128_CTS_HMAC_SHA1_96;
205         case ENC_HMAC_SHA1_96_AES256:
206                 return ENCTYPE_AES256_CTS_HMAC_SHA1_96;
207         default:
208                 return 0;
209         }
210 }
211
212 /* Return an array of krb5_enctype values */
213 static krb5_error_code ms_suptypes_to_ietf_enctypes(TALLOC_CTX *mem_ctx,
214                                                 uint32_t enctype_bitmap,
215                                                 krb5_enctype **enctypes)
216 {
217         unsigned int i, j = 0;
218         *enctypes = talloc_zero_array(mem_ctx, krb5_enctype,
219                                         (8 * sizeof(enctype_bitmap)) + 1);
220         if (!*enctypes) {
221                 return ENOMEM;
222         }
223         for (i = 0; i < (8 * sizeof(enctype_bitmap)); i++) {
224                 uint32_t bit_value = (1 << i) & enctype_bitmap;
225                 if (bit_value & enctype_bitmap) {
226                         (*enctypes)[j] = ms_suptype_to_ietf_enctype(bit_value);
227                         if (!(*enctypes)[j]) {
228                                 continue;
229                         }
230                         j++;
231                 }
232         }
233         (*enctypes)[j] = 0;
234         return 0;
235 }
236
237 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
238                                        krb5_principal *principals,
239                                        krb5_principal salt_princ,
240                                        int kvno,
241                                        const char *password_s,
242                                        krb5_context context,
243                                        krb5_enctype *enctypes,
244                                        krb5_keytab keytab,
245                                        const char **error_string)
246 {
247         unsigned int i, p;
248         krb5_error_code ret;
249         krb5_data password;
250         char *unparsed;
251
252         password.data = discard_const_p(char *, password_s);
253         password.length = strlen(password_s);
254
255         for (i = 0; enctypes[i]; i++) {
256                 krb5_keytab_entry entry;
257
258                 ZERO_STRUCT(entry);
259
260                 ret = create_kerberos_key_from_string_direct(context,
261                                                 salt_princ, &password,
262                                                 &entry.keyblock, enctypes[i]);
263                 if (ret != 0) {
264                         return ret;
265                 }
266
267                 entry.vno = kvno;
268
269                 for (p = 0; principals[p]; p++) {
270                         unparsed = NULL;
271                         entry.principal = principals[p];
272                         ret = krb5_kt_add_entry(context, keytab, &entry);
273                         if (ret != 0) {
274                                 char *k5_error_string =
275                                         smb_get_krb5_error_message(context,
276                                                                    ret, NULL);
277                                 krb5_unparse_name(context,
278                                                 principals[p], &unparsed);
279                                 *error_string = talloc_asprintf(parent_ctx,
280                                         "Failed to add enctype %d entry for "
281                                         "%s(kvno %d) to keytab: %s\n",
282                                         (int)enctypes[i], unparsed,
283                                         kvno, k5_error_string);
284
285                                 free(unparsed);
286                                 talloc_free(k5_error_string);
287                                 krb5_free_keyblock_contents(context,
288                                                             &entry.keyblock);
289                                 return ret;
290                         }
291
292                         DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
293                                   kvno, (int)enctypes[i]));
294                 }
295                 krb5_free_keyblock_contents(context, &entry.keyblock);
296         }
297         return 0;
298 }
299
300 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
301                                      const char *samAccountName,
302                                      const char *realm,
303                                      const char *saltPrincipal,
304                                      int kvno,
305                                      const char *new_secret,
306                                      const char *old_secret,
307                                      uint32_t supp_enctypes,
308                                      krb5_principal *principals,
309                                      krb5_context context,
310                                      krb5_keytab keytab,
311                                      bool add_old,
312                                      const char **error_string)
313 {
314         krb5_error_code ret;
315         krb5_principal salt_princ = NULL;
316         krb5_enctype *enctypes;
317         TALLOC_CTX *mem_ctx;
318
319         if (!new_secret) {
320                 /* There is no password here, so nothing to do */
321                 return 0;
322         }
323
324         mem_ctx = talloc_new(parent_ctx);
325         if (!mem_ctx) {
326                 *error_string = "unable to allocate tmp_ctx for create_keytab";
327                 return ENOMEM;
328         }
329
330         /* The salt used to generate these entries may be different however,
331          * fetch that */
332         ret = salt_principal(mem_ctx, samAccountName, realm, saltPrincipal,
333                              context, &salt_princ, error_string);
334         if (ret) {
335                 talloc_free(mem_ctx);
336                 return ret;
337         }
338
339         ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
340         if (ret) {
341                 *error_string = talloc_asprintf(parent_ctx,
342                                         "create_keytab: generating list of "
343                                         "encryption types failed (%s)\n",
344                                         smb_get_krb5_error_message(context,
345                                                                 ret, mem_ctx));
346                 goto done;
347         }
348
349         ret = keytab_add_keys(mem_ctx, principals,
350                               salt_princ, kvno, new_secret,
351                               context, enctypes, keytab, error_string);
352         if (ret) {
353                 goto done;
354         }
355
356         if (old_secret && add_old && kvno != 0) {
357                 ret = keytab_add_keys(mem_ctx, principals,
358                                       salt_princ, kvno - 1, old_secret,
359                                       context, enctypes, keytab, error_string);
360         }
361
362 done:
363         krb5_free_principal(context, salt_princ);
364         talloc_free(mem_ctx);
365         return ret;
366 }
367
368 /*
369  * Walk the keytab, looking for entries of this principal name,
370  * with KVNO other than current kvno -1.
371  *
372  * These entries are now stale,
373  * we only keep the current and previous entries around.
374  *
375  * Inspired by the code in Samba3 for 'use kerberos keytab'.
376  */
377
378 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
379                                           int kvno,
380                                           krb5_principal *principals,
381                                           bool delete_all_kvno,
382                                           krb5_context context,
383                                           krb5_keytab keytab,
384                                           bool *found_previous,
385                                           const char **error_string)
386 {
387         krb5_error_code ret, ret2;
388         krb5_kt_cursor cursor;
389         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
390
391         if (!mem_ctx) {
392                 return ENOMEM;
393         }
394
395         *found_previous = false;
396
397         /* for each entry in the keytab */
398         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
399         switch (ret) {
400         case 0:
401                 break;
402         case HEIM_ERR_OPNOTSUPP:
403         case ENOENT:
404         case KRB5_KT_END:
405                 /* no point enumerating if there isn't anything here */
406                 talloc_free(mem_ctx);
407                 return 0;
408         default:
409                 *error_string = talloc_asprintf(parent_ctx,
410                         "failed to open keytab for read of old entries: %s\n",
411                         smb_get_krb5_error_message(context, ret, mem_ctx));
412                 talloc_free(mem_ctx);
413                 return ret;
414         }
415
416         while (!ret) {
417                 unsigned int i;
418                 bool matched = false;
419                 krb5_keytab_entry entry;
420                 ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
421                 if (ret) {
422                         break;
423                 }
424                 for (i = 0; principals[i]; i++) {
425                         /* if it matches our principal */
426                         if (krb5_kt_compare(context, &entry,
427                                             principals[i], 0, 0)) {
428                                 matched = true;
429                                 break;
430                         }
431                 }
432
433                 if (!matched) {
434                         /* Free the entry,
435                          * it wasn't the one we were looking for anyway */
436                         krb5_kt_free_entry(context, &entry);
437                         continue;
438                 }
439
440                 /* delete it, if it is not kvno -1 */
441                 if (entry.vno != (kvno - 1 )) {
442                         /* Release the enumeration.  We are going to
443                          * have to start this from the top again,
444                          * because deletes during enumeration may not
445                          * always be consistent.
446                          *
447                          * Also, the enumeration locks a FILE: keytab
448                          */
449
450                         krb5_kt_end_seq_get(context, keytab, &cursor);
451
452                         ret = krb5_kt_remove_entry(context, keytab, &entry);
453                         krb5_kt_free_entry(context, &entry);
454
455                         /* Deleted: Restart from the top */
456                         ret2 = krb5_kt_start_seq_get(context, keytab, &cursor);
457                         if (ret2) {
458                                 krb5_kt_free_entry(context, &entry);
459                                 DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
460                                           smb_get_krb5_error_message(context,
461                                                                 ret, mem_ctx)));
462
463                                 talloc_free(mem_ctx);
464                                 return ret2;
465                         }
466
467                         if (ret) {
468                                 break;
469                         }
470
471                 } else {
472                         *found_previous = true;
473                 }
474
475                 /* Free the entry, we don't need it any more */
476                 krb5_kt_free_entry(context, &entry);
477         }
478         krb5_kt_end_seq_get(context, keytab, &cursor);
479
480         switch (ret) {
481         case 0:
482                 break;
483         case ENOENT:
484         case KRB5_KT_END:
485                 ret = 0;
486                 break;
487         default:
488                 *error_string = talloc_asprintf(parent_ctx,
489                         "failed in deleting old entries for principal: %s\n",
490                         smb_get_krb5_error_message(context, ret, mem_ctx));
491         }
492         talloc_free(mem_ctx);
493         return ret;
494 }
495
496 krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
497                                 struct smb_krb5_context *smb_krb5_context,
498                                 const char *keytab_name,
499                                 const char *samAccountName,
500                                 const char *realm,
501                                 const char **SPNs,
502                                 int num_SPNs,
503                                 const char *saltPrincipal,
504                                 const char *new_secret,
505                                 const char *old_secret,
506                                 int kvno,
507                                 uint32_t supp_enctypes,
508                                 bool delete_all_kvno,
509                                 krb5_keytab *_keytab,
510                                 const char **error_string)
511 {
512         krb5_keytab keytab;
513         krb5_error_code ret;
514         bool found_previous;
515         TALLOC_CTX *tmp_ctx;
516         krb5_principal *principals = NULL;
517
518         if (keytab_name == NULL) {
519                 return ENOENT;
520         }
521
522         ret = krb5_kt_resolve(smb_krb5_context->krb5_context,
523                                         keytab_name, &keytab);
524         if (ret) {
525                 *error_string = smb_get_krb5_error_message(
526                                         smb_krb5_context->krb5_context,
527                                         ret, parent_ctx);
528                 return ret;
529         }
530
531         DEBUG(5, ("Opened keytab %s\n", keytab_name));
532
533         tmp_ctx = talloc_new(parent_ctx);
534         if (!tmp_ctx) {
535                 return ENOMEM;
536         }
537
538         /* Get the principal we will store the new keytab entries under */
539         ret = principals_from_list(tmp_ctx,
540                                   samAccountName, realm, SPNs, num_SPNs,
541                                   smb_krb5_context->krb5_context,
542                                   &principals, error_string);
543
544         if (ret != 0) {
545                 *error_string = talloc_asprintf(parent_ctx,
546                         "Failed to load principals from ldb message: %s\n",
547                         *error_string);
548                 goto done;
549         }
550
551         ret = remove_old_entries(tmp_ctx, kvno, principals, delete_all_kvno,
552                                  smb_krb5_context->krb5_context,
553                                  keytab, &found_previous, error_string);
554         if (ret != 0) {
555                 *error_string = talloc_asprintf(parent_ctx,
556                         "Failed to remove old principals from keytab: %s\n",
557                         *error_string);
558                 goto done;
559         }
560
561         if (!delete_all_kvno) {
562                 /* Create a new keytab.  If during the cleanout we found
563                  * entires for kvno -1, then don't try and duplicate them.
564                  * Otherwise, add kvno, and kvno -1 */
565
566                 ret = create_keytab(tmp_ctx,
567                                     samAccountName, realm, saltPrincipal,
568                                     kvno, new_secret, old_secret,
569                                     supp_enctypes, principals,
570                                     smb_krb5_context->krb5_context,
571                                     keytab,
572                                     found_previous ? false : true,
573                                     error_string);
574                 if (ret) {
575                         talloc_steal(parent_ctx, *error_string);
576                 }
577         }
578
579         if (ret == 0 && _keytab != NULL) {
580                 /* caller wants the keytab handle back */
581                 *_keytab = keytab;
582         }
583
584 done:
585         keytab_principals_free(smb_krb5_context->krb5_context, principals);
586         if (ret != 0 || _keytab == NULL) {
587                 krb5_kt_close(smb_krb5_context->krb5_context, keytab);
588         }
589         talloc_free(tmp_ctx);
590         return ret;
591 }
592
593 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
594                                 struct smb_krb5_context *smb_krb5_context,
595                                 const char *new_secret,
596                                 const char *samAccountName,
597                                 const char *realm,
598                                 int kvno,
599                                 krb5_keytab *keytab,
600                                 const char **keytab_name)
601 {
602         krb5_error_code ret;
603         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
604         const char *rand_string;
605         const char *error_string;
606         if (!mem_ctx) {
607                 return ENOMEM;
608         }
609
610         rand_string = generate_random_str(mem_ctx, 16);
611         if (!rand_string) {
612                 talloc_free(mem_ctx);
613                 return ENOMEM;
614         }
615
616         *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
617         if (*keytab_name == NULL) {
618                 talloc_free(mem_ctx);
619                 return ENOMEM;
620         }
621
622
623         ret = smb_krb5_update_keytab(mem_ctx, smb_krb5_context,
624                                      *keytab_name, samAccountName, realm,
625                                      NULL, 0, NULL, new_secret, NULL,
626                                      kvno, ENC_ALL_TYPES,
627                                      false, keytab, &error_string);
628         if (ret == 0) {
629                 talloc_steal(parent_ctx, *keytab_name);
630         } else {
631                 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
632                           error_string));
633                 *keytab_name = NULL;
634         }
635         talloc_free(mem_ctx);
636         return ret;
637 }