cc41db13600092bd5b4538b94eeb16552cdc5a16
[metze/samba/wip.git] / source4 / dsdb / repl / drepl_ridalloc.c
1 /*
2    Unix SMB/CIFS mplementation.
3
4    DSDB replication service - RID allocation code
5
6    Copyright (C) Andrew Tridgell 2010
7    Copyright (C) Andrew Bartlett 2010
8
9    based on drepl_notify.c
10
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.
15
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.
20
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/>.
23
24 */
25
26 #include "includes.h"
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"
32
33
34 /*
35   create the role owner source dsa structure
36
37   nc_dn: the DN of the subtree being replicated
38   source_dsa_dn: the DN of the server that we are replicating from
39  */
40 WERROR drepl_create_role_owner_source_dsa(struct dreplsrv_service *service,
41                                           struct ldb_dn *nc_dn,
42                                           struct ldb_dn *source_dsa_dn,
43                                           struct dreplsrv_partition_source_dsa **_sdsa)
44 {
45         struct dreplsrv_partition_source_dsa *sdsa;
46         struct ldb_context *ldb = service->samdb;
47         int ret;
48         WERROR werr;
49
50         sdsa = talloc_zero(service, struct dreplsrv_partition_source_dsa);
51         W_ERROR_HAVE_NO_MEMORY(sdsa);
52
53         sdsa->partition = talloc_zero(sdsa, struct dreplsrv_partition);
54         if (!sdsa->partition) {
55                 talloc_free(sdsa);
56                 return WERR_NOMEM;
57         }
58
59         sdsa->partition->dn = ldb_dn_copy(sdsa->partition, nc_dn);
60         if (!sdsa->partition->dn) {
61                 talloc_free(sdsa);
62                 return WERR_NOMEM;
63         }
64         sdsa->partition->nc.dn = ldb_dn_alloc_linearized(sdsa->partition, nc_dn);
65         if (!sdsa->partition->nc.dn) {
66                 talloc_free(sdsa);
67                 return WERR_NOMEM;
68         }
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)));
73                 talloc_free(sdsa);
74                 return WERR_DS_DRA_INTERNAL_ERROR;
75         }
76
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)));
82                 talloc_free(sdsa);
83                 return WERR_DS_DRA_INTERNAL_ERROR;
84         }
85
86         sdsa->repsFrom1->other_info = talloc_zero(sdsa, struct repsFromTo1OtherInfo);
87         if (!sdsa->repsFrom1->other_info) {
88                 talloc_free(sdsa);
89                 return WERR_NOMEM;
90         }
91
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) {
97                 talloc_free(sdsa);
98                 return WERR_NOMEM;
99         }
100
101
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)));
106                 talloc_free(sdsa);
107                 return werr;
108         }
109
110         *_sdsa = sdsa;
111         return WERR_OK;
112 }
113
114 struct extended_op_data {
115         dreplsrv_fsmo_callback_t callback;
116         void *callback_data;
117         struct dreplsrv_partition_source_dsa *sdsa;
118 };
119
120 /*
121   called when an extended op finishes
122  */
123 static void extended_op_callback(struct dreplsrv_service *service,
124                                  WERROR err,
125                                  enum drsuapi_DsExtendedError exop_error,
126                                  void *cb_data)
127 {
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);
131         talloc_free(data);
132 }
133
134 /*
135   schedule a getncchanges request to the role owner for an extended operation
136  */
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,
141                                  uint64_t fsmo_info,
142                                  dreplsrv_fsmo_callback_t callback,
143                                  void *callback_data)
144 {
145         WERROR werr;
146         struct extended_op_data *data;
147         struct dreplsrv_partition_source_dsa *sdsa;
148
149         werr = drepl_create_role_owner_source_dsa(service, role_owner_dn, fsmo_role_dn, &sdsa);
150         W_ERROR_NOT_OK_RETURN(werr);
151
152         data = talloc(service, struct extended_op_data);
153         W_ERROR_HAVE_NO_MEMORY(data);
154
155         data->callback = callback;
156         data->callback_data = callback_data;
157         data->sdsa = sdsa;
158
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)) {
163                 talloc_free(sdsa);
164                 talloc_free(data);
165         }
166         return werr;
167 }
168
169 /*
170   called when a rid allocation request has completed
171  */
172 static void drepl_new_rid_pool_callback(struct dreplsrv_service *service,
173                                         WERROR werr,
174                                         enum drsuapi_DsExtendedError ext_err,
175                                         void *cb_data)
176 {
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));
180         } else {
181                 DEBUG(3,(__location__ ": RID Manager completed RID allocation OK\n"));
182         }
183
184         service->rid_alloc_in_progress = false;
185 }
186
187 /*
188   schedule a getncchanges request to the RID Manager to ask for a new
189   set of RIDs using DRSUAPI_EXOP_FSMO_RID_ALLOC
190  */
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,
193                                          uint64_t alloc_pool)
194 {
195         WERROR werr = drepl_request_extended_op(service,
196                                                 rid_manager_dn,
197                                                 fsmo_role_dn,
198                                                 DRSUAPI_EXOP_FSMO_RID_ALLOC,
199                                                 alloc_pool,
200                                                 drepl_new_rid_pool_callback, NULL);
201         if (W_ERROR_IS_OK(werr)) {
202                 service->rid_alloc_in_progress = true;
203         }
204         return werr;
205 }
206
207
208 /*
209   see if we are on the last pool we have
210  */
211 static int drepl_ridalloc_pool_exhausted(struct ldb_context *ldb,
212                                          bool *exhausted,
213                                          uint64_t *_alloc_pool)
214 {
215         struct ldb_dn *server_dn, *machine_dn, *rid_set_dn;
216         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
217         uint64_t alloc_pool;
218         uint64_t prev_pool;
219         uint32_t prev_pool_lo, prev_pool_hi;
220         uint32_t next_rid;
221         static const char * const attrs[] = {
222                 "rIDAllocationPool",
223                 "rIDPreviousAllocationPool",
224                 "rIDNextRid",
225                 NULL
226         };
227         int ret;
228         struct ldb_result *res;
229
230         *exhausted = false;
231         *_alloc_pool = UINT64_MAX;
232
233         server_dn = ldb_dn_get_parent(tmp_ctx, samdb_ntds_settings_dn(ldb));
234         if (!server_dn) {
235                 talloc_free(tmp_ctx);
236                 return ldb_operr(ldb);
237         }
238
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);
244                 return ret;
245         }
246
247         ret = samdb_reference_dn(ldb, tmp_ctx, machine_dn, "rIDSetReferences", &rid_set_dn);
248         if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
249                 *exhausted = true;
250                 *_alloc_pool = 0;
251                 talloc_free(tmp_ctx);
252                 return LDB_SUCCESS;
253         }
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);
258                 return ret;
259         }
260
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);
266                 return ret;
267         }
268
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);
274
275         if (alloc_pool != prev_pool) {
276                 talloc_free(tmp_ctx);
277                 return LDB_SUCCESS;
278         }
279
280         if (next_rid < (prev_pool_hi + prev_pool_lo)/2) {
281                 talloc_free(tmp_ctx);
282                 return LDB_SUCCESS;
283         }
284
285         *exhausted = true;
286         *_alloc_pool = alloc_pool;
287         talloc_free(tmp_ctx);
288         return LDB_SUCCESS;
289 }
290
291
292 /*
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
295   to the RID Manager
296  */
297 WERROR dreplsrv_ridalloc_check_rid_pool(struct dreplsrv_service *service)
298 {
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;
302         bool exhausted;
303         WERROR werr;
304         int ret;
305         uint64_t alloc_pool;
306
307         if (service->rid_alloc_in_progress) {
308                 talloc_free(tmp_ctx);
309                 return WERR_OK;
310         }
311
312         /*
313           steps:
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
319               nothing to do
320             - schedule a getncchanges with DRSUAPI_EXOP_FSMO_RID_ALLOC
321               to the RID Manager
322          */
323
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;
330         }
331
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;
339         }
340
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);
345                 return WERR_OK;
346         }
347
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;
352         }
353
354         if (!exhausted) {
355                 /* don't need a new pool */
356                 talloc_free(tmp_ctx);
357                 return WERR_OK;
358         }
359
360         DEBUG(2,(__location__ ": Requesting more RIDs from RID Manager\n"));
361
362         werr = drepl_request_new_rid_pool(service, rid_manager_dn, fsmo_role_dn, alloc_pool);
363         talloc_free(tmp_ctx);
364         return werr;
365 }
366
367 /* called by the samldb ldb module to tell us to ask for a new RID
368    pool */
369 void dreplsrv_allocate_rid(struct messaging_context *msg, void *private_data,
370                            uint32_t msg_type,
371                            struct server_id server_id, DATA_BLOB *data)
372 {
373         struct dreplsrv_service *service = talloc_get_type(private_data, struct dreplsrv_service);
374         dreplsrv_ridalloc_check_rid_pool(service);
375 }