s4-kcc: simplify the ReplicaGetInfo implementation a bit
[ira/wip.git] / source4 / dsdb / kcc / kcc_drs_replica_info.c
1 /*
2  Unix SMB/CIFS implementation.
3
4  DRS Replica Information
5
6  Copyright (C) Erick Nogueira do Nascimento 2009
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 3 of the License, or
11  (at your option) any later version.
12
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21  */
22
23 #include "includes.h"
24 #include "dsdb/samdb/samdb.h"
25 #include "auth/auth.h"
26 #include "smbd/service.h"
27 #include "lib/events/events.h"
28 #include "lib/messaging/irpc.h"
29 #include "dsdb/kcc/kcc_service.h"
30 #include "lib/ldb/include/ldb_errors.h"
31 #include "../lib/util/dlinklist.h"
32 #include "librpc/gen_ndr/ndr_misc.h"
33 #include "librpc/gen_ndr/ndr_drsuapi.h"
34 #include "librpc/gen_ndr/ndr_drsblobs.h"
35 #include "param/param.h"
36
37 struct ncList {
38         struct ldb_dn *dn;
39         struct ncList *prev, *next;
40 };
41
42 struct neighList {
43         struct drsuapi_DsReplicaNeighbour *neigh;
44         struct neighList *prev, *next;
45 };
46
47 static WERROR copy_repsfrom_1_to_2(TALLOC_CTX *mem_ctx,
48                                  struct repsFromTo2 **reps2,
49                                  struct repsFromTo1 *reps1)
50 {
51         struct repsFromTo2* reps;
52
53         reps = talloc_zero(mem_ctx, struct repsFromTo2);
54         W_ERROR_HAVE_NO_MEMORY(reps);
55
56         reps->blobsize = reps1->blobsize;
57         reps->consecutive_sync_failures = reps1->consecutive_sync_failures;
58         reps->last_attempt = reps1->last_attempt;
59         reps->last_success = reps1->last_success;
60         reps->other_info = talloc_zero(mem_ctx, struct repsFromTo2OtherInfo);
61         W_ERROR_HAVE_NO_MEMORY(reps->other_info);
62         reps->other_info->dns_name1 = reps1->other_info->dns_name;
63         reps->replica_flags = reps1->replica_flags;
64         memcpy(reps->schedule, reps1->schedule, sizeof(reps1->schedule));
65         reps->reserved = reps1->reserved;
66         reps->highwatermark = reps1->highwatermark;
67         reps->source_dsa_obj_guid = reps1->source_dsa_obj_guid;
68         reps->source_dsa_invocation_id = reps1->source_dsa_invocation_id;
69         reps->transport_guid = reps1->transport_guid;
70
71         *reps2 = reps;
72         return WERR_OK;
73 }
74
75 static WERROR fill_neighbor_from_repsFrom(TALLOC_CTX *mem_ctx,
76                                           struct ldb_context *samdb,
77                                           struct ldb_dn *nc_dn,
78                                           struct drsuapi_DsReplicaNeighbour *neigh,
79                                           struct repsFromTo2 *reps_from)
80 {
81         WERROR status;
82         struct ldb_dn *source_dsa_dn;
83         int ret;
84         char *dsa_guid_str;
85         struct ldb_dn *transport_obj_dn = NULL;
86
87         neigh->source_dsa_address = reps_from->other_info->dns_name1;
88         neigh->replica_flags = reps_from->replica_flags;
89         neigh->last_attempt = reps_from->last_attempt;
90         neigh->source_dsa_obj_guid = reps_from->source_dsa_obj_guid;
91
92         dsa_guid_str = GUID_string(mem_ctx, &reps_from->source_dsa_obj_guid);
93         W_ERROR_HAVE_NO_MEMORY(dsa_guid_str);
94         ret = dsdb_find_dn_by_guid(samdb, mem_ctx, dsa_guid_str, &source_dsa_dn);
95
96         if (ret != LDB_SUCCESS) {
97                 DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n",
98                       dsa_guid_str));
99                 status = WERR_DS_DRA_INTERNAL_ERROR;
100                 goto DONE;
101         }
102
103         neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn);
104         neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn);
105
106         if (dsdb_find_guid_by_dn(samdb, nc_dn, &neigh->naming_context_obj_guid)
107                         != LDB_SUCCESS) {
108                 status = WERR_DS_DRA_INTERNAL_ERROR;
109                 goto DONE;
110         }
111
112         if (!GUID_all_zero(&reps_from->transport_guid)) {
113                 char *transp_guid_str = GUID_string(mem_ctx, &reps_from->transport_guid);
114                 W_ERROR_HAVE_NO_MEMORY(transp_guid_str);
115                 if (dsdb_find_dn_by_guid(samdb, mem_ctx, transp_guid_str,
116                                          &transport_obj_dn) != LDB_SUCCESS)
117                 {
118                         status = WERR_DS_DRA_INTERNAL_ERROR;
119                         goto DONE;
120                 }
121         }
122
123         neigh->transport_obj_dn = ldb_dn_get_linearized(transport_obj_dn);
124         neigh->source_dsa_invocation_id = reps_from->source_dsa_invocation_id;
125         neigh->transport_obj_guid = reps_from->transport_guid;
126         neigh->highest_usn = reps_from->highwatermark.highest_usn;
127         neigh->tmp_highest_usn = reps_from->highwatermark.tmp_highest_usn;
128         neigh->last_success = reps_from->last_success;
129         neigh->result_last_attempt = reps_from->result_last_attempt;
130         neigh->consecutive_sync_failures = reps_from->consecutive_sync_failures;
131         neigh->reserved = 0; /* Unused. MUST be 0. */
132
133         /* If everything went fine so far, set the status to OK */
134         status = WERR_OK;
135 DONE:
136         return status;
137 }
138
139 /*
140  * See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_NEIGHBORS
141  * */
142 static WERROR kccdrs_replica_get_info_neighbours(TALLOC_CTX *mem_ctx,
143                                                  struct ldb_context *samdb,
144                                                  struct drsuapi_DsReplicaGetInfo *r,
145                                                  union drsuapi_DsReplicaInfo *reply,
146                                                  int base_index,
147                                                  struct GUID req_src_dsa_guid,
148                                                  struct ncList *nc_list)
149 {
150         WERROR status;
151
152         int i, j, k;
153         struct ldb_dn *nc_dn = NULL;
154         struct ncList *p_nc_list = NULL;
155
156         struct repsFromToBlob *reps_from_blob = NULL;
157         struct repsFromTo2 *reps_from = NULL;
158         uint32_t c_reps_from;
159
160         int i_rep;
161
162         struct neighList *neigh_list = NULL;
163         struct neighList *neigh_elem = NULL;
164
165         struct drsuapi_DsReplicaNeighbour *neigh = NULL;
166
167         i = j = 0;
168         neigh_list = NULL;
169
170         /* foreach nc in ncs */
171         for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) {
172
173                 nc_dn = p_nc_list->dn;
174
175                 /* load the nc's repsFromTo blob */
176                 status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsFrom",
177                                 &reps_from_blob, &c_reps_from);
178                 if (!W_ERROR_IS_OK(status)) {
179                         status = WERR_DS_DRA_INTERNAL_ERROR;
180                         goto DONE;
181                 }
182
183                 /* foreach r in nc!repsFrom */
184                 for (i_rep = 0; i_rep < c_reps_from; i_rep++) {
185
186                         /* put all info on reps_from */
187                         if (reps_from_blob[i_rep].version == 1) {
188                                 status = copy_repsfrom_1_to_2(mem_ctx, &reps_from,
189                                                               &reps_from_blob[i_rep].ctr.ctr1);
190                                 if (!W_ERROR_IS_OK(status)) {
191                                         goto DONE;
192                                 }
193                         } else { /* reps_from->version == 2 */
194                                 reps_from = &reps_from_blob[i_rep].ctr.ctr2;
195                         }
196
197                         if (GUID_all_zero(&req_src_dsa_guid) ||
198                             GUID_compare(&req_src_dsa_guid, &reps_from->source_dsa_obj_guid) == 0)
199                         {
200
201                                 if (i >= base_index) {
202                                         neigh = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbour);
203                                         W_ERROR_HAVE_NO_MEMORY(neigh);
204
205                                         status = fill_neighbor_from_repsFrom(mem_ctx, samdb,
206                                                                              nc_dn, neigh,
207                                                                              reps_from);
208                                         if (!W_ERROR_IS_OK(status)) {
209                                                 goto DONE;
210                                         }
211
212                                         /* append the neighbor to neigh_list */
213                                         neigh_elem = talloc_zero(mem_ctx, struct neighList);
214                                         W_ERROR_HAVE_NO_MEMORY(neigh_elem);
215                                         neigh_elem->neigh = neigh;
216                                         DLIST_ADD_END(neigh_list, neigh_elem, struct neighList*);
217
218                                         j++;
219                                 }
220
221                                 i++;
222                         }
223                 }
224         }
225
226         /* put all neighbours on neigh_list on reply->neighbours->array */
227         reply->neighbours = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr);
228         W_ERROR_HAVE_NO_MEMORY(reply->neighbours);
229
230         reply->neighbours->count = j;
231         reply->neighbours->reserved = 0;
232         reply->neighbours->array = talloc_array(mem_ctx, struct drsuapi_DsReplicaNeighbour, j);
233         W_ERROR_HAVE_NO_MEMORY(reply->neighbours->array);
234
235         for (k = 0; neigh_list != NULL; neigh_list = neigh_list->next, k++) {
236                 reply->neighbours->array[k] = *neigh_list->neigh;
237         }
238
239         /* If everything went fine so far, set the status to OK */
240         status = WERR_OK;
241 DONE:
242         return status;
243 }
244
245 static WERROR get_master_ncs(TALLOC_CTX *mem_ctx, struct ldb_context *samdb,
246                              const char *ntds_guid_str, struct ncList **master_nc_list)
247 {
248         WERROR status;
249         const char *attrs[] = { "hasMasterNCs", NULL };
250         struct ldb_result *res;
251         struct ncList *nc_list = NULL;
252         struct ncList *nc_list_elem;
253         int ret;
254         int i;
255         char *nc_str;
256
257         ret = ldb_search(samdb, mem_ctx, &res, ldb_get_config_basedn(samdb),
258                         LDB_SCOPE_DEFAULT, attrs, "(objectguid=%s)", ntds_guid_str);
259
260         if (ret != LDB_SUCCESS) {
261                 DEBUG(0,(__location__ ": Failed objectguid search - %s\n", ldb_errstring(samdb)));
262                 status = WERR_INTERNAL_ERROR;
263                 goto DONE;
264         }
265
266         if (res->count == 0) {
267                 DEBUG(0,(__location__ ": Failed: objectguid=%s not found\n", ntds_guid_str));
268                 status = WERR_INTERNAL_ERROR;
269                 goto DONE;
270         }
271
272         for (i = 0; i < res->count; i++) {
273
274                 struct ldb_message_element *msg_elem = ldb_msg_find_element(
275                                 res->msgs[i], "hasMasterNCs");
276                 int k;
277
278                 if (!msg_elem || msg_elem->num_values == 0) {
279                         DEBUG(0,(__location__ ": Failed: Attribute hasMasterNCs not found - %s\n",
280                               ldb_errstring(samdb)));
281                         status = WERR_INTERNAL_ERROR;
282                         goto DONE;
283                 }
284
285                 for (k = 0; k < msg_elem->num_values; k++) {
286                         int len = msg_elem->values[k].length;
287
288                         /* copy the string on msg_elem->values[k]->data to nc_str */
289                         nc_str = talloc_array(mem_ctx, char, len);
290                         W_ERROR_HAVE_NO_MEMORY(nc_str);
291                         memcpy(nc_str, msg_elem->values[k].data, len);
292                         nc_str[len] = '\0';
293
294                         nc_list_elem = talloc_zero(mem_ctx, struct ncList);
295                         W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
296                         nc_list_elem->dn = ldb_dn_new(mem_ctx, samdb, nc_str);
297                         W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
298                         DLIST_ADD(nc_list, nc_list_elem);
299                 }
300
301         }
302
303         *master_nc_list = nc_list;
304         /* If everything went fine so far, set the status to OK */
305         status = WERR_OK;
306 DONE:
307         return status;
308 }
309
310 NTSTATUS kccdrs_replica_get_info(struct irpc_message *msg,
311                                  struct drsuapi_DsReplicaGetInfo *req)
312 {
313         WERROR status;
314
315         struct drsuapi_DsReplicaGetInfoRequest1 *req1;
316         struct drsuapi_DsReplicaGetInfoRequest2 *req2;
317         enum drsuapi_DsReplicaInfoType info_type, *tmp_p_info_type;
318
319         int base_index;
320         union drsuapi_DsReplicaInfo *reply;
321
322         struct GUID req_src_dsa_guid;
323         const char *object_dn = NULL;
324         struct ldb_dn *nc_dn = NULL;
325         struct ncList *nc_list = NULL, *nc_list_elem = NULL;
326
327         struct kccsrv_service *service;
328         struct ldb_context *samdb;
329         TALLOC_CTX *mem_ctx;
330
331         service = talloc_get_type(msg->private_data, struct kccsrv_service);
332         samdb = service->samdb;
333         mem_ctx = talloc_new(msg);
334         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
335
336         NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaGetInfo, req);
337
338         /* check request version */
339         if (req->in.level != DRSUAPI_DS_REPLICA_GET_INFO &&
340             req->in.level != DRSUAPI_DS_REPLICA_GET_INFO2)
341         {
342                 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo level %u\n",
343                          req->in.level));
344                 status = WERR_REVISION_MISMATCH;
345                 goto DONE;
346         }
347
348         if (req->in.level == DRSUAPI_DS_REPLICA_GET_INFO) {
349                 req1 = &req->in.req->req1;
350                 base_index = 0;
351                 info_type = req1->info_type;
352                 object_dn = req1->object_dn;
353                 req_src_dsa_guid = req1->guid1;
354
355         } else { /* r->in.level == DRSUAPI_DS_REPLICA_GET_INFO2 */
356                 req2 = &req->in.req->req2;
357                 if (req2->unknown2 == 0xffffffff) {
358                         /* no more data is available */
359                         status = WERR_NO_MORE_ITEMS; /* on MS-DRSR it is ERROR_NO_MORE_ITEMS */
360                         goto DONE;
361                 }
362
363                 base_index = req2->unknown2; /* Note: This is dwEnumerationContext on MS-DRSR 4.1.13.1.3 */
364                 info_type = req2->info_type;
365                 object_dn = req2->object_dn;
366                 req_src_dsa_guid = req2->guid1;
367         }
368
369         /* TODO: Perform the necessary access permission checking here according to the infoType requested */
370         switch (info_type) {
371         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS:
372         case DRSUAPI_DS_REPLICA_INFO_CURSORS:
373         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA:
374         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES:
375         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES:
376         case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS:
377         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA:
378         case DRSUAPI_DS_REPLICA_INFO_CURSORS2:
379         case DRSUAPI_DS_REPLICA_INFO_CURSORS3:
380         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2:
381         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2:
382         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02:
383         case DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04:
384         case DRSUAPI_DS_REPLICA_INFO_CURSORS05:
385         case DRSUAPI_DS_REPLICA_INFO_06:
386                 break;
387         default:
388                 DEBUG(0,(__location__ ": infoType %u requested is invalid.", (unsigned)info_type));
389                 status = WERR_INVALID_PARAMETER; /* infoType is invalid */
390                 goto DONE;
391         }
392
393         /* allocate the reply and fill in some fields */
394         reply = talloc_zero(mem_ctx, union drsuapi_DsReplicaInfo);
395         NT_STATUS_HAVE_NO_MEMORY(reply);
396         req->out.info = reply;
397         tmp_p_info_type = talloc(mem_ctx, enum drsuapi_DsReplicaInfoType);
398         NT_STATUS_HAVE_NO_MEMORY(tmp_p_info_type);
399         *tmp_p_info_type = info_type;
400         req->out.info_type = tmp_p_info_type;
401
402         /* Based on the infoType requested, retrieve the corresponding
403          * information and construct the response message */
404         switch (info_type) {
405
406         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS:
407                 if (object_dn != NULL) { /* ncs := { object_dn } */
408                         nc_list = NULL;
409                         nc_dn = ldb_dn_new(mem_ctx, samdb, object_dn);
410                         nc_list_elem = talloc_zero(mem_ctx, struct ncList);
411                         NT_STATUS_HAVE_NO_MEMORY(nc_list_elem);
412                         nc_list_elem->dn = nc_dn;
413                         DLIST_ADD_END(nc_list, nc_list_elem, struct ncList*);
414
415                 } else {
416                         /* ncs := getNCs() from ldb database.
417                          * getNCs() must return an array containing
418                          * the DSNames of all NCs hosted by this
419                          * server.
420                          */
421                         char *ntds_guid_str = GUID_string(mem_ctx, &service->ntds_guid);
422                         NT_STATUS_HAVE_NO_MEMORY(ntds_guid_str);
423                         status = get_master_ncs(mem_ctx, samdb, ntds_guid_str, &nc_list);
424                         if (!W_ERROR_IS_OK(status)) {
425                                 goto DONE;
426                         }
427                 }
428
429                 status = kccdrs_replica_get_info_neighbours(mem_ctx, samdb, req,
430                                                             reply, base_index,
431                                                             req_src_dsa_guid, nc_list);
432                 break;
433
434         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02: /* DS_REPL_INFO_REPSTO */
435         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
436         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
437         case DRSUAPI_DS_REPLICA_INFO_CURSORS: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_FOR_NC */
438         case DRSUAPI_DS_REPLICA_INFO_CURSORS2: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_2_FOR_NC */
439         case DRSUAPI_DS_REPLICA_INFO_CURSORS3: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_3_FOR_NC */
440         case DRSUAPI_DS_REPLICA_INFO_CURSORS05: /* On MS-DRSR it is DS_REPL_INFO_UPTODATE_VECTOR_V1 */
441         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_ATTR_VALUE */
442         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE */
443         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES */
444         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_LINK_FAILURES */
445         case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS: /* On MS-DRSR it is DS_REPL_INFO_PENDING_OPS */
446         case DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04: /* On MS-DRSR it is DS_REPL_INFO_CLIENT_CONTEXTS */
447         case DRSUAPI_DS_REPLICA_INFO_06: /* On MS-DRSR it is DS_REPL_INFO_SERVER_OUTGOING_CALLS */
448         default:
449                 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo info_type %u\n",
450                          info_type));
451                 status = WERR_INVALID_LEVEL;
452                 break;
453         }
454
455 DONE:
456         /* put the status on the result field of the reply */
457         req->out.result = status;
458         NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaGetInfo, req);
459         return NT_STATUS_OK;
460 }