Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : implement the DRSUpdateRefs call
5 :
6 : Copyright (C) Andrew Tridgell 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 : #include "includes.h"
23 : #include "rpc_server/dcerpc_server.h"
24 : #include "dsdb/samdb/samdb.h"
25 : #include "libcli/security/security.h"
26 : #include "libcli/security/session.h"
27 : #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
28 : #include "auth/session.h"
29 : #include "librpc/gen_ndr/ndr_drsuapi.h"
30 : #include "librpc/gen_ndr/ndr_irpc_c.h"
31 : #include "lib/messaging/irpc.h"
32 :
33 : #undef DBGC_CLASS
34 : #define DBGC_CLASS DBGC_DRS_REPL
35 :
36 : struct repsTo {
37 : uint32_t count;
38 : struct repsFromToBlob *r;
39 : };
40 :
41 2509 : static WERROR uref_check_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
42 : struct ldb_dn *dn, struct GUID *dest_guid,
43 : uint32_t options)
44 : {
45 0 : struct repsTo reps;
46 0 : WERROR werr;
47 0 : unsigned int i;
48 2509 : bool found = false;
49 :
50 2509 : werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
51 2509 : if (!W_ERROR_IS_OK(werr)) {
52 0 : return werr;
53 : }
54 :
55 4049 : for (i=0; i<reps.count; i++) {
56 3792 : if (GUID_equal(dest_guid,
57 3792 : &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
58 2252 : found = true;
59 2252 : break;
60 : }
61 : }
62 :
63 2509 : if (options & DRSUAPI_DRS_ADD_REF) {
64 2506 : if (found && !(options & DRSUAPI_DRS_DEL_REF)) {
65 877 : return WERR_DS_DRA_REF_ALREADY_EXISTS;
66 : }
67 : }
68 :
69 1632 : if (options & DRSUAPI_DRS_DEL_REF) {
70 1617 : if (!found && !(options & DRSUAPI_DRS_ADD_REF)) {
71 1 : return WERR_DS_DRA_REF_NOT_FOUND;
72 : }
73 : }
74 :
75 1631 : return WERR_OK;
76 : }
77 :
78 : /*
79 : add a replication destination for a given partition GUID
80 : */
81 1629 : static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
82 : struct ldb_dn *dn, struct repsFromTo1 *dest,
83 : uint32_t options)
84 : {
85 0 : struct repsTo reps;
86 0 : WERROR werr;
87 0 : unsigned int i;
88 :
89 1629 : werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
90 1629 : if (!W_ERROR_IS_OK(werr)) {
91 0 : return werr;
92 : }
93 :
94 2793 : for (i=0; i<reps.count; i++) {
95 1164 : if (GUID_equal(&dest->source_dsa_obj_guid,
96 1164 : &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
97 0 : if (options & DRSUAPI_DRS_GETCHG_CHECK) {
98 0 : return WERR_OK;
99 : } else {
100 0 : return WERR_DS_DRA_REF_ALREADY_EXISTS;
101 : }
102 : }
103 : }
104 :
105 1629 : reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
106 1629 : if (reps.r == NULL) {
107 0 : return WERR_DS_DRA_INTERNAL_ERROR;
108 : }
109 1629 : ZERO_STRUCT(reps.r[reps.count]);
110 1629 : reps.r[reps.count].version = 1;
111 1629 : reps.r[reps.count].ctr.ctr1 = *dest;
112 : /* add the GCSPN flag if the client asked for it */
113 1629 : reps.r[reps.count].ctr.ctr1.replica_flags |= (options & DRSUAPI_DRS_REF_GCSPN);
114 1629 : reps.count++;
115 :
116 1629 : werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
117 1629 : if (!W_ERROR_IS_OK(werr)) {
118 0 : return werr;
119 : }
120 :
121 1629 : return WERR_OK;
122 : }
123 :
124 : /*
125 : delete a replication destination for a given partition GUID
126 : */
127 1616 : static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
128 : struct ldb_dn *dn, struct GUID *dest_guid,
129 : uint32_t options)
130 : {
131 0 : struct repsTo reps;
132 0 : WERROR werr;
133 0 : unsigned int i;
134 1616 : bool found = false;
135 :
136 1616 : werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
137 1616 : if (!W_ERROR_IS_OK(werr)) {
138 0 : return werr;
139 : }
140 :
141 4155 : for (i=0; i<reps.count; i++) {
142 2539 : if (GUID_equal(dest_guid,
143 2539 : &reps.r[i].ctr.ctr1.source_dsa_obj_guid)) {
144 1375 : if (i+1 < reps.count) {
145 73 : memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
146 : }
147 : /*
148 : * If we remove an element, decrement i so that we don't
149 : * skip over the element following.
150 : */
151 1375 : i--;
152 1375 : reps.count--;
153 1375 : found = true;
154 : }
155 : }
156 :
157 1616 : werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
158 1616 : if (!W_ERROR_IS_OK(werr)) {
159 0 : return werr;
160 : }
161 :
162 1616 : if (!found &&
163 241 : !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
164 241 : !(options & DRSUAPI_DRS_ADD_REF)) {
165 0 : return WERR_DS_DRA_REF_NOT_FOUND;
166 : }
167 :
168 1616 : return WERR_OK;
169 : }
170 :
171 : struct drepl_refresh_state {
172 : struct dreplsrv_refresh r;
173 : };
174 :
175 : /**
176 : * @brief Update the references for the given NC and the destination DSA object
177 : *
178 : * This function is callable from non RPC functions (ie. getncchanges), it
179 : * will validate the request to update reference and then will add/del a repsTo
180 : * to the specified server referenced by its DSA GUID in the request.
181 : *
182 : * @param[in] msg_ctx Messaging context for sending partition
183 : * refresh in dreplsrv
184 : *
185 : * @param[in] b_state A bind_state object
186 : *
187 : * @param[in] mem_ctx A talloc context for memory allocation
188 : *
189 : * @param[in] req A drsuapi_DsReplicaUpdateRefsRequest1
190 : * object which NC, which server and which
191 : * action (add/delete) should be performed
192 : *
193 : * @return WERR_OK is success, different error
194 : * otherwise.
195 : */
196 2509 : WERROR drsuapi_UpdateRefs(struct imessaging_context *msg_ctx,
197 : struct tevent_context *event_ctx,
198 : struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
199 : struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
200 : {
201 0 : WERROR werr;
202 0 : int ret;
203 0 : struct ldb_dn *dn_normalised;
204 0 : struct ldb_dn *nc_root;
205 2509 : struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
206 0 : struct dcerpc_binding_handle *irpc_handle;
207 0 : struct tevent_req *subreq;
208 0 : struct drepl_refresh_state *state;
209 :
210 :
211 2509 : DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
212 : req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
213 : req->options,
214 : drs_ObjectIdentifier_to_debug_string(mem_ctx, req->naming_context)));
215 :
216 : /*
217 : * 4.1.26.2 Server Behavior of the IDL_DRSUpdateRefs Method
218 : * Implements the input validation checks
219 : */
220 2509 : if (GUID_all_zero(&req->dest_dsa_guid)) {
221 0 : return WERR_DS_DRA_INVALID_PARAMETER;
222 : }
223 :
224 : /* FIXME it seems that we should check the length of the stuff too*/
225 2509 : if (req->dest_dsa_dns_name == NULL) {
226 0 : return WERR_DS_DRA_INVALID_PARAMETER;
227 : }
228 :
229 2509 : if (!(req->options & (DRSUAPI_DRS_DEL_REF|DRSUAPI_DRS_ADD_REF))) {
230 0 : return WERR_DS_DRA_INVALID_PARAMETER;
231 : }
232 :
233 2509 : ret = drs_ObjectIdentifier_to_dn_and_nc_root(mem_ctx, sam_ctx, req->naming_context,
234 : &dn_normalised, &nc_root);
235 2509 : if (ret != LDB_SUCCESS) {
236 0 : DBG_WARNING("Didn't find a nc for %s: %s\n",
237 : drs_ObjectIdentifier_to_debug_string(mem_ctx,
238 : req->naming_context),
239 : ldb_errstring(sam_ctx));
240 0 : return WERR_DS_DRA_BAD_NC;
241 : }
242 2509 : if (ldb_dn_compare(dn_normalised, nc_root) != 0) {
243 0 : DBG_NOTICE("dn %s is not equal to %s (from %s)\n",
244 : ldb_dn_get_linearized(dn_normalised),
245 : ldb_dn_get_linearized(nc_root),
246 : drs_ObjectIdentifier_to_debug_string(mem_ctx,
247 : req->naming_context));
248 0 : return WERR_DS_DRA_BAD_NC;
249 : }
250 :
251 : /*
252 : * First check without a transaction open.
253 : *
254 : * This means that in the usual case, it will never open it and never
255 : * bother to refresh the dreplsrv.
256 : */
257 2509 : werr = uref_check_dest(sam_ctx,
258 : mem_ctx,
259 : dn_normalised,
260 : &req->dest_dsa_guid,
261 : req->options);
262 2509 : if (W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_ALREADY_EXISTS) ||
263 1632 : W_ERROR_EQUAL(werr, WERR_DS_DRA_REF_NOT_FOUND)) {
264 878 : if (req->options & DRSUAPI_DRS_GETCHG_CHECK) {
265 876 : return WERR_OK;
266 : }
267 2 : return werr;
268 : }
269 :
270 1631 : if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
271 0 : DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
272 : ldb_errstring(sam_ctx)));
273 0 : return WERR_DS_DRA_INTERNAL_ERROR;
274 : }
275 :
276 1631 : if (req->options & DRSUAPI_DRS_DEL_REF) {
277 1616 : werr = uref_del_dest(sam_ctx,
278 : mem_ctx,
279 : dn_normalised,
280 : &req->dest_dsa_guid,
281 : req->options);
282 1616 : if (!W_ERROR_IS_OK(werr)) {
283 0 : DEBUG(0,("Failed to delete repsTo for %s: %s\n",
284 : GUID_string(mem_ctx, &req->dest_dsa_guid),
285 : win_errstr(werr)));
286 0 : goto failed;
287 : }
288 : }
289 :
290 1631 : if (req->options & DRSUAPI_DRS_ADD_REF) {
291 0 : struct repsFromTo1 dest;
292 0 : struct repsFromTo1OtherInfo oi;
293 :
294 1629 : ZERO_STRUCT(dest);
295 1629 : ZERO_STRUCT(oi);
296 :
297 1629 : oi.dns_name = req->dest_dsa_dns_name;
298 1629 : dest.other_info = &oi;
299 1629 : dest.source_dsa_obj_guid = req->dest_dsa_guid;
300 1629 : dest.replica_flags = req->options;
301 :
302 1629 : werr = uref_add_dest(sam_ctx,
303 : mem_ctx,
304 : dn_normalised,
305 : &dest,
306 : req->options);
307 1629 : if (!W_ERROR_IS_OK(werr)) {
308 0 : DEBUG(0,("Failed to add repsTo for %s: %s\n",
309 : GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
310 : win_errstr(werr)));
311 0 : goto failed;
312 : }
313 : }
314 :
315 1631 : if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
316 0 : DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
317 : ldb_errstring(sam_ctx)));
318 0 : return WERR_DS_DRA_INTERNAL_ERROR;
319 : }
320 :
321 1631 : state = talloc_zero(mem_ctx, struct drepl_refresh_state);
322 1631 : if (state == NULL) {
323 0 : return WERR_OK;
324 : }
325 :
326 1631 : irpc_handle = irpc_binding_handle_by_name(mem_ctx, msg_ctx,
327 : "dreplsrv", &ndr_table_irpc);
328 1631 : if (irpc_handle == NULL) {
329 : /* dreplsrv is not running yet */
330 0 : TALLOC_FREE(state);
331 0 : return WERR_OK;
332 : }
333 :
334 : /*
335 : * [Taken from auth_sam_trigger_repl_secret in auth_sam.c]
336 : *
337 : * This seem to rely on the current IRPC implementation,
338 : * which delivers the message in the _send function.
339 : *
340 : * TODO: we need a ONE_WAY IRPC handle and register
341 : * a callback and wait for it to be triggered!
342 : */
343 1631 : subreq = dcerpc_dreplsrv_refresh_r_send(state, event_ctx,
344 : irpc_handle, &state->r);
345 1631 : TALLOC_FREE(subreq);
346 1631 : TALLOC_FREE(state);
347 :
348 1631 : return WERR_OK;
349 :
350 0 : failed:
351 0 : ldb_transaction_cancel(sam_ctx);
352 0 : return werr;
353 : }
354 :
355 : /*
356 : drsuapi_DsReplicaUpdateRefs
357 : */
358 1619 : WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
359 : struct drsuapi_DsReplicaUpdateRefs *r)
360 : {
361 0 : struct auth_session_info *session_info =
362 1619 : dcesrv_call_session_info(dce_call);
363 0 : struct imessaging_context *imsg_ctx =
364 1619 : dcesrv_imessaging_context(dce_call->conn);
365 0 : struct dcesrv_handle *h;
366 0 : struct drsuapi_bind_state *b_state;
367 0 : struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
368 0 : WERROR werr;
369 0 : int ret;
370 0 : enum security_user_level security_level;
371 :
372 1619 : DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
373 1619 : b_state = h->data;
374 :
375 1619 : if (r->in.level != 1) {
376 0 : DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
377 0 : return WERR_DS_DRA_INVALID_PARAMETER;
378 : }
379 1619 : req = &r->in.req.req1;
380 1619 : werr = drs_security_access_check(b_state->sam_ctx,
381 : mem_ctx,
382 : session_info->security_token,
383 : req->naming_context,
384 : GUID_DRS_MANAGE_TOPOLOGY);
385 :
386 1619 : if (!W_ERROR_IS_OK(werr)) {
387 0 : return werr;
388 : }
389 :
390 1619 : security_level = security_session_user_level(session_info, NULL);
391 1619 : if (security_level < SECURITY_ADMINISTRATOR) {
392 : /* check that they are using an DSA objectGUID that they own */
393 1405 : ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
394 1405 : &req->dest_dsa_guid,
395 1405 : &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
396 1405 : if (ret != LDB_SUCCESS) {
397 0 : DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
398 : dom_sid_string(mem_ctx,
399 : &session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
400 : GUID_string(mem_ctx, &req->dest_dsa_guid)));
401 0 : return WERR_DS_DRA_ACCESS_DENIED;
402 : }
403 : }
404 :
405 1619 : werr = drsuapi_UpdateRefs(imsg_ctx,
406 : dce_call->event_ctx,
407 : b_state,
408 : mem_ctx,
409 : req);
410 :
411 : #if 0
412 : NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);
413 : #endif
414 :
415 1619 : return werr;
416 : }
|