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