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