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