2 Unix SMB/CIFS mplementation.
4 DSDB replication service - RID allocation code
6 Copyright (C) Andrew Tridgell 2010
7 Copyright (C) Andrew Bartlett 2010
9 based on drepl_notify.c
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include "ldb_module.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "smbd/service.h"
30 #include "dsdb/repl/drepl_service.h"
31 #include "param/param.h"
35 create the role owner source dsa structure
37 nc_dn: the DN of the subtree being replicated
38 source_dsa_dn: the DN of the server that we are replicating from
40 WERROR drepl_create_role_owner_source_dsa(struct dreplsrv_service *service,
42 struct ldb_dn *source_dsa_dn,
43 struct dreplsrv_partition_source_dsa **_sdsa)
45 struct dreplsrv_partition_source_dsa *sdsa;
46 struct ldb_context *ldb = service->samdb;
50 sdsa = talloc_zero(service, struct dreplsrv_partition_source_dsa);
51 W_ERROR_HAVE_NO_MEMORY(sdsa);
53 sdsa->partition = talloc_zero(sdsa, struct dreplsrv_partition);
54 if (!sdsa->partition) {
59 sdsa->partition->dn = ldb_dn_copy(sdsa->partition, nc_dn);
60 if (!sdsa->partition->dn) {
64 sdsa->partition->nc.dn = ldb_dn_alloc_linearized(sdsa->partition, nc_dn);
65 if (!sdsa->partition->nc.dn) {
69 ret = dsdb_find_guid_by_dn(ldb, nc_dn, &sdsa->partition->nc.guid);
70 if (ret != LDB_SUCCESS) {
71 DEBUG(0,(__location__ ": Failed to find GUID for %s\n",
72 ldb_dn_get_linearized(nc_dn)));
74 return WERR_DS_DRA_INTERNAL_ERROR;
77 sdsa->repsFrom1 = &sdsa->_repsFromBlob.ctr.ctr1;
78 ret = dsdb_find_guid_by_dn(ldb, source_dsa_dn, &sdsa->repsFrom1->source_dsa_obj_guid);
79 if (ret != LDB_SUCCESS) {
80 DEBUG(0,(__location__ ": Failed to find objectGUID for %s\n",
81 ldb_dn_get_linearized(source_dsa_dn)));
83 return WERR_DS_DRA_INTERNAL_ERROR;
86 sdsa->repsFrom1->other_info = talloc_zero(sdsa, struct repsFromTo1OtherInfo);
87 if (!sdsa->repsFrom1->other_info) {
92 sdsa->repsFrom1->other_info->dns_name =
93 talloc_asprintf(sdsa->repsFrom1->other_info, "%s._msdcs.%s",
94 GUID_string(sdsa->repsFrom1->other_info, &sdsa->repsFrom1->source_dsa_obj_guid),
95 lpcfg_dnsdomain(service->task->lp_ctx));
96 if (!sdsa->repsFrom1->other_info->dns_name) {
102 werr = dreplsrv_out_connection_attach(service, sdsa->repsFrom1, &sdsa->conn);
103 if (!W_ERROR_IS_OK(werr)) {
104 DEBUG(0,(__location__ ": Failed to attach connection to %s\n",
105 ldb_dn_get_linearized(nc_dn)));
114 struct extended_op_data {
115 dreplsrv_fsmo_callback_t callback;
117 struct dreplsrv_partition_source_dsa *sdsa;
121 called when an extended op finishes
123 static void extended_op_callback(struct dreplsrv_service *service,
125 enum drsuapi_DsExtendedError exop_error,
128 struct extended_op_data *data = talloc_get_type_abort(cb_data, struct extended_op_data);
129 talloc_free(data->sdsa);
130 data->callback(service, err, exop_error, data->callback_data);
135 schedule a getncchanges request to the role owner for an extended operation
137 WERROR drepl_request_extended_op(struct dreplsrv_service *service,
138 struct ldb_dn *fsmo_role_dn,
139 struct ldb_dn *role_owner_dn,
140 enum drsuapi_DsExtendedOperation extended_op,
142 dreplsrv_fsmo_callback_t callback,
146 struct extended_op_data *data;
147 struct dreplsrv_partition_source_dsa *sdsa;
149 werr = drepl_create_role_owner_source_dsa(service, role_owner_dn, fsmo_role_dn, &sdsa);
150 W_ERROR_NOT_OK_RETURN(werr);
152 data = talloc(service, struct extended_op_data);
153 W_ERROR_HAVE_NO_MEMORY(data);
155 data->callback = callback;
156 data->callback_data = callback_data;
159 werr = dreplsrv_schedule_partition_pull_source(service, sdsa,
160 extended_op, fsmo_info,
161 extended_op_callback, data);
162 if (!W_ERROR_IS_OK(werr)) {
170 called when a rid allocation request has completed
172 static void drepl_new_rid_pool_callback(struct dreplsrv_service *service,
174 enum drsuapi_DsExtendedError ext_err,
177 if (!W_ERROR_IS_OK(werr)) {
178 DEBUG(0,(__location__ ": RID Manager failed RID allocation - %s - extended_ret[0x%X]\n",
179 win_errstr(werr), ext_err));
181 DEBUG(3,(__location__ ": RID Manager completed RID allocation OK\n"));
184 service->rid_alloc_in_progress = false;
188 schedule a getncchanges request to the RID Manager to ask for a new
189 set of RIDs using DRSUAPI_EXOP_FSMO_RID_ALLOC
191 static WERROR drepl_request_new_rid_pool(struct dreplsrv_service *service,
192 struct ldb_dn *rid_manager_dn, struct ldb_dn *fsmo_role_dn,
195 WERROR werr = drepl_request_extended_op(service,
198 DRSUAPI_EXOP_FSMO_RID_ALLOC,
200 drepl_new_rid_pool_callback, NULL);
201 if (W_ERROR_IS_OK(werr)) {
202 service->rid_alloc_in_progress = true;
209 see if we are on the last pool we have
211 static int drepl_ridalloc_pool_exhausted(struct ldb_context *ldb,
213 uint64_t *_alloc_pool)
215 struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
216 TALLOC_CTX *tmp_ctx = talloc_new(ldb);
219 uint32_t prev_pool_lo, prev_pool_hi;
221 static const char * const attrs[] = {
223 "rIDPreviousAllocationPool",
228 struct ldb_result *res;
231 *_alloc_pool = UINT64_MAX;
233 server_dn = ldb_dn_get_parent(tmp_ctx, samdb_ntds_settings_dn(ldb));
235 talloc_free(tmp_ctx);
236 return ldb_operr(ldb);
239 ret = samdb_reference_dn(ldb, tmp_ctx, server_dn, "serverReference", &machine_dn);
240 if (ret != LDB_SUCCESS) {
241 DEBUG(0,(__location__ ": Failed to find serverReference in %s - %s",
242 ldb_dn_get_linearized(server_dn), ldb_errstring(ldb)));
243 talloc_free(tmp_ctx);
247 ret = samdb_reference_dn(ldb, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
248 if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
251 talloc_free(tmp_ctx);
254 if (ret != LDB_SUCCESS) {
255 DEBUG(0,(__location__ ": Failed to find rIDSetReferences in %s - %s",
256 ldb_dn_get_linearized(machine_dn), ldb_errstring(ldb)));
257 talloc_free(tmp_ctx);
261 ret = ldb_search(ldb, tmp_ctx, &res, rid_set_dn, LDB_SCOPE_BASE, attrs, NULL);
262 if (ret != LDB_SUCCESS) {
263 DEBUG(0,(__location__ ": Failed to load RID Set attrs from %s - %s",
264 ldb_dn_get_linearized(rid_set_dn), ldb_errstring(ldb)));
265 talloc_free(tmp_ctx);
269 alloc_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDAllocationPool", 0);
270 prev_pool = ldb_msg_find_attr_as_uint64(res->msgs[0], "rIDPreviousAllocationPool", 0);
271 prev_pool_lo = prev_pool & 0xFFFFFFFF;
272 prev_pool_hi = prev_pool >> 32;
273 next_rid = ldb_msg_find_attr_as_uint(res->msgs[0], "rIDNextRid", 0);
275 if (alloc_pool != prev_pool) {
276 talloc_free(tmp_ctx);
280 if (next_rid < (prev_pool_hi + prev_pool_lo)/2) {
281 talloc_free(tmp_ctx);
286 *_alloc_pool = alloc_pool;
287 talloc_free(tmp_ctx);
293 see if we are low on RIDs in the RID Set rIDAllocationPool. If we
294 are, then schedule a replication call with DRSUAPI_EXOP_FSMO_RID_ALLOC
297 WERROR dreplsrv_ridalloc_check_rid_pool(struct dreplsrv_service *service)
299 struct ldb_dn *rid_manager_dn, *fsmo_role_dn;
300 TALLOC_CTX *tmp_ctx = talloc_new(service);
301 struct ldb_context *ldb = service->samdb;
307 if (service->rid_alloc_in_progress) {
308 talloc_free(tmp_ctx);
314 - find who the RID Manager is
315 - if we are the RID Manager then nothing to do
316 - find our RID Set object
317 - load rIDAllocationPool and rIDPreviousAllocationPool
318 - if rIDAllocationPool != rIDPreviousAllocationPool then
320 - schedule a getncchanges with DRSUAPI_EXOP_FSMO_RID_ALLOC
324 /* work out who is the RID Manager */
325 ret = samdb_rid_manager_dn(ldb, tmp_ctx, &rid_manager_dn);
326 if (ret != LDB_SUCCESS) {
327 DEBUG(0, (__location__ ": Failed to find RID Manager object - %s", ldb_errstring(ldb)));
328 talloc_free(tmp_ctx);
329 return WERR_DS_DRA_INTERNAL_ERROR;
332 /* find the DN of the RID Manager */
333 ret = samdb_reference_dn(ldb, tmp_ctx, rid_manager_dn, "fSMORoleOwner", &fsmo_role_dn);
334 if (ret != LDB_SUCCESS) {
335 DEBUG(0,(__location__ ": Failed to find fSMORoleOwner in RID Manager object - %s",
336 ldb_errstring(ldb)));
337 talloc_free(tmp_ctx);
338 return WERR_DS_DRA_INTERNAL_ERROR;
341 if (ldb_dn_compare(samdb_ntds_settings_dn(ldb), fsmo_role_dn) == 0) {
342 /* we are the RID Manager - no need to do a
343 DRSUAPI_EXOP_FSMO_RID_ALLOC */
344 talloc_free(tmp_ctx);
348 ret = drepl_ridalloc_pool_exhausted(ldb, &exhausted, &alloc_pool);
349 if (ret != LDB_SUCCESS) {
350 talloc_free(tmp_ctx);
351 return WERR_DS_DRA_INTERNAL_ERROR;
355 /* don't need a new pool */
356 talloc_free(tmp_ctx);
360 DEBUG(2,(__location__ ": Requesting more RIDs from RID Manager\n"));
362 werr = drepl_request_new_rid_pool(service, rid_manager_dn, fsmo_role_dn, alloc_pool);
363 talloc_free(tmp_ctx);
367 /* called by the samldb ldb module to tell us to ask for a new RID
369 void dreplsrv_allocate_rid(struct messaging_context *msg, void *private_data,
371 struct server_id server_id, DATA_BLOB *data)
373 struct dreplsrv_service *service = talloc_get_type(private_data, struct dreplsrv_service);
374 dreplsrv_ridalloc_check_rid_pool(service);