s4-kcc: added DsReplicaGetInfo CURSORS2 level
[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
38 /*
39   get cursors info for a specified DN
40 */
41 static WERROR kccdrs_replica_get_info_cursors(TALLOC_CTX *mem_ctx,
42                                               struct ldb_context *samdb,
43                                               struct drsuapi_DsReplicaGetInfo *r,
44                                               union drsuapi_DsReplicaInfo *reply,
45                                               struct ldb_dn *dn)
46 {
47         int ret;
48
49         if (!ldb_dn_validate(dn)) {
50                 return WERR_INVALID_PARAMETER;
51         }
52         reply->cursors = talloc(mem_ctx, struct drsuapi_DsReplicaCursorCtr);
53         W_ERROR_HAVE_NO_MEMORY(reply->cursors);
54
55         reply->cursors->reserved = 0;
56
57         ret = dsdb_load_udv_v1(samdb, dn, reply->cursors, &reply->cursors->array, &reply->cursors->count);
58         if (ret != LDB_SUCCESS) {
59                 return WERR_DS_DRA_BAD_NC;
60         }
61         return WERR_OK;
62 }
63
64 /*
65   get cursors2 info for a specified DN
66 */
67 static WERROR kccdrs_replica_get_info_cursors2(TALLOC_CTX *mem_ctx,
68                                                struct ldb_context *samdb,
69                                                struct drsuapi_DsReplicaGetInfo *r,
70                                                union drsuapi_DsReplicaInfo *reply,
71                                                struct ldb_dn *dn)
72 {
73         int ret;
74
75         if (!ldb_dn_validate(dn)) {
76                 return WERR_INVALID_PARAMETER;
77         }
78         reply->cursors2 = talloc(mem_ctx, struct drsuapi_DsReplicaCursor2Ctr);
79         W_ERROR_HAVE_NO_MEMORY(reply->cursors2);
80
81         ret = dsdb_load_udv_v2(samdb, dn, reply->cursors2, &reply->cursors2->array, &reply->cursors2->count);
82         if (ret != LDB_SUCCESS) {
83                 return WERR_DS_DRA_BAD_NC;
84         }
85
86         reply->cursors2->enumeration_context = reply->cursors2->count;
87         return WERR_OK;
88 }
89
90
91 struct ncList {
92         struct ldb_dn *dn;
93         struct ncList *prev, *next;
94 };
95
96 struct neighList {
97         struct drsuapi_DsReplicaNeighbour *neigh;
98         struct neighList *prev, *next;
99 };
100
101 static WERROR copy_repsfrom_1_to_2(TALLOC_CTX *mem_ctx,
102                                  struct repsFromTo2 **reps2,
103                                  struct repsFromTo1 *reps1)
104 {
105         struct repsFromTo2* reps;
106
107         reps = talloc_zero(mem_ctx, struct repsFromTo2);
108         W_ERROR_HAVE_NO_MEMORY(reps);
109
110         reps->blobsize = reps1->blobsize;
111         reps->consecutive_sync_failures = reps1->consecutive_sync_failures;
112         reps->last_attempt = reps1->last_attempt;
113         reps->last_success = reps1->last_success;
114         reps->other_info = talloc_zero(mem_ctx, struct repsFromTo2OtherInfo);
115         W_ERROR_HAVE_NO_MEMORY(reps->other_info);
116         reps->other_info->dns_name1 = reps1->other_info->dns_name;
117         reps->replica_flags = reps1->replica_flags;
118         memcpy(reps->schedule, reps1->schedule, sizeof(reps1->schedule));
119         reps->reserved = reps1->reserved;
120         reps->highwatermark = reps1->highwatermark;
121         reps->source_dsa_obj_guid = reps1->source_dsa_obj_guid;
122         reps->source_dsa_invocation_id = reps1->source_dsa_invocation_id;
123         reps->transport_guid = reps1->transport_guid;
124
125         *reps2 = reps;
126         return WERR_OK;
127 }
128
129 static WERROR fill_neighbor_from_repsFrom(TALLOC_CTX *mem_ctx,
130                                           struct ldb_context *samdb,
131                                           struct ldb_dn *nc_dn,
132                                           struct drsuapi_DsReplicaNeighbour *neigh,
133                                           struct repsFromTo2 *reps_from)
134 {
135         WERROR status;
136         struct ldb_dn *source_dsa_dn;
137         int ret;
138         char *dsa_guid_str;
139         struct ldb_dn *transport_obj_dn = NULL;
140
141         neigh->source_dsa_address = reps_from->other_info->dns_name1;
142         neigh->replica_flags = reps_from->replica_flags;
143         neigh->last_attempt = reps_from->last_attempt;
144         neigh->source_dsa_obj_guid = reps_from->source_dsa_obj_guid;
145
146         dsa_guid_str = GUID_string(mem_ctx, &reps_from->source_dsa_obj_guid);
147         W_ERROR_HAVE_NO_MEMORY(dsa_guid_str);
148         ret = dsdb_find_dn_by_guid(samdb, mem_ctx, dsa_guid_str, &source_dsa_dn);
149
150         if (ret != LDB_SUCCESS) {
151                 DEBUG(0,(__location__ ": Failed to find DN for neighbor GUID %s\n",
152                       dsa_guid_str));
153                 status = WERR_DS_DRA_INTERNAL_ERROR;
154                 goto DONE;
155         }
156
157         neigh->source_dsa_obj_dn = ldb_dn_get_linearized(source_dsa_dn);
158         neigh->naming_context_dn = ldb_dn_get_linearized(nc_dn);
159
160         if (dsdb_find_guid_by_dn(samdb, nc_dn, &neigh->naming_context_obj_guid)
161                         != LDB_SUCCESS) {
162                 status = WERR_DS_DRA_INTERNAL_ERROR;
163                 goto DONE;
164         }
165
166         if (!GUID_all_zero(&reps_from->transport_guid)) {
167                 char *transp_guid_str = GUID_string(mem_ctx, &reps_from->transport_guid);
168                 W_ERROR_HAVE_NO_MEMORY(transp_guid_str);
169                 if (dsdb_find_dn_by_guid(samdb, mem_ctx, transp_guid_str,
170                                          &transport_obj_dn) != LDB_SUCCESS)
171                 {
172                         status = WERR_DS_DRA_INTERNAL_ERROR;
173                         goto DONE;
174                 }
175         }
176
177         neigh->transport_obj_dn = ldb_dn_get_linearized(transport_obj_dn);
178         neigh->source_dsa_invocation_id = reps_from->source_dsa_invocation_id;
179         neigh->transport_obj_guid = reps_from->transport_guid;
180         neigh->highest_usn = reps_from->highwatermark.highest_usn;
181         neigh->tmp_highest_usn = reps_from->highwatermark.tmp_highest_usn;
182         neigh->last_success = reps_from->last_success;
183         neigh->result_last_attempt = reps_from->result_last_attempt;
184         neigh->consecutive_sync_failures = reps_from->consecutive_sync_failures;
185         neigh->reserved = 0; /* Unused. MUST be 0. */
186
187         /* If everything went fine so far, set the status to OK */
188         status = WERR_OK;
189 DONE:
190         return status;
191 }
192
193 /*
194  * See details on MS-DRSR 4.1.13.3, for infoType DS_REPL_INFO_NEIGHBORS
195  * */
196 static WERROR kccdrs_replica_get_info_neighbours(TALLOC_CTX *mem_ctx,
197                                                  struct ldb_context *samdb,
198                                                  struct drsuapi_DsReplicaGetInfo *r,
199                                                  union drsuapi_DsReplicaInfo *reply,
200                                                  int base_index,
201                                                  struct GUID req_src_dsa_guid,
202                                                  struct ncList *nc_list)
203 {
204         WERROR status;
205
206         int i, j, k;
207         struct ldb_dn *nc_dn = NULL;
208         struct ncList *p_nc_list = NULL;
209
210         struct repsFromToBlob *reps_from_blob = NULL;
211         struct repsFromTo2 *reps_from = NULL;
212         uint32_t c_reps_from;
213
214         int i_rep;
215
216         struct neighList *neigh_list = NULL;
217         struct neighList *neigh_elem = NULL;
218
219         struct drsuapi_DsReplicaNeighbour *neigh = NULL;
220
221         i = j = 0;
222         neigh_list = NULL;
223
224         /* foreach nc in ncs */
225         for (p_nc_list = nc_list; p_nc_list != NULL; p_nc_list = p_nc_list->next) {
226
227                 nc_dn = p_nc_list->dn;
228
229                 /* load the nc's repsFromTo blob */
230                 status = dsdb_loadreps(samdb, mem_ctx, nc_dn, "repsFrom",
231                                 &reps_from_blob, &c_reps_from);
232                 if (!W_ERROR_IS_OK(status)) {
233                         status = WERR_DS_DRA_INTERNAL_ERROR;
234                         goto DONE;
235                 }
236
237                 /* foreach r in nc!repsFrom */
238                 for (i_rep = 0; i_rep < c_reps_from; i_rep++) {
239
240                         /* put all info on reps_from */
241                         if (reps_from_blob[i_rep].version == 1) {
242                                 status = copy_repsfrom_1_to_2(mem_ctx, &reps_from,
243                                                               &reps_from_blob[i_rep].ctr.ctr1);
244                                 if (!W_ERROR_IS_OK(status)) {
245                                         goto DONE;
246                                 }
247                         } else { /* reps_from->version == 2 */
248                                 reps_from = &reps_from_blob[i_rep].ctr.ctr2;
249                         }
250
251                         if (GUID_all_zero(&req_src_dsa_guid) ||
252                             GUID_compare(&req_src_dsa_guid, &reps_from->source_dsa_obj_guid) == 0)
253                         {
254
255                                 if (i >= base_index) {
256                                         neigh = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbour);
257                                         W_ERROR_HAVE_NO_MEMORY(neigh);
258
259                                         status = fill_neighbor_from_repsFrom(mem_ctx, samdb,
260                                                                              nc_dn, neigh,
261                                                                              reps_from);
262                                         if (!W_ERROR_IS_OK(status)) {
263                                                 goto DONE;
264                                         }
265
266                                         /* append the neighbor to neigh_list */
267                                         neigh_elem = talloc_zero(mem_ctx, struct neighList);
268                                         W_ERROR_HAVE_NO_MEMORY(neigh_elem);
269                                         neigh_elem->neigh = neigh;
270                                         DLIST_ADD_END(neigh_list, neigh_elem, struct neighList*);
271
272                                         j++;
273                                 }
274
275                                 i++;
276                         }
277                 }
278         }
279
280         /* put all neighbours on neigh_list on reply->neighbours->array */
281         reply->neighbours = talloc_zero(mem_ctx, struct drsuapi_DsReplicaNeighbourCtr);
282         W_ERROR_HAVE_NO_MEMORY(reply->neighbours);
283
284         reply->neighbours->count = j;
285         reply->neighbours->reserved = 0;
286         reply->neighbours->array = talloc_array(mem_ctx, struct drsuapi_DsReplicaNeighbour, j);
287         W_ERROR_HAVE_NO_MEMORY(reply->neighbours->array);
288
289         for (k = 0; neigh_list != NULL; neigh_list = neigh_list->next, k++) {
290                 reply->neighbours->array[k] = *neigh_list->neigh;
291         }
292
293         /* If everything went fine so far, set the status to OK */
294         status = WERR_OK;
295 DONE:
296         return status;
297 }
298
299 static WERROR get_master_ncs(TALLOC_CTX *mem_ctx, struct ldb_context *samdb,
300                              const char *ntds_guid_str, struct ncList **master_nc_list)
301 {
302         WERROR status;
303         const char *attrs[] = { "hasMasterNCs", NULL };
304         struct ldb_result *res;
305         struct ncList *nc_list = NULL;
306         struct ncList *nc_list_elem;
307         int ret;
308         int i;
309         char *nc_str;
310
311         ret = ldb_search(samdb, mem_ctx, &res, ldb_get_config_basedn(samdb),
312                         LDB_SCOPE_DEFAULT, attrs, "(objectguid=%s)", ntds_guid_str);
313
314         if (ret != LDB_SUCCESS) {
315                 DEBUG(0,(__location__ ": Failed objectguid search - %s\n", ldb_errstring(samdb)));
316                 status = WERR_INTERNAL_ERROR;
317                 goto DONE;
318         }
319
320         if (res->count == 0) {
321                 DEBUG(0,(__location__ ": Failed: objectguid=%s not found\n", ntds_guid_str));
322                 status = WERR_INTERNAL_ERROR;
323                 goto DONE;
324         }
325
326         for (i = 0; i < res->count; i++) {
327
328                 struct ldb_message_element *msg_elem = ldb_msg_find_element(
329                                 res->msgs[i], "hasMasterNCs");
330                 int k;
331
332                 if (!msg_elem || msg_elem->num_values == 0) {
333                         DEBUG(0,(__location__ ": Failed: Attribute hasMasterNCs not found - %s\n",
334                               ldb_errstring(samdb)));
335                         status = WERR_INTERNAL_ERROR;
336                         goto DONE;
337                 }
338
339                 for (k = 0; k < msg_elem->num_values; k++) {
340                         int len = msg_elem->values[k].length;
341
342                         /* copy the string on msg_elem->values[k]->data to nc_str */
343                         nc_str = talloc_array(mem_ctx, char, len);
344                         W_ERROR_HAVE_NO_MEMORY(nc_str);
345                         memcpy(nc_str, msg_elem->values[k].data, len);
346                         nc_str[len] = '\0';
347
348                         nc_list_elem = talloc_zero(mem_ctx, struct ncList);
349                         W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
350                         nc_list_elem->dn = ldb_dn_new(mem_ctx, samdb, nc_str);
351                         W_ERROR_HAVE_NO_MEMORY(nc_list_elem);
352                         DLIST_ADD(nc_list, nc_list_elem);
353                 }
354
355         }
356
357         *master_nc_list = nc_list;
358         /* If everything went fine so far, set the status to OK */
359         status = WERR_OK;
360 DONE:
361         return status;
362 }
363
364 NTSTATUS kccdrs_replica_get_info(struct irpc_message *msg,
365                                  struct drsuapi_DsReplicaGetInfo *req)
366 {
367         WERROR status;
368
369         struct drsuapi_DsReplicaGetInfoRequest1 *req1;
370         struct drsuapi_DsReplicaGetInfoRequest2 *req2;
371         enum drsuapi_DsReplicaInfoType info_type, *tmp_p_info_type;
372
373         int base_index;
374         union drsuapi_DsReplicaInfo *reply;
375
376         struct GUID req_src_dsa_guid;
377         const char *object_dn = NULL;
378         struct ldb_dn *nc_dn = NULL;
379         struct ncList *nc_list = NULL, *nc_list_elem = NULL;
380
381         struct kccsrv_service *service;
382         struct ldb_context *samdb;
383         TALLOC_CTX *mem_ctx;
384
385         service = talloc_get_type(msg->private_data, struct kccsrv_service);
386         samdb = service->samdb;
387         mem_ctx = talloc_new(msg);
388         NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
389
390         NDR_PRINT_IN_DEBUG(drsuapi_DsReplicaGetInfo, req);
391
392         /* check request version */
393         if (req->in.level != DRSUAPI_DS_REPLICA_GET_INFO &&
394             req->in.level != DRSUAPI_DS_REPLICA_GET_INFO2)
395         {
396                 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo level %u\n",
397                          req->in.level));
398                 status = WERR_REVISION_MISMATCH;
399                 goto DONE;
400         }
401
402         if (req->in.level == DRSUAPI_DS_REPLICA_GET_INFO) {
403                 req1 = &req->in.req->req1;
404                 base_index = 0;
405                 info_type = req1->info_type;
406                 object_dn = req1->object_dn;
407                 req_src_dsa_guid = req1->guid1;
408
409         } else { /* r->in.level == DRSUAPI_DS_REPLICA_GET_INFO2 */
410                 req2 = &req->in.req->req2;
411                 if (req2->enumeration_context == 0xffffffff) {
412                         /* no more data is available */
413                         status = WERR_NO_MORE_ITEMS; /* on MS-DRSR it is ERROR_NO_MORE_ITEMS */
414                         goto DONE;
415                 }
416
417                 base_index = req2->enumeration_context;
418                 info_type = req2->info_type;
419                 object_dn = req2->object_dn;
420                 req_src_dsa_guid = req2->guid1;
421         }
422
423         /* TODO: Perform the necessary access permission checking here according to the infoType requested */
424         switch (info_type) {
425         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS:
426         case DRSUAPI_DS_REPLICA_INFO_CURSORS:
427         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA:
428         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES:
429         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES:
430         case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS:
431         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA:
432         case DRSUAPI_DS_REPLICA_INFO_CURSORS2:
433         case DRSUAPI_DS_REPLICA_INFO_CURSORS3:
434         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2:
435         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2:
436         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02:
437         case DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04:
438         case DRSUAPI_DS_REPLICA_INFO_CURSORS05:
439         case DRSUAPI_DS_REPLICA_INFO_06:
440                 break;
441         default:
442                 DEBUG(0,(__location__ ": infoType %u requested is invalid.", (unsigned)info_type));
443                 status = WERR_INVALID_PARAMETER; /* infoType is invalid */
444                 goto DONE;
445         }
446
447         /* allocate the reply and fill in some fields */
448         reply = talloc_zero(mem_ctx, union drsuapi_DsReplicaInfo);
449         NT_STATUS_HAVE_NO_MEMORY(reply);
450         req->out.info = reply;
451         tmp_p_info_type = talloc(mem_ctx, enum drsuapi_DsReplicaInfoType);
452         NT_STATUS_HAVE_NO_MEMORY(tmp_p_info_type);
453         *tmp_p_info_type = info_type;
454         req->out.info_type = tmp_p_info_type;
455
456         /* Based on the infoType requested, retrieve the corresponding
457          * information and construct the response message */
458         switch (info_type) {
459
460         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS:
461                 if (object_dn != NULL) { /* ncs := { object_dn } */
462                         nc_list = NULL;
463                         nc_dn = ldb_dn_new(mem_ctx, samdb, object_dn);
464                         nc_list_elem = talloc_zero(mem_ctx, struct ncList);
465                         NT_STATUS_HAVE_NO_MEMORY(nc_list_elem);
466                         nc_list_elem->dn = nc_dn;
467                         DLIST_ADD_END(nc_list, nc_list_elem, struct ncList*);
468
469                 } else {
470                         /* ncs := getNCs() from ldb database.
471                          * getNCs() must return an array containing
472                          * the DSNames of all NCs hosted by this
473                          * server.
474                          */
475                         char *ntds_guid_str = GUID_string(mem_ctx, &service->ntds_guid);
476                         NT_STATUS_HAVE_NO_MEMORY(ntds_guid_str);
477                         status = get_master_ncs(mem_ctx, samdb, ntds_guid_str, &nc_list);
478                         if (!W_ERROR_IS_OK(status)) {
479                                 goto DONE;
480                         }
481                 }
482
483                 status = kccdrs_replica_get_info_neighbours(mem_ctx, samdb, req,
484                                                             reply, base_index,
485                                                             req_src_dsa_guid, nc_list);
486                 break;
487
488         case DRSUAPI_DS_REPLICA_INFO_CURSORS: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_FOR_NC */
489                 status = kccdrs_replica_get_info_cursors(mem_ctx, samdb, req, reply,
490                                                          ldb_dn_new(mem_ctx, samdb, object_dn));
491                 break;
492         case DRSUAPI_DS_REPLICA_INFO_CURSORS2: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_2_FOR_NC */
493                 status = kccdrs_replica_get_info_cursors2(mem_ctx, samdb, req, reply,
494                                                           ldb_dn_new(mem_ctx, samdb, object_dn));
495                 break;
496         case DRSUAPI_DS_REPLICA_INFO_CURSORS3: /* On MS-DRSR it is DS_REPL_INFO_CURSORS_3_FOR_NC */
497         case DRSUAPI_DS_REPLICA_INFO_CURSORS05: /* On MS-DRSR it is DS_REPL_INFO_UPTODATE_VECTOR_V1 */
498         case DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02: /* DS_REPL_INFO_REPSTO */
499         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
500         case DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_OBJ */
501         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA: /* On MS-DRSR it is DS_REPL_INFO_METADATA_FOR_ATTR_VALUE */
502         case DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2: /* On MS-DRSR it is DS_REPL_INFO_METADATA_2_FOR_ATTR_VALUE */
503         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_DSA_CONNECT_FAILURES */
504         case DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES: /* On MS-DRSR it is DS_REPL_INFO_KCC_LINK_FAILURES */
505         case DRSUAPI_DS_REPLICA_INFO_PENDING_OPS: /* On MS-DRSR it is DS_REPL_INFO_PENDING_OPS */
506         case DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04: /* On MS-DRSR it is DS_REPL_INFO_CLIENT_CONTEXTS */
507         case DRSUAPI_DS_REPLICA_INFO_06: /* On MS-DRSR it is DS_REPL_INFO_SERVER_OUTGOING_CALLS */
508         default:
509                 DEBUG(1,(__location__ ": Unsupported DsReplicaGetInfo info_type %u\n",
510                          info_type));
511                 status = WERR_INVALID_LEVEL;
512                 break;
513         }
514
515 DONE:
516         /* put the status on the result field of the reply */
517         req->out.result = status;
518         NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaGetInfo, req);
519         return NT_STATUS_OK;
520 }