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