s3:libads: don't pass given_principal to ads_generate_service_principal() anymore.
[sfrench/samba-autobuild/.git] / source3 / libads / kerberos_keytab.c
1 /*
2    Unix SMB/CIFS implementation.
3    kerberos keytab utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Luke Howard 2003
7    Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
8    Copyright (C) Guenther Deschner 2003
9    Copyright (C) Rakesh Patel 2004
10    Copyright (C) Dan Perry 2004
11    Copyright (C) Jeremy Allison 2004
12    Copyright (C) Gerald Carter 2006
13
14    This program is free software; you can redistribute it and/or modify
15    it under the terms of the GNU General Public License as published by
16    the Free Software Foundation; either version 3 of the License, or
17    (at your option) any later version.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License for more details.
23
24    You should have received a copy of the GNU General Public License
25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
26 */
27
28 #include "includes.h"
29 #include "smb_krb5.h"
30 #include "ads.h"
31 #include "secrets.h"
32
33 #ifdef HAVE_KRB5
34
35 #ifdef HAVE_ADS
36
37 /**********************************************************************
38  Adds a single service principal, i.e. 'host' to the system keytab
39 ***********************************************************************/
40
41 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
42 {
43         krb5_error_code ret = 0;
44         krb5_context context = NULL;
45         krb5_keytab keytab = NULL;
46         krb5_data password;
47         krb5_kvno kvno;
48         krb5_enctype enctypes[6] = {
49                 ENCTYPE_DES_CBC_CRC,
50                 ENCTYPE_DES_CBC_MD5,
51 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
52                 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
53 #endif
54 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
55                 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
56 #endif
57                 ENCTYPE_ARCFOUR_HMAC,
58                 0
59         };
60         char *princ_s = NULL;
61         char *short_princ_s = NULL;
62         char *salt_princ_s = NULL;
63         char *password_s = NULL;
64         char *my_fqdn;
65         TALLOC_CTX *tmpctx = NULL;
66         char *machine_name;
67         ADS_STATUS aderr;
68         int i;
69
70         initialize_krb5_error_table();
71         ret = krb5_init_context(&context);
72         if (ret) {
73                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
74                           error_message(ret)));
75                 return -1;
76         }
77
78         ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
79         if (ret) {
80                 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
81                           error_message(ret)));
82                 goto out;
83         }
84
85         /* retrieve the password */
86         if (!secrets_init()) {
87                 DEBUG(1, (__location__ ": secrets_init failed\n"));
88                 ret = -1;
89                 goto out;
90         }
91         password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
92         if (!password_s) {
93                 DEBUG(1, (__location__ ": failed to fetch machine password\n"));
94                 ret = -1;
95                 goto out;
96         }
97         ZERO_STRUCT(password);
98         password.data = password_s;
99         password.length = strlen(password_s);
100
101         /* we need the dNSHostName value here */
102         tmpctx = talloc_init(__location__);
103         if (!tmpctx) {
104                 DEBUG(0, (__location__ ": talloc_init() failed!\n"));
105                 ret = -1;
106                 goto out;
107         }
108
109         my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
110         if (!my_fqdn) {
111                 DEBUG(0, (__location__ ": unable to determine machine "
112                           "account's dns name in AD!\n"));
113                 ret = -1;
114                 goto out;
115         }
116
117         machine_name = ads_get_samaccountname(ads, tmpctx, lp_netbios_name());
118         if (!machine_name) {
119                 DEBUG(0, (__location__ ": unable to determine machine "
120                           "account's short name in AD!\n"));
121                 ret = -1;
122                 goto out;
123         }
124         /*strip the trailing '$' */
125         machine_name[strlen(machine_name)-1] = '\0';
126
127         /* Construct our principal */
128         if (strchr_m(srvPrinc, '@')) {
129                 /* It's a fully-named principal. */
130                 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
131                 if (!princ_s) {
132                         ret = -1;
133                         goto out;
134                 }
135         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
136                 /* It's the machine account, as used by smbclient clients. */
137                 princ_s = talloc_asprintf(tmpctx, "%s@%s",
138                                           srvPrinc, lp_realm());
139                 if (!princ_s) {
140                         ret = -1;
141                         goto out;
142                 }
143         } else {
144                 /* It's a normal service principal.  Add the SPN now so that we
145                  * can obtain credentials for it and double-check the salt value
146                  * used to generate the service's keys. */
147
148                 princ_s = talloc_asprintf(tmpctx, "%s/%s@%s",
149                                           srvPrinc, my_fqdn, lp_realm());
150                 if (!princ_s) {
151                         ret = -1;
152                         goto out;
153                 }
154                 short_princ_s = talloc_asprintf(tmpctx, "%s/%s@%s",
155                                                 srvPrinc, machine_name,
156                                                 lp_realm());
157                 if (short_princ_s == NULL) {
158                         ret = -1;
159                         goto out;
160                 }
161
162                 /* According to http://support.microsoft.com/kb/326985/en-us,
163                    certain principal names are automatically mapped to the
164                    host/... principal in the AD account.
165                    So only create these in the keytab, not in AD.  --jerry */
166
167                 if (!strequal(srvPrinc, "cifs") &&
168                     !strequal(srvPrinc, "host")) {
169                         DEBUG(3, (__location__ ": Attempting to add/update "
170                                   "'%s'\n", princ_s));
171
172                         aderr = ads_add_service_principal_name(ads,
173                                         lp_netbios_name(), my_fqdn, srvPrinc);
174                         if (!ADS_ERR_OK(aderr)) {
175                                 DEBUG(1, (__location__ ": failed to "
176                                          "ads_add_service_principal_name.\n"));
177                                 goto out;
178                         }
179                 }
180         }
181
182         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
183         if (kvno == -1) {
184                 /* -1 indicates failure, everything else is OK */
185                 DEBUG(1, (__location__ ": ads_get_machine_kvno failed to "
186                          "determine the system's kvno.\n"));
187                 ret = -1;
188                 goto out;
189         }
190
191         for (i = 0; enctypes[i]; i++) {
192                 salt_princ_s = kerberos_fetch_salt_princ_for_host_princ(context,
193                                                                         princ_s,
194                                                                         enctypes[i]);
195
196                 /* add the fqdn principal to the keytab */
197                 ret = smb_krb5_kt_add_entry(context,
198                                             keytab,
199                                             kvno,
200                                             princ_s,
201                                             salt_princ_s,
202                                             enctypes[i],
203                                             &password,
204                                             false,
205                                             false);
206                 if (ret) {
207                         DEBUG(1, (__location__ ": Failed to add entry to keytab\n"));
208                         SAFE_FREE(salt_princ_s);
209                         goto out;
210                 }
211
212                 /* add the short principal name if we have one */
213                 if (short_princ_s) {
214                         ret = smb_krb5_kt_add_entry(context,
215                                                     keytab,
216                                                     kvno,
217                                                     short_princ_s,
218                                                     salt_princ_s,
219                                                     enctypes[i],
220                                                     &password,
221                                                     false,
222                                                     false);
223                         if (ret) {
224                                 DEBUG(1, (__location__
225                                           ": Failed to add short entry to keytab\n"));
226                                 SAFE_FREE(salt_princ_s);
227                                 goto out;
228                         }
229                 }
230                 SAFE_FREE(salt_princ_s);
231         }
232
233 out:
234         TALLOC_FREE(tmpctx);
235
236         if (keytab) {
237                 krb5_kt_close(context, keytab);
238         }
239         if (context) {
240                 krb5_free_context(context);
241         }
242         return (int)ret;
243 }
244
245 /**********************************************************************
246  Flushes all entries from the system keytab.
247 ***********************************************************************/
248
249 int ads_keytab_flush(ADS_STRUCT *ads)
250 {
251         krb5_error_code ret = 0;
252         krb5_context context = NULL;
253         krb5_keytab keytab = NULL;
254         krb5_kvno kvno;
255         ADS_STATUS aderr;
256
257         initialize_krb5_error_table();
258         ret = krb5_init_context(&context);
259         if (ret) {
260                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
261                           error_message(ret)));
262                 return ret;
263         }
264
265         ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
266         if (ret) {
267                 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
268                           error_message(ret)));
269                 goto out;
270         }
271
272         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
273         if (kvno == -1) {
274                 /* -1 indicates a failure */
275                 DEBUG(1, (__location__ ": Error determining the kvno.\n"));
276                 goto out;
277         }
278
279         /* Seek and delete old keytab entries */
280         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
281                                                       keytab,
282                                                       kvno,
283                                                       NULL,
284                                                       NULL,
285                                                       true,
286                                                       false);
287         if (ret) {
288                 goto out;
289         }
290
291         aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
292         if (!ADS_ERR_OK(aderr)) {
293                 DEBUG(1, (__location__ ": Error while clearing service "
294                           "principal listings in LDAP.\n"));
295                 goto out;
296         }
297
298 out:
299         if (keytab) {
300                 krb5_kt_close(context, keytab);
301         }
302         if (context) {
303                 krb5_free_context(context);
304         }
305         return ret;
306 }
307
308 /**********************************************************************
309  Adds all the required service principals to the system keytab.
310 ***********************************************************************/
311
312 int ads_keytab_create_default(ADS_STRUCT *ads)
313 {
314         krb5_error_code ret = 0;
315         krb5_context context = NULL;
316         krb5_keytab keytab = NULL;
317         krb5_kt_cursor cursor = {0};
318         krb5_keytab_entry kt_entry = {0};
319         krb5_kvno kvno;
320         size_t found = 0;
321         char *sam_account_name, *upn;
322         char **oldEntries = NULL, *princ_s[26];
323         TALLOC_CTX *frame;
324         char *machine_name;
325         char **spn_array;
326         size_t num_spns;
327         size_t i;
328         ADS_STATUS status;
329
330         ZERO_STRUCT(kt_entry);
331         ZERO_STRUCT(cursor);
332
333         frame = talloc_stackframe();
334         if (frame == NULL) {
335                 ret = -1;
336                 goto done;
337         }
338
339         status = ads_get_service_principal_names(frame,
340                                                  ads,
341                                                  lp_netbios_name(),
342                                                  &spn_array,
343                                                  &num_spns);
344         if (!ADS_ERR_OK(status)) {
345                 ret = -1;
346                 goto done;
347         }
348
349         for (i = 0; i < num_spns; i++) {
350                 char *srv_princ;
351                 char *p;
352
353                 srv_princ = strlower_talloc(frame, spn_array[i]);
354                 if (srv_princ == NULL) {
355                         ret = -1;
356                         goto done;
357                 }
358
359                 p = strchr_m(srv_princ, '/');
360                 if (p == NULL) {
361                         continue;
362                 }
363                 p[0] = '\0';
364
365                 /* Add the SPNs found on the DC */
366                 ret = ads_keytab_add_entry(ads, srv_princ);
367                 if (ret != 0) {
368                         DEBUG(1, ("ads_keytab_add_entry failed while "
369                                   "adding '%s' principal.\n",
370                                   spn_array[i]));
371                         goto done;
372                 }
373         }
374
375 #if 0   /* don't create the CIFS/... keytab entries since no one except smbd
376            really needs them and we will fall back to verifying against
377            secrets.tdb */
378
379         ret = ads_keytab_add_entry(ads, "cifs"));
380         if (ret != 0 ) {
381                 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
382                           "adding 'cifs'.\n"));
383                 return ret;
384         }
385 #endif
386
387         memset(princ_s, '\0', sizeof(princ_s));
388
389         initialize_krb5_error_table();
390         ret = krb5_init_context(&context);
391         if (ret) {
392                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
393                           error_message(ret)));
394                 goto done;
395         }
396
397         machine_name = talloc_strdup(frame, lp_netbios_name());
398         if (!machine_name) {
399                 ret = -1;
400                 goto done;
401         }
402
403         /* now add the userPrincipalName and sAMAccountName entries */
404         sam_account_name = ads_get_samaccountname(ads, frame, machine_name);
405         if (!sam_account_name) {
406                 DEBUG(0, (__location__ ": unable to determine machine "
407                           "account's name in AD!\n"));
408                 ret = -1;
409                 goto done;
410         }
411
412         /* upper case the sAMAccountName to make it easier for apps to
413            know what case to use in the keytab file */
414         if (!strupper_m(sam_account_name)) {
415                 ret = -1;
416                 goto done;
417         }
418
419         ret = ads_keytab_add_entry(ads, sam_account_name);
420         if (ret != 0) {
421                 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
422                           "while adding sAMAccountName (%s)\n",
423                           sam_account_name));
424                 goto done;
425         }
426
427         /* remember that not every machine account will have a upn */
428         upn = ads_get_upn(ads, frame, machine_name);
429         if (upn) {
430                 ret = ads_keytab_add_entry(ads, upn);
431                 if (ret != 0) {
432                         DEBUG(1, (__location__ ": ads_keytab_add_entry() "
433                                   "failed while adding UPN (%s)\n", upn));
434                         goto done;
435                 }
436         }
437
438         /* Now loop through the keytab and update any other existing entries */
439         kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
440         if (kvno == (krb5_kvno)-1) {
441                 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
442                           "determine the system's kvno.\n"));
443                 goto done;
444         }
445
446         DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
447                   "and update.\n"));
448
449         ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
450         if (ret) {
451                 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
452                           error_message(ret)));
453                 goto done;
454         }
455
456         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
457         if (ret != KRB5_KT_END && ret != ENOENT ) {
458                 while ((ret = krb5_kt_next_entry(context, keytab,
459                                                  &kt_entry, &cursor)) == 0) {
460                         smb_krb5_kt_free_entry(context, &kt_entry);
461                         ZERO_STRUCT(kt_entry);
462                         found++;
463                 }
464         }
465         krb5_kt_end_seq_get(context, keytab, &cursor);
466         ZERO_STRUCT(cursor);
467
468         /*
469          * Hmmm. There is no "rewind" function for the keytab. This means we
470          * have a race condition where someone else could add entries after
471          * we've counted them. Re-open asap to minimise the race. JRA.
472          */
473         DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
474         if (!found) {
475                 goto done;
476         }
477
478         oldEntries = talloc_zero_array(frame, char *, found + 1);
479         if (!oldEntries) {
480                 DEBUG(1, (__location__ ": Failed to allocate space to store "
481                           "the old keytab entries (talloc failed?).\n"));
482                 ret = -1;
483                 goto done;
484         }
485
486         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
487         if (ret == KRB5_KT_END || ret == ENOENT) {
488                 krb5_kt_end_seq_get(context, keytab, &cursor);
489                 ZERO_STRUCT(cursor);
490                 goto done;
491         }
492
493         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
494                 if (kt_entry.vno != kvno) {
495                         char *ktprinc = NULL;
496                         char *p;
497
498                         /* This returns a malloc'ed string in ktprinc. */
499                         ret = smb_krb5_unparse_name(oldEntries, context,
500                                                     kt_entry.principal,
501                                                     &ktprinc);
502                         if (ret) {
503                                 DEBUG(1, (__location__
504                                          ": smb_krb5_unparse_name failed "
505                                          "(%s)\n", error_message(ret)));
506                                 goto done;
507                         }
508                         /*
509                          * From looking at the krb5 source they don't seem to
510                          * take locale or mb strings into account.
511                          * Maybe this is because they assume utf8 ?
512                          * In this case we may need to convert from utf8 to
513                          * mb charset here ? JRA.
514                          */
515                         p = strchr_m(ktprinc, '@');
516                         if (p) {
517                                 *p = '\0';
518                         }
519
520                         p = strchr_m(ktprinc, '/');
521                         if (p) {
522                                 *p = '\0';
523                         }
524                         for (i = 0; i < found; i++) {
525                                 if (!oldEntries[i]) {
526                                         oldEntries[i] = ktprinc;
527                                         break;
528                                 }
529                                 if (!strcmp(oldEntries[i], ktprinc)) {
530                                         TALLOC_FREE(ktprinc);
531                                         break;
532                                 }
533                         }
534                         if (i == found) {
535                                 TALLOC_FREE(ktprinc);
536                         }
537                 }
538                 smb_krb5_kt_free_entry(context, &kt_entry);
539                 ZERO_STRUCT(kt_entry);
540         }
541         krb5_kt_end_seq_get(context, keytab, &cursor);
542         ZERO_STRUCT(cursor);
543
544         ret = 0;
545         for (i = 0; oldEntries[i]; i++) {
546                 ret |= ads_keytab_add_entry(ads, oldEntries[i]);
547                 TALLOC_FREE(oldEntries[i]);
548         }
549
550 done:
551         TALLOC_FREE(oldEntries);
552         TALLOC_FREE(frame);
553
554         {
555                 krb5_keytab_entry zero_kt_entry;
556                 ZERO_STRUCT(zero_kt_entry);
557                 if (memcmp(&zero_kt_entry, &kt_entry,
558                                 sizeof(krb5_keytab_entry))) {
559                         smb_krb5_kt_free_entry(context, &kt_entry);
560                 }
561         }
562         {
563                 krb5_kt_cursor zero_csr;
564                 ZERO_STRUCT(zero_csr);
565                 if ((memcmp(&cursor, &zero_csr,
566                                 sizeof(krb5_kt_cursor)) != 0) && keytab) {
567                         krb5_kt_end_seq_get(context, keytab, &cursor);
568                 }
569         }
570         if (keytab) {
571                 krb5_kt_close(context, keytab);
572         }
573         if (context) {
574                 krb5_free_context(context);
575         }
576         return ret;
577 }
578
579 #endif /* HAVE_ADS */
580
581 /**********************************************************************
582  List system keytab.
583 ***********************************************************************/
584
585 int ads_keytab_list(const char *keytab_name)
586 {
587         krb5_error_code ret = 0;
588         krb5_context context = NULL;
589         krb5_keytab keytab = NULL;
590         krb5_kt_cursor cursor;
591         krb5_keytab_entry kt_entry;
592
593         ZERO_STRUCT(kt_entry);
594         ZERO_STRUCT(cursor);
595
596         initialize_krb5_error_table();
597         ret = krb5_init_context(&context);
598         if (ret) {
599                 DEBUG(1, (__location__ ": could not krb5_init_context: %s\n",
600                           error_message(ret)));
601                 return ret;
602         }
603
604         ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
605         if (ret) {
606                 DEBUG(1, (__location__ ": smb_krb5_open_keytab failed (%s)\n",
607                           error_message(ret)));
608                 goto out;
609         }
610
611         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
612         if (ret) {
613                 ZERO_STRUCT(cursor);
614                 goto out;
615         }
616
617         printf("Vno  Type                                        Principal\n");
618
619         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
620
621                 char *princ_s = NULL;
622                 char *etype_s = NULL;
623                 krb5_enctype enctype = 0;
624
625                 ret = smb_krb5_unparse_name(talloc_tos(), context,
626                                             kt_entry.principal, &princ_s);
627                 if (ret) {
628                         goto out;
629                 }
630
631                 enctype = smb_get_enctype_from_kt_entry(&kt_entry);
632
633                 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
634                 if (ret &&
635                     (asprintf(&etype_s, "UNKNOWN: %d\n", enctype) == -1)) {
636                         TALLOC_FREE(princ_s);
637                         goto out;
638                 }
639
640                 printf("%3d  %-43s %s\n", kt_entry.vno, etype_s, princ_s);
641
642                 TALLOC_FREE(princ_s);
643                 SAFE_FREE(etype_s);
644
645                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
646                 if (ret) {
647                         goto out;
648                 }
649         }
650
651         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
652         if (ret) {
653                 goto out;
654         }
655
656         /* Ensure we don't double free. */
657         ZERO_STRUCT(kt_entry);
658         ZERO_STRUCT(cursor);
659 out:
660
661         {
662                 krb5_keytab_entry zero_kt_entry;
663                 ZERO_STRUCT(zero_kt_entry);
664                 if (memcmp(&zero_kt_entry, &kt_entry,
665                                 sizeof(krb5_keytab_entry))) {
666                         smb_krb5_kt_free_entry(context, &kt_entry);
667                 }
668         }
669         {
670                 krb5_kt_cursor zero_csr;
671                 ZERO_STRUCT(zero_csr);
672                 if ((memcmp(&cursor, &zero_csr,
673                                 sizeof(krb5_kt_cursor)) != 0) && keytab) {
674                         krb5_kt_end_seq_get(context, keytab, &cursor);
675                 }
676         }
677
678         if (keytab) {
679                 krb5_kt_close(context, keytab);
680         }
681         if (context) {
682                 krb5_free_context(context);
683         }
684         return ret;
685 }
686
687 #endif /* HAVE_KRB5 */