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