dssync keytab: add support for keeping track of the up-to-date-ness vector.
[jra/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
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "libnet/libnet.h"
22 #include "librpc/gen_ndr/ndr_drsblobs.h"
23
24 #if defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC)
25
26 static NTSTATUS add_to_keytab_entries(TALLOC_CTX *mem_ctx,
27                                       struct libnet_keytab_context *ctx,
28                                       uint32_t kvno,
29                                       const char *name,
30                                       const char *prefix,
31                                       DATA_BLOB blob)
32 {
33         struct libnet_keytab_entry entry;
34
35         entry.kvno = kvno;
36         entry.name = talloc_strdup(mem_ctx, name);
37         entry.principal = talloc_asprintf(mem_ctx, "%s%s%s@%s",
38                                           prefix ? prefix : "",
39                                           prefix ? "/" : "",
40                                           name, ctx->dns_domain_name);
41         entry.password = blob;
42         NT_STATUS_HAVE_NO_MEMORY(entry.name);
43         NT_STATUS_HAVE_NO_MEMORY(entry.principal);
44         NT_STATUS_HAVE_NO_MEMORY(entry.password.data);
45
46         ADD_TO_ARRAY(mem_ctx, struct libnet_keytab_entry, entry,
47                      &ctx->entries, &ctx->count);
48         NT_STATUS_HAVE_NO_MEMORY(ctx->entries);
49
50         return NT_STATUS_OK;
51 }
52
53 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
54                                struct replUpToDateVectorBlob **pold_utdv)
55 {
56         krb5_error_code ret = 0;
57         struct libnet_keytab_context *keytab_ctx;
58         struct libnet_keytab_entry *entry;
59         struct replUpToDateVectorBlob *old_utdv = NULL;
60         char *principal;
61
62         ret = libnet_keytab_init(mem_ctx, ctx->output_filename, &keytab_ctx);
63         if (ret) {
64                 return krb5_to_nt_status(ret);
65         }
66
67         keytab_ctx->dns_domain_name = ctx->dns_domain_name;
68         ctx->private_data = keytab_ctx;
69
70         principal = talloc_asprintf(mem_ctx, "UTDV/%s@%s",
71                                     ctx->nc_dn, ctx->dns_domain_name);
72         NT_STATUS_HAVE_NO_MEMORY(principal);
73
74         entry = libnet_keytab_search(keytab_ctx, principal, 0, mem_ctx);
75         if (entry) {
76                 enum ndr_err_code ndr_err;
77                 old_utdv = talloc(mem_ctx, struct replUpToDateVectorBlob);
78
79                 ndr_err = ndr_pull_struct_blob(&entry->password, old_utdv,
80                                 old_utdv,
81                                 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
82                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
83                         NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
84                         ctx->error_message = talloc_asprintf(mem_ctx,
85                                         "Failed to pull UpToDateVector: %s",
86                                         nt_errstr(status));
87                         return status;
88                 }
89
90                 NDR_PRINT_DEBUG(replUpToDateVectorBlob, old_utdv);
91         }
92
93         if (pold_utdv) {
94                 *pold_utdv = old_utdv;
95         }
96
97         return NT_STATUS_OK;
98 }
99
100 static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
101                               struct replUpToDateVectorBlob *new_utdv)
102 {
103         NTSTATUS status = NT_STATUS_OK;
104         krb5_error_code ret = 0;
105         struct libnet_keytab_context *keytab_ctx =
106                 (struct libnet_keytab_context *)ctx->private_data;
107
108         if (new_utdv) {
109                 enum ndr_err_code ndr_err;
110                 DATA_BLOB blob;
111
112                 NDR_PRINT_DEBUG(replUpToDateVectorBlob, new_utdv);
113                 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, new_utdv,
114                                 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
115                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
116                         status = ndr_map_error2ntstatus(ndr_err);
117                         ctx->error_message = talloc_asprintf(mem_ctx,
118                                         "Failed to push UpToDateVector: %s",
119                                         nt_errstr(status));
120                         goto done;
121                 }
122
123                 status = add_to_keytab_entries(mem_ctx, keytab_ctx, 0,
124                                                ctx->nc_dn, "UTDV", blob);
125                 if (!NT_STATUS_IS_OK(status)) {
126                         goto done;
127                 }
128         }
129
130         ret = libnet_keytab_add(keytab_ctx);
131         if (ret) {
132                 status = krb5_to_nt_status(ret);
133                 ctx->error_message = talloc_asprintf(mem_ctx,
134                         "Failed to add entries to keytab %s: %s",
135                         keytab_ctx->keytab_name, error_message(ret));
136                 goto done;
137         }
138
139         ctx->result_message = talloc_asprintf(mem_ctx,
140                 "Vampired %d accounts to keytab %s",
141                 keytab_ctx->count,
142                 keytab_ctx->keytab_name);
143
144 done:
145         TALLOC_FREE(keytab_ctx);
146         return status;
147 }
148
149 /****************************************************************
150 ****************************************************************/
151
152 static NTSTATUS parse_object(TALLOC_CTX *mem_ctx,
153                              struct libnet_keytab_context *ctx,
154                              struct drsuapi_DsReplicaObjectListItemEx *cur)
155 {
156         NTSTATUS status = NT_STATUS_OK;
157         uchar nt_passwd[16];
158         DATA_BLOB *blob;
159         int i = 0;
160         struct drsuapi_DsReplicaAttribute *attr;
161         bool got_pwd = false;
162
163         char *upn = NULL;
164         char *name = NULL;
165         uint32_t kvno = 0;
166         uint32_t uacc = 0;
167         uint32_t sam_type = 0;
168
169         uint32_t pwd_history_len = 0;
170         uint8_t *pwd_history = NULL;
171
172         ZERO_STRUCT(nt_passwd);
173
174         for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
175
176                 attr = &cur->object.attribute_ctr.attributes[i];
177
178                 if (attr->value_ctr.num_values != 1) {
179                         continue;
180                 }
181
182                 if (!attr->value_ctr.values[0].blob) {
183                         continue;
184                 }
185
186                 blob = attr->value_ctr.values[0].blob;
187
188                 switch (attr->attid) {
189                         case DRSUAPI_ATTRIBUTE_unicodePwd:
190
191                                 if (blob->length != 16) {
192                                         break;
193                                 }
194
195                                 memcpy(&nt_passwd, blob->data, 16);
196                                 got_pwd = true;
197
198                                 /* pick the kvno from the meta_data version,
199                                  * thanks, metze, for explaining this */
200
201                                 if (!cur->meta_data_ctr) {
202                                         break;
203                                 }
204                                 if (cur->meta_data_ctr->count !=
205                                     cur->object.attribute_ctr.num_attributes) {
206                                         break;
207                                 }
208                                 kvno = cur->meta_data_ctr->meta_data[i].version;
209                                 break;
210                         case DRSUAPI_ATTRIBUTE_ntPwdHistory:
211                                 pwd_history_len = blob->length / 16;
212                                 pwd_history = blob->data;
213                                 break;
214                         case DRSUAPI_ATTRIBUTE_msDS_KeyVersionNumber:
215                                 kvno = IVAL(blob->data, 0);
216                                 break;
217                         case DRSUAPI_ATTRIBUTE_userPrincipalName:
218                                 pull_string_talloc(mem_ctx, NULL, 0, &upn,
219                                                    blob->data, blob->length,
220                                                    STR_UNICODE);
221                                 break;
222                         case DRSUAPI_ATTRIBUTE_sAMAccountName:
223                                 pull_string_talloc(mem_ctx, NULL, 0, &name,
224                                                    blob->data, blob->length,
225                                                    STR_UNICODE);
226                                 break;
227                         case DRSUAPI_ATTRIBUTE_sAMAccountType:
228                                 sam_type = IVAL(blob->data, 0);
229                                 break;
230                         case DRSUAPI_ATTRIBUTE_userAccountControl:
231                                 uacc = IVAL(blob->data, 0);
232                                 break;
233                         default:
234                                 break;
235                 }
236         }
237
238         if (!got_pwd || !name) {
239                 return NT_STATUS_OK;
240         }
241
242         DEBUG(1,("#%02d: %s:%d, ", ctx->count, name, kvno));
243         DEBUGADD(1,("sAMAccountType: 0x%08x, userAccountControl: 0x%08x ",
244                 sam_type, uacc));
245         if (upn) {
246                 DEBUGADD(1,("upn: %s", upn));
247         }
248         DEBUGADD(1,("\n"));
249
250         status = add_to_keytab_entries(mem_ctx, ctx, kvno, name, NULL,
251                                        data_blob_talloc(mem_ctx, nt_passwd, 16));
252
253         if (!NT_STATUS_IS_OK(status)) {
254                 return status;
255         }
256
257         if ((kvno < 0) && (kvno < pwd_history_len)) {
258                 return status;
259         }
260
261         /* add password history */
262
263         /* skip first entry */
264         if (got_pwd) {
265                 kvno--;
266                 i = 1;
267         } else {
268                 i = 0;
269         }
270
271         for (; i<pwd_history_len; i++) {
272                 status = add_to_keytab_entries(mem_ctx, ctx, kvno--, name, NULL,
273                                 data_blob_talloc(mem_ctx, &pwd_history[i*16], 16));
274                 if (!NT_STATUS_IS_OK(status)) {
275                         break;
276                 }
277         }
278
279         return status;
280 }
281
282 /****************************************************************
283 ****************************************************************/
284
285 static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
286                                        TALLOC_CTX *mem_ctx,
287                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
288                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
289 {
290         NTSTATUS status = NT_STATUS_OK;
291         struct libnet_keytab_context *keytab_ctx =
292                 (struct libnet_keytab_context *)ctx->private_data;
293
294         for (; cur; cur = cur->next_object) {
295                 status = parse_object(mem_ctx, keytab_ctx, cur);
296                 if (!NT_STATUS_IS_OK(status)) {
297                         goto out;
298                 }
299         }
300
301  out:
302         return status;
303 }
304
305 #else
306
307 static NTSTATUS keytab_startup(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
308                                struct replUpToDateVectorBlob **pold_utdv)
309 {
310         return NT_STATUS_NOT_SUPPORTED;
311 }
312
313 static NTSTATUS keytab_finish(struct dssync_context *ctx, TALLOC_CTX *mem_ctx,
314                               struct replUpToDateVectorBlob *new_utdv)
315 {
316         return NT_STATUS_NOT_SUPPORTED;
317 }
318
319 static NTSTATUS keytab_process_objects(struct dssync_context *ctx,
320                                        TALLOC_CTX *mem_ctx,
321                                        struct drsuapi_DsReplicaObjectListItemEx *cur,
322                                        struct drsuapi_DsReplicaOIDMapping_Ctr *mapping_ctr)
323 {
324         return NT_STATUS_NOT_SUPPORTED;
325 }
326 #endif /* defined(HAVE_ADS) && defined(ENCTYPE_ARCFOUR_HMAC) */
327
328 const struct dssync_ops libnet_dssync_keytab_ops = {
329         .startup                = keytab_startup,
330         .process_objects        = keytab_process_objects,
331         .finish                 = keytab_finish,
332 };