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