f7f706d90cd9cba0e9b5aab006c3288c8d0812ab
[samba.git] / source3 / libnet / libnet_dssync_keytab.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Guenther Deschner <gd@samba.org> 2008
5    Copyright (C) Michael Adam 2008
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "libnet/libnet.h"
23 #include "librpc/gen_ndr/ndr_drsblobs.h"
24
25 #if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
26
27 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
28                                struct replUpToDateVectorBlob **pold_utdv)
29 {
30         krb5_error_code ret = 0;
31         struct libnet_keytab_context *keytab_ctx;
32         struct libnet_keytab_entry *entry;
33         struct replUpToDateVectorBlob *old_utdv = NULL;
34         char *principal;
35
36         ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
37         if (ret) {
38                 return krb5_to_nt_status(ret);
39         }
40
41         keytab_ctx->dns_domain_name = ctx->dns_domain_name;
42         keytab_ctx->clean_old_entries = ctx->clean_old_entries;
43         ctx->private_data = keytab_ctx;
44
45         principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
46                                     ctx->nc_dn, ctx->dns_domain_name);
47         NT_STATUS_HAVE_NO_MEMORY(principal);
48
49         entry = libnet_keytab_search(keytab_ctx, principal, 0, ENCTYPE_NULL,
50                                      mem_ctx);
51         if (entry) {
52                 enum ndr_err_code ndr_err;
53                 old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
54
55                 ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv, old_utdv,
56                                 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
57                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
58                         NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
59                         ctx->error_message = talloc_asprintf(ctx,
60                                         "Failed to pull UpToDateVector: %s",
61                                         nt_errstr(status));
62                         return status;
63                 }
64
65                 if (DEBUGLEVEL >= 10) {
66                         NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
67                 }
68         }
69
70         if (pold_utdv) {
71                 *pold_utdv = old_utdv;
72         }
73
74         return NT_STATUS_OK;
75 }
76
77 static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
78                               struct replUpToDateVectorBlob *new_utdv)
79 {
80         NTSTATUS status = NT_STATUS_OK;
81         krb5_error_code ret = 0;
82         struct libnet_keytab_context *keytab_ctx =
83                 (struct libnet_keytab_context *)ctx->private_data;
84
85         if (new_utdv) {
86                 enum ndr_err_code ndr_err;
87                 DATA_BLOB blob;
88
89                 if (DEBUGLEVEL >= 10) {
90                         NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
91                 }
92
93                 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
94                                 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
95                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
96                         status = ndr_map_error2ntstatus(ndr_err);
97                         ctx->error_message = talloc_asprintf(ctx,
98                                         "Failed to push UpToDateVector: %s",
99                                         nt_errstr(status));
100                         goto done;
101                 }
102
103                 status = libnet_keytab_add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
104                                                              ctx->nc_dn, "UTDV",
105                                                              ENCTYPE_NULL,
106                                                              blob);
107                 if (!NT_STATUS_IS_OK(status)) {
108                         goto done;
109                 }
110         }
111
112         ret = libnet_keytab_add(keytab_ctx);
113         if (ret) {
114                 status = krb5_to_nt_status(ret);
115                 ctx->error_message = talloc_asprintf(ctx,
116                         "Failed to add entries to keytab %s: %s",
117                         keytab_ctx->keytab_name, error_message(ret));
118                 goto done;
119         }
120
121         ctx->result_message = talloc_asprintf(ctx,
122                 "Vampired %d accounts to keytab %s",
123                 keytab_ctx->count,
124                 keytab_ctx->keytab_name);
125
126 done:
127         TALLOC_FREE(keytab_ctx);
128         return status;
129 }
130
131 /****************************************************************
132 ****************************************************************/
133
134 static  NTSTATUS parse_supplemental_credentials(TALLOC_CTX *mem_ctx,
135                         const DATA_BLOB *blob,
136                         struct package_PrimaryKerberosCtr3 **pkb3,
137                         struct package_PrimaryKerberosCtr4 **pkb4)
138 {
139         NTSTATUS status;
140         enum ndr_err_code ndr_err;
141         struct supplementalCredentialsBlob scb;
142         struct supplementalCredentialsPackage *scpk = NULL;
143         DATA_BLOB scpk_blob;
144         struct package_PrimaryKerberosBlob *pkb;
145         bool newer_keys = false;
146         uint32_t j;
147
148         ndr_err = ndr_pull_struct_blob_all(blob, mem_ctx, &scb,
149                         (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob);
150         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
151                 status = ndr_map_error2ntstatus(ndr_err);
152                 goto done;
153         }
154         if (scb.sub.signature !=
155             SUPPLEMENTAL_CREDENTIALS_SIGNATURE)
156         {
157                 if (DEBUGLEVEL >= 10) {
158                         NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
159                 }
160                 status = NT_STATUS_INVALID_PARAMETER;
161                 goto done;
162         }
163         for (j=0; j < scb.sub.num_packages; j++) {
164                 if (strcmp("Primary:Kerberos-Newer-Keys",
165                     scb.sub.packages[j].name) == 0)
166                 {
167                         scpk = &scb.sub.packages[j];
168                         if (!scpk->data || !scpk->data[0]) {
169                                 scpk = NULL;
170                                 continue;
171                         }
172                         newer_keys = true;
173                         break;
174                 } else  if (strcmp("Primary:Kerberos",
175                                    scb.sub.packages[j].name) == 0)
176                 {
177                         /*
178                          * grab this but don't break here:
179                          * there might still be newer-keys ...
180                          */
181                         scpk = &scb.sub.packages[j];
182                         if (!scpk->data || !scpk->data[0]) {
183                                 scpk = NULL;
184                         }
185                 }
186         }
187
188         if (!scpk) {
189                 /* no data */
190                 status = NT_STATUS_OK;
191                 goto done;
192         }
193
194         scpk_blob = strhex_to_data_blob(mem_ctx, scpk->data);
195         if (!scpk_blob.data) {
196                 status = NT_STATUS_NO_MEMORY;
197                 goto done;
198         }
199
200         pkb = TALLOC_ZERO_P(mem_ctx, struct package_PrimaryKerberosBlob);
201         if (!pkb) {
202                 status = NT_STATUS_NO_MEMORY;
203                 goto done;
204         }
205         ndr_err = ndr_pull_struct_blob(&scpk_blob, mem_ctx, pkb,
206                         (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob);
207         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
208                 status = ndr_map_error2ntstatus(ndr_err);
209                 goto done;
210         }
211
212         if (!newer_keys && pkb->version != 3) {
213                 status = NT_STATUS_INVALID_PARAMETER;
214                 goto done;
215         }
216
217         if (newer_keys && pkb->version != 4) {
218                 status = NT_STATUS_INVALID_PARAMETER;
219                 goto done;
220         }
221
222         if (pkb->version == 4 && pkb4) {
223                 *pkb4 = &pkb->ctr.ctr4;
224         } else if (pkb->version == 3 && pkb3) {
225                 *pkb3 = &pkb->ctr.ctr3;
226         }
227
228         status = NT_STATUS_OK;
229
230 done:
231         return status;
232 }
233
234 static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
235                              struct libnet_keytab_context *ctx,
236                              struct drsuapi_DsReplicaObjectListItemEx *cur)
237 {
238         NTSTATUS status = NT_STATUS_OK;
239         uchar nt_passwd[16];
240         DATA_BLOB *blob;
241         int i = 0;
242         struct drsuapi_DsReplicaAttribute *attr;
243         bool got_pwd = false;
244
245         struct package_PrimaryKerberosCtr3 *pkb3 = NULL;
246         struct package_PrimaryKerberosCtr4 *pkb4 = NULL;
247
248         char *object_dn = NULL;
249         char *upn = NULL;
250         char **spn = NULL;
251         uint32_t num_spns = 0;
252         char *name = NULL;
253         uint32_t kvno = 0;
254         uint32_t uacc = 0;
255         uint32_t sam_type = 0;
256
257         uint32_t pwd_history_len = 0;
258         uint8_t *pwd_history = NULL;
259
260         ZERO_STRUCT(nt_passwd);
261
262         object_dn = talloc_strdup(mem_ctx, cur->object.identifier->dn);
263         if (!object_dn) {
264                 return NT_STATUS_NO_MEMORY;
265         }
266
267         DEBUG(3, ("parsing object '%s'\n", object_dn));
268
269         for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
270
271                 attr = &cur->object.attribute_ctr.attributes[i];
272
273                 if (attr->attid == DRSUAPI_ATTRIBUTE_servicePrincipalName) {
274                         uint32_t count;
275                         num_spns = attr->value_ctr.num_values;
276                         spn = TALLOC_ARRAY(mem_ctx, char *, num_spns);
277                         for (count = 0; count < num_spns; count++) {
278                                 blob = attr->value_ctr.values[count].blob;
279                                 pull_string_talloc(spn, NULL, 0,
280                                                    &spn[count],
281                                                    blob->data, blob->length,
282                                                    STR_UNICODE);
283                         }
284                 }
285
286                 if (attr->value_ctr.num_values != 1) {
287                         continue;
288                 }
289
290                 if (!attr->value_ctr.values[0].blob) {
291                         continue;
292                 }
293
294                 blob = attr->value_ctr.values[0].blob;
295
296                 switch (attr->attid) {
297                         case DRSUAPI_ATTRIBUTE_unicodePwd:
298
299                                 if (blob->length != 16) {
300                                         break;
301                                 }
302
303                                 memcpy(&nt_passwd, blob->data, 16);
304                                 got_pwd = true;
305
306                                 /* pick the kvno from the meta_data version,
307                                  * thanks, metze, for explaining this */
308
309                                 if (!cur->meta_data_ctr) {
310                                         break;
311                                 }
312                                 if (cur->meta_data_ctr->count !=
313                                     cur->object.attribute_ctr.num_attributes) {
314                                         break;
315                                 }
316                                 kvno = cur->meta_data_ctr->meta_data[i].version;
317                                 break;
318                         case DRSUAPI_ATTRIBUTE_ntPwdHistory:
319                                 pwd_history_len = blob->length / 16;
320                                 pwd_history = blob->data;
321                                 break;
322                         case DRSUAPI_ATTRIBUTE_userPrincipalName:
323                                 pull_string_talloc(mem_ctx, NULL, 0, &upn,
324                                                    blob->data, blob->length,
325                                                    STR_UNICODE);
326                                 break;
327                         case DRSUAPI_ATTRIBUTE_sAMAccountName:
328                                 pull_string_talloc(mem_ctx, NULL, 0, &name,
329                                                    blob->data, blob->length,
330                                                    STR_UNICODE);
331                                 break;
332                         case DRSUAPI_ATTRIBUTE_sAMAccountType:
333                                 sam_type = IVAL(blob->data, 0);
334                                 break;
335                         case DRSUAPI_ATTRIBUTE_userAccountControl:
336                                 uacc = IVAL(blob->data, 0);
337                                 break;
338                         case DRSUAPI_ATTRIBUTE_supplementalCredentials:
339                                 status = parse_supplemental_credentials(mem_ctx,
340                                                                         blob,
341                                                                         &pkb3,
342                                                                         &pkb4);
343                                 if (!NT_STATUS_IS_OK(status)) {
344                                         DEBUG(2, ("parsing of supplemental "
345                                                   "credentials failed: %s\n",
346                                                   nt_errstr(status)));
347                                 }
348                                 break;
349                         default:
350                                 break;
351                 }
352         }
353
354         if (!got_pwd) {
355                 DEBUG(10, ("no password (unicodePwd) found - skipping.\n"));
356                 return NT_STATUS_OK;
357         }
358
359         if (name) {
360                 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, 0, object_dn,
361                                                              "SAMACCOUNTNAME",
362                                                              ENCTYPE_NULL,
363                                                              data_blob_talloc(mem_ctx, name,
364                                                              strlen(name) + 1));
365                 if (!NT_STATUS_IS_OK(status)) {
366                         return status;
367                 }
368         } else {
369                 /* look into keytab ... */
370                 struct libnet_keytab_entry *entry = NULL;
371                 char *principal = NULL;
372
373                 DEBUG(10, ("looking for SAMACCOUNTNAME/%s@%s in keytayb...\n",
374                            object_dn, ctx->dns_domain_name));
375
376                 principal = talloc_asprintf(mem_ctx, "%s/%s@%s",
377                                             "SAMACCOUNTNAME",
378                                             object_dn,
379                                             ctx->dns_domain_name);
380                 if (!principal) {
381                         DEBUG(1, ("talloc failed\n"));
382                         return NT_STATUS_NO_MEMORY;
383                 }
384                 entry = libnet_keytab_search(ctx, principal, 0, ENCTYPE_NULL,
385                                              mem_ctx);
386                 if (entry) {
387                         name = (char *)TALLOC_MEMDUP(mem_ctx,
388                                                      entry->password.data,
389                                                      entry->password.length);
390                         if (!name) {
391                                 DEBUG(1, ("talloc failed!"));
392                                 return NT_STATUS_NO_MEMORY;
393                         } else {
394                                 DEBUG(10, ("found name %s\n", name));
395                         }
396                         TALLOC_FREE(entry);
397                 } else {
398                         DEBUG(10, ("entry not found\n"));
399                 }
400                 TALLOC_FREE(principal);
401         }
402
403         if (!name) {
404                 DEBUG(10, ("no name (sAMAccountName) found - skipping.\n"));
405                 return NT_STATUS_OK;
406         }
407
408         DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
409         DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x",
410                 sam_type, uacc));
411         if (upn) {
412                 DEBUGADD(1,(", upn: %s", upn));
413         }
414         if (num_spns > 0) {
415                 DEBUGADD(1, (", spns: ["));
416                 for (i = 0; i < num_spns; i++) {
417                         DEBUGADD(1, ("%s%s", spn[i],
418                                      (i+1 == num_spns)?"]":", "));
419                 }
420         }
421         DEBUGADD(1,("\n"));
422
423         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno, name, NULL,
424                                                      ENCTYPE_ARCFOUR_HMAC,
425                                                      data_blob_talloc(mem_ctx, nt_passwd, 16));
426
427         if (!NT_STATUS_IS_OK(status)) {
428                 return status;
429         }
430
431         /* add kerberos keys (if any) */
432
433         if (pkb4) {
434                 for (i=0; i < pkb4->num_keys; i++) {
435                         if (!pkb4->keys[i].value) {
436                                 continue;
437                         }
438                         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno,
439                                                                      name,
440                                                                      NULL,
441                                                                      pkb4->keys[i].keytype,
442                                                                      *pkb4->keys[i].value);
443                         if (!NT_STATUS_IS_OK(status)) {
444                                 return status;
445                         }
446                 }
447                 for (i=0; i < pkb4->num_old_keys; i++) {
448                         if (!pkb4->old_keys[i].value) {
449                                 continue;
450                         }
451                         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
452                                                                      name,
453                                                                      NULL,
454                                                                      pkb4->old_keys[i].keytype,
455                                                                      *pkb4->old_keys[i].value);
456                         if (!NT_STATUS_IS_OK(status)) {
457                                 return status;
458                         }
459                 }
460                 for (i=0; i < pkb4->num_older_keys; i++) {
461                         if (!pkb4->older_keys[i].value) {
462                                 continue;
463                         }
464                         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 2,
465                                                                      name,
466                                                                      NULL,
467                                                                      pkb4->older_keys[i].keytype,
468                                                                      *pkb4->older_keys[i].value);
469                         if (!NT_STATUS_IS_OK(status)) {
470                                 return status;
471                         }
472                 }
473         }
474
475         if (pkb3) {
476                 for (i=0; i < pkb3->num_keys; i++) {
477                         if (!pkb3->keys[i].value) {
478                                 continue;
479                         }
480                         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno, name,
481                                                                      NULL,
482                                                                      pkb3->keys[i].keytype,
483                                                                      *pkb3->keys[i].value);
484                         if (!NT_STATUS_IS_OK(status)) {
485                                 return status;
486                         }
487                 }
488                 for (i=0; i < pkb3->num_old_keys; i++) {
489                         if (!pkb3->old_keys[i].value) {
490                                 continue;
491                         }
492                         status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno - 1,
493                                                                      name,
494                                                                      NULL,
495                                                                      pkb3->old_keys[i].keytype,
496                                                                      *pkb3->old_keys[i].value);
497                         if (!NT_STATUS_IS_OK(status)) {
498                                 return status;
499                         }
500                 }
501         }
502
503         if ((kvno < 0) && (kvno < pwd_history_len)) {
504                 return status;
505         }
506
507         /* add password history */
508
509         /* skip first entry */
510         if (got_pwd) {
511                 kvno--;
512                 i = 1;
513         } else {
514                 i = 0;
515         }
516
517         for (; i<pwd_history_len; i++) {
518                 status = libnet_keytab_add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
519                                                              ENCTYPE_ARCFOUR_HMAC,
520                                                              data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
521                 if (!NT_STATUS_IS_OK(status)) {
522                         break;
523                 }
524         }
525
526         return status;
527 }
528
529 static bool dn_is_in_object_list(struct dssync_context *ctx,
530                                  const char *dn)
531 {
532         uint32_t count;
533
534         if (ctx->object_count == 0) {
535                 return true;
536         }
537
538         for (count = 0; count < ctx->object_count; count++) {
539                 if (strequal(ctx->object_dns[count], dn)) {
540                         return true;
541                 }
542         }
543
544         return false;
545 }
546
547 /****************************************************************
548 ****************************************************************/
549
550 static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
551                                        TALLOC_CTX *mem_ctx,
552                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
553                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
554 {
555         NTSTATUS status = NT_STATUS_OK;
556         struct libnet_keytab_context *keytab_ctx =
557                 (struct libnet_keytab_context *)ctx->private_data;
558
559         for (; cur; cur = cur->next_object) {
560                 /*
561                  * When not in single object replication mode,
562                  * the object_dn list is used as a positive write filter.
563                  */
564                 if (!ctx->single_object_replication &&
565                     !dn_is_in_object_list(ctx, cur->object.identifier->dn))
566                 {
567                         continue;
568                 }
569
570                 status = parse_object(mem_ctx, keytab_ctx, cur);
571                 if (!NT_STATUS_IS_OK(status)) {
572                         goto out;
573                 }
574         }
575
576  out:
577         return status;
578 }
579
580 #else
581
582 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
583                                struct replUpToDateVectorBlob **pold_utdv)
584 {
585         return NT_STATUS_NOT_SUPPORTED;
586 }
587
588 static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
589                               struct replUpToDateVectorBlob *new_utdv)
590 {
591         return NT_STATUS_NOT_SUPPORTED;
592 }
593
594 static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
595                                        TALLOC_CTX *mem_ctx,
596                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
597                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
598 {
599         return NT_STATUS_NOT_SUPPORTED;
600 }
601 #endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
602
603 const struct dssync_ops libnet_dssync_keytab_ops = {
604         .startup                = keytab_startup,
605         .process_objects        = keytab_process_objects,
606         .finish                 = keytab_finish,
607 };