475f09d9342807de2300204ae706721efe381f2c
[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 "auth/credentials/credentials.h"
26 #include "system/kerberos.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "auth/kerberos/kerberos_srv_keytab.h"
29
30 static void keytab_principals_free(krb5_context context, krb5_principal *set)
31 {
32         int i;
33         for (i = 0; set[i] != NULL; i++) {
34                 krb5_free_principal(context, set[i]);
35         }
36 }
37
38 static krb5_error_code principals_from_list(TALLOC_CTX *parent_ctx,
39                                         const char *samAccountName,
40                                         const char *realm,
41                                         const char **SPNs, int num_SPNs,
42                                         krb5_context context,
43                                         krb5_principal **principals_out,
44                                         const char **error_string)
45 {
46         unsigned int i;
47         krb5_error_code ret;
48         char *upper_realm;
49         TALLOC_CTX *tmp_ctx;
50         krb5_principal *principals = NULL;
51         tmp_ctx = talloc_new(parent_ctx);
52         if (!tmp_ctx) {
53                 *error_string = "Cannot allocate tmp_ctx";
54                 return ENOMEM;
55         }
56
57         if (!realm) {
58                 *error_string = "Cannot have a kerberos secret in "
59                                 "secrets.ldb without a realm";
60                 ret = EINVAL;
61                 goto done;
62         }
63
64         upper_realm = strupper_talloc(tmp_ctx, realm);
65         if (!upper_realm) {
66                 *error_string = "Cannot allocate full upper case realm";
67                 ret = ENOMEM;
68                 goto done;
69         }
70
71         principals = talloc_zero_array(tmp_ctx, krb5_principal,
72                                         num_SPNs ? (num_SPNs + 2) : 2);
73
74         for (i = 0; num_SPNs && i < num_SPNs; i++) {
75                 ret = krb5_parse_name(context, SPNs[i], &principals[i]);
76
77                 if (ret) {
78                         *error_string = smb_get_krb5_error_message(context, ret,
79                                                                    parent_ctx);
80                         goto done;
81                 }
82         }
83
84         if (samAccountName) {
85                 ret = krb5_make_principal(context, &principals[i],
86                                           upper_realm, samAccountName,
87                                           NULL);
88                 if (ret) {
89                         *error_string = smb_get_krb5_error_message(context, ret,
90                                                                    parent_ctx);
91                         goto done;
92                 }
93         }
94
95 done:
96         if (ret) {
97                 keytab_principals_free(context, principals);
98         } else {
99                 *principals_out = talloc_steal(parent_ctx, principals);
100         }
101         talloc_free(tmp_ctx);
102         return ret;
103 }
104
105 static krb5_error_code salt_principal(TALLOC_CTX *parent_ctx,
106                                 const char *samAccountName,
107                                 const char *realm,
108                                 const char *saltPrincipal,
109                                 krb5_context context,
110                                 krb5_principal *salt_princ,
111                                 const char **error_string)
112 {
113
114         krb5_error_code ret;
115         char *machine_username;
116         char *salt_body;
117         char *lower_realm;
118         char *upper_realm;
119
120         TALLOC_CTX *tmp_ctx;
121
122         if (saltPrincipal) {
123                 ret = krb5_parse_name(context, saltPrincipal, salt_princ);
124                 if (ret) {
125                         *error_string = smb_get_krb5_error_message(
126                                                 context, ret, parent_ctx);
127                 }
128                 return ret;
129         }
130
131         if (!samAccountName) {
132                 (*error_string) = "Cannot determine salt principal, no "
133                                 "saltPrincipal or samAccountName specified";
134                 return EINVAL;
135         }
136
137         if (!realm) {
138                 *error_string = "Cannot have a kerberos secret in "
139                                 "secrets.ldb without a realm";
140                 return EINVAL;
141         }
142
143         tmp_ctx = talloc_new(parent_ctx);
144         if (!tmp_ctx) {
145                 *error_string = "Cannot allocate tmp_ctx";
146                 return ENOMEM;
147         }
148
149         machine_username = talloc_strdup(tmp_ctx, samAccountName);
150         if (!machine_username) {
151                 *error_string = "Cannot duplicate samAccountName";
152                 talloc_free(tmp_ctx);
153                 return ENOMEM;
154         }
155
156         if (machine_username[strlen(machine_username)-1] == '$') {
157                 machine_username[strlen(machine_username)-1] = '\0';
158         }
159
160         lower_realm = strlower_talloc(tmp_ctx, realm);
161         if (!lower_realm) {
162                 *error_string = "Cannot allocate to lower case realm";
163                 talloc_free(tmp_ctx);
164                 return ENOMEM;
165         }
166
167         upper_realm = strupper_talloc(tmp_ctx, realm);
168         if (!upper_realm) {
169                 *error_string = "Cannot allocate to upper case realm";
170                 talloc_free(tmp_ctx);
171                 return ENOMEM;
172         }
173
174         salt_body = talloc_asprintf(tmp_ctx, "%s.%s",
175                                     machine_username, lower_realm);
176         if (!salt_body) {
177                 *error_string = "Cannot form salt principal body";
178                 talloc_free(tmp_ctx);
179                 return ENOMEM;
180         }
181
182         ret = krb5_make_principal(context, salt_princ, upper_realm,
183                                                 "host", salt_body, NULL);
184         if (ret) {
185                 *error_string = smb_get_krb5_error_message(context,
186                                                            ret, parent_ctx);
187         }
188
189         talloc_free(tmp_ctx);
190         return ret;
191 }
192
193 /* Translate between the Microsoft msDS-SupportedEncryptionTypes values
194  * and the IETF encryption type values */
195 static krb5_enctype ms_suptype_to_ietf_enctype(uint32_t enctype_bitmap)
196 {
197         switch (enctype_bitmap) {
198         case ENC_CRC32:
199                 return ENCTYPE_DES_CBC_CRC;
200         case ENC_RSA_MD5:
201                 return ENCTYPE_DES_CBC_MD5;
202         case ENC_RC4_HMAC_MD5:
203                 return ENCTYPE_ARCFOUR_HMAC_MD5;
204         case ENC_HMAC_SHA1_96_AES128:
205                 return ENCTYPE_AES128_CTS_HMAC_SHA1_96;
206         case ENC_HMAC_SHA1_96_AES256:
207                 return ENCTYPE_AES256_CTS_HMAC_SHA1_96;
208         default:
209                 return 0;
210         }
211 }
212
213 /* Return an array of krb5_enctype values */
214 static krb5_error_code ms_suptypes_to_ietf_enctypes(TALLOC_CTX *mem_ctx,
215                                                 uint32_t enctype_bitmap,
216                                                 krb5_enctype **enctypes)
217 {
218         unsigned int i, j = 0;
219         *enctypes = talloc_zero_array(mem_ctx, krb5_enctype,
220                                         (8 * sizeof(enctype_bitmap)) + 1);
221         if (!*enctypes) {
222                 return ENOMEM;
223         }
224         for (i = 0; i < (8 * sizeof(enctype_bitmap)); i++) {
225                 uint32_t bit_value = (1 << i) & enctype_bitmap;
226                 if (bit_value & enctype_bitmap) {
227                         (*enctypes)[j] = ms_suptype_to_ietf_enctype(bit_value);
228                         if (!(*enctypes)[j]) {
229                                 continue;
230                         }
231                         j++;
232                 }
233         }
234         (*enctypes)[j] = 0;
235         return 0;
236 }
237
238 static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
239                                        krb5_principal *principals,
240                                        krb5_principal salt_princ,
241                                        int kvno,
242                                        const char *password_s,
243                                        krb5_context context,
244                                        krb5_enctype *enctypes,
245                                        krb5_keytab keytab,
246                                        const char **error_string)
247 {
248         unsigned int i, p;
249         krb5_error_code ret;
250         krb5_data password;
251         char *unparsed;
252
253         password.data = discard_const_p(char *, password_s);
254         password.length = strlen(password_s);
255
256         for (i = 0; enctypes[i]; i++) {
257                 krb5_keytab_entry entry;
258
259                 ZERO_STRUCT(entry);
260
261                 ret = create_kerberos_key_from_string_direct(context,
262                                                 salt_princ, &password,
263                                                 &entry.keyblock, enctypes[i]);
264                 if (ret != 0) {
265                         return ret;
266                 }
267
268                 entry.vno = kvno;
269
270                 for (p = 0; principals[p]; p++) {
271                         unparsed = NULL;
272                         entry.principal = principals[p];
273                         ret = krb5_kt_add_entry(context, keytab, &entry);
274                         if (ret != 0) {
275                                 char *k5_error_string =
276                                         smb_get_krb5_error_message(context,
277                                                                    ret, NULL);
278                                 krb5_unparse_name(context,
279                                                 principals[p], &unparsed);
280                                 *error_string = talloc_asprintf(parent_ctx,
281                                         "Failed to add enctype %d entry for "
282                                         "%s(kvno %d) to keytab: %s\n",
283                                         (int)enctypes[i], unparsed,
284                                         kvno, k5_error_string);
285
286                                 free(unparsed);
287                                 talloc_free(k5_error_string);
288                                 krb5_free_keyblock_contents(context,
289                                                             &entry.keyblock);
290                                 return ret;
291                         }
292
293                         DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
294                                   kvno, (int)enctypes[i]));
295                 }
296                 krb5_free_keyblock_contents(context, &entry.keyblock);
297         }
298         return 0;
299 }
300
301 static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
302                                      const char *samAccountName,
303                                      const char *realm,
304                                      const char *saltPrincipal,
305                                      int kvno,
306                                      const char *new_secret,
307                                      const char *old_secret,
308                                      uint32_t supp_enctypes,
309                                      krb5_principal *principals,
310                                      krb5_context context,
311                                      krb5_keytab keytab,
312                                      bool add_old,
313                                      const char **error_string)
314 {
315         krb5_error_code ret;
316         krb5_principal salt_princ = NULL;
317         krb5_enctype *enctypes;
318         TALLOC_CTX *mem_ctx;
319
320         if (!new_secret) {
321                 /* There is no password here, so nothing to do */
322                 return 0;
323         }
324
325         mem_ctx = talloc_new(parent_ctx);
326         if (!mem_ctx) {
327                 *error_string = "unable to allocate tmp_ctx for create_keytab";
328                 return ENOMEM;
329         }
330
331         /* The salt used to generate these entries may be different however,
332          * fetch that */
333         ret = salt_principal(mem_ctx, samAccountName, realm, saltPrincipal,
334                              context, &salt_princ, error_string);
335         if (ret) {
336                 talloc_free(mem_ctx);
337                 return ret;
338         }
339
340         ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
341         if (ret) {
342                 *error_string = talloc_asprintf(parent_ctx,
343                                         "create_keytab: generating list of "
344                                         "encryption types failed (%s)\n",
345                                         smb_get_krb5_error_message(context,
346                                                                 ret, mem_ctx));
347                 goto done;
348         }
349
350         ret = keytab_add_keys(mem_ctx, principals,
351                               salt_princ, kvno, new_secret,
352                               context, enctypes, keytab, error_string);
353         if (ret) {
354                 goto done;
355         }
356
357         if (old_secret && add_old && kvno != 0) {
358                 ret = keytab_add_keys(mem_ctx, principals,
359                                       salt_princ, kvno - 1, old_secret,
360                                       context, enctypes, keytab, error_string);
361         }
362
363 done:
364         krb5_free_principal(context, salt_princ);
365         talloc_free(mem_ctx);
366         return ret;
367 }
368
369 /*
370  * Walk the keytab, looking for entries of this principal name,
371  * with KVNO other than current kvno -1.
372  *
373  * These entries are now stale,
374  * we only keep the current and previous entries around.
375  *
376  * Inspired by the code in Samba3 for 'use kerberos keytab'.
377  */
378
379 static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
380                                           int kvno,
381                                           krb5_principal *principals,
382                                           bool delete_all_kvno,
383                                           krb5_context context,
384                                           krb5_keytab keytab,
385                                           bool *found_previous,
386                                           const char **error_string)
387 {
388         krb5_error_code ret, ret2;
389         krb5_kt_cursor cursor;
390         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
391
392         if (!mem_ctx) {
393                 return ENOMEM;
394         }
395
396         *found_previous = false;
397
398         /* for each entry in the keytab */
399         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
400         switch (ret) {
401         case 0:
402                 break;
403         case HEIM_ERR_OPNOTSUPP:
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 (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                                 struct smb_krb5_context *smb_krb5_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(smb_krb5_context->krb5_context,
524                                         keytab_name, &keytab);
525         if (ret) {
526                 *error_string = smb_get_krb5_error_message(
527                                         smb_krb5_context->krb5_context,
528                                         ret, parent_ctx);
529                 return ret;
530         }
531
532         DEBUG(5, ("Opened keytab %s\n", keytab_name));
533
534         tmp_ctx = talloc_new(parent_ctx);
535         if (!tmp_ctx) {
536                 return ENOMEM;
537         }
538
539         /* Get the principal we will store the new keytab entries under */
540         ret = principals_from_list(tmp_ctx,
541                                   samAccountName, realm, SPNs, num_SPNs,
542                                   smb_krb5_context->krb5_context,
543                                   &principals, error_string);
544
545         if (ret != 0) {
546                 *error_string = talloc_asprintf(parent_ctx,
547                         "Failed to load principals from ldb message: %s\n",
548                         *error_string);
549                 goto done;
550         }
551
552         ret = remove_old_entries(tmp_ctx, kvno, principals, delete_all_kvno,
553                                  smb_krb5_context->krb5_context,
554                                  keytab, &found_previous, error_string);
555         if (ret != 0) {
556                 *error_string = talloc_asprintf(parent_ctx,
557                         "Failed to remove old principals from keytab: %s\n",
558                         *error_string);
559                 goto done;
560         }
561
562         if (!delete_all_kvno) {
563                 /* Create a new keytab.  If during the cleanout we found
564                  * entires for kvno -1, then don't try and duplicate them.
565                  * Otherwise, add kvno, and kvno -1 */
566
567                 ret = create_keytab(tmp_ctx,
568                                     samAccountName, realm, saltPrincipal,
569                                     kvno, new_secret, old_secret,
570                                     supp_enctypes, principals,
571                                     smb_krb5_context->krb5_context,
572                                     keytab,
573                                     found_previous ? false : true,
574                                     error_string);
575                 if (ret) {
576                         talloc_steal(parent_ctx, *error_string);
577                 }
578         }
579
580         if (ret == 0 && _keytab != NULL) {
581                 /* caller wants the keytab handle back */
582                 *_keytab = keytab;
583         }
584
585 done:
586         keytab_principals_free(smb_krb5_context->krb5_context, principals);
587         if (ret != 0 || _keytab == NULL) {
588                 krb5_kt_close(smb_krb5_context->krb5_context, keytab);
589         }
590         talloc_free(tmp_ctx);
591         return ret;
592 }
593
594 krb5_error_code smb_krb5_create_memory_keytab(TALLOC_CTX *parent_ctx,
595                                 struct cli_credentials *machine_account,
596                                 struct smb_krb5_context *smb_krb5_context,
597                                 krb5_keytab *keytab,
598                                 const char **keytab_name)
599 {
600         krb5_error_code ret;
601         TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
602         const char *rand_string;
603         const char *new_secret;
604         const char *samAccountName;
605         const char *realm;
606         int kvno;
607         const char *error_string;
608         if (!mem_ctx) {
609                 return ENOMEM;
610         }
611
612         rand_string = generate_random_str(mem_ctx, 16);
613         if (!rand_string) {
614                 talloc_free(mem_ctx);
615                 return ENOMEM;
616         }
617
618         *keytab_name = talloc_asprintf(mem_ctx, "MEMORY:%s", rand_string);
619         if (*keytab_name == NULL) {
620                 talloc_free(mem_ctx);
621                 return ENOMEM;
622         }
623
624         new_secret = cli_credentials_get_password(machine_account);
625         samAccountName = cli_credentials_get_username(machine_account);
626         realm = cli_credentials_get_realm(machine_account);
627         kvno = cli_credentials_get_kvno(machine_account);
628
629         ret = smb_krb5_update_keytab(mem_ctx, smb_krb5_context,
630                                      *keytab_name, samAccountName, realm,
631                                      NULL, 0, NULL, new_secret, NULL,
632                                      kvno, ENC_ALL_TYPES,
633                                      false, keytab, &error_string);
634         if (ret == 0) {
635                 talloc_steal(parent_ctx, *keytab_name);
636         } else {
637                 DEBUG(0, ("Failed to create in-memory keytab: %s\n",
638                           error_string));
639                 *keytab_name = NULL;
640         }
641         talloc_free(mem_ctx);
642         return ret;
643 }