s4-drs: Added check for drs-manage-topology to updateRefs.
[samba.git] / source4 / rpc_server / drsuapi / updaterefs.c
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 "rpc_server/drsuapi/dcesrv_drsuapi.h"
26 #include "libcli/security/security.h"
27 #include "auth/session.h"
28 #include "librpc/gen_ndr/ndr_drsuapi.h"
29
30 struct repsTo {
31         uint32_t count;
32         struct repsFromToBlob *r;
33 };
34
35 /*
36   add a replication destination for a given partition GUID
37  */
38 static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
39                             struct ldb_dn *dn, struct repsFromTo1 *dest, 
40                             uint32_t options)
41 {
42         struct repsTo reps;
43         WERROR werr;
44         unsigned int i;
45
46         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
47         if (!W_ERROR_IS_OK(werr)) {
48                 return werr;
49         }
50
51         for (i=0; i<reps.count; i++) {
52                 if (GUID_compare(&dest->source_dsa_obj_guid, 
53                                  &reps.r[i].ctr.ctr1.source_dsa_obj_guid) == 0) {
54                         if (options & DRSUAPI_DRS_GETCHG_CHECK) {
55                                 return WERR_OK;
56                         } else {
57                                 return WERR_DS_DRA_REF_ALREADY_EXISTS;
58                         }
59                 }
60         }
61
62         reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
63         if (reps.r == NULL) {
64                 return WERR_DS_DRA_INTERNAL_ERROR;
65         }
66         ZERO_STRUCT(reps.r[reps.count]);
67         reps.r[reps.count].version = 1;
68         reps.r[reps.count].ctr.ctr1 = *dest;
69         reps.count++;
70
71         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
72         if (!W_ERROR_IS_OK(werr)) {
73                 return werr;
74         }
75
76         return WERR_OK; 
77 }
78
79 /*
80   delete a replication destination for a given partition GUID
81  */
82 static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
83                             struct ldb_dn *dn, struct GUID *dest_guid, 
84                             uint32_t options)
85 {
86         struct repsTo reps;
87         WERROR werr;
88         unsigned int i;
89         bool found = false;
90
91         werr = dsdb_loadreps(sam_ctx, mem_ctx, dn, "repsTo", &reps.r, &reps.count);
92         if (!W_ERROR_IS_OK(werr)) {
93                 return werr;
94         }
95
96         for (i=0; i<reps.count; i++) {
97                 if (GUID_compare(dest_guid, &reps.r[i].ctr.ctr1.source_dsa_obj_guid) == 0) {
98                         if (i+1 < reps.count) {
99                                 memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
100                         }
101                         reps.count--;
102                         found = true;
103                 }
104         }
105
106         werr = dsdb_savereps(sam_ctx, mem_ctx, dn, "repsTo", reps.r, reps.count);
107         if (!W_ERROR_IS_OK(werr)) {
108                 return werr;
109         }
110
111         if (!found &&
112             !(options & DRSUAPI_DRS_GETCHG_CHECK) &&
113             !(options & DRSUAPI_DRS_ADD_REF)) {
114                 return WERR_DS_DRA_REF_NOT_FOUND;
115         }
116
117         return WERR_OK; 
118 }
119
120 /* 
121   drsuapi_DsReplicaUpdateRefs - a non RPC version callable from getncchanges
122 */
123 WERROR drsuapi_UpdateRefs(struct drsuapi_bind_state *b_state, TALLOC_CTX *mem_ctx,
124                           struct drsuapi_DsReplicaUpdateRefsRequest1 *req)
125 {
126         WERROR werr;
127         struct ldb_dn *dn;
128         struct ldb_context *sam_ctx = b_state->sam_ctx_system?b_state->sam_ctx_system:b_state->sam_ctx;
129
130         DEBUG(4,("DsReplicaUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
131                  req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
132                  req->options,
133                  drs_ObjectIdentifier_to_string(mem_ctx, req->naming_context)));
134
135         dn = ldb_dn_new(mem_ctx, sam_ctx, req->naming_context->dn);
136         if (dn == NULL) {
137                 return WERR_DS_INVALID_DN_SYNTAX;
138         }
139
140         if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
141                 DEBUG(0,(__location__ ": Failed to start transaction on samdb: %s\n",
142                          ldb_errstring(sam_ctx)));
143                 return WERR_DS_DRA_INTERNAL_ERROR;              
144         }
145
146         if (req->options & DRSUAPI_DRS_DEL_REF) {
147                 werr = uref_del_dest(sam_ctx, mem_ctx, dn, &req->dest_dsa_guid, req->options);
148                 if (!W_ERROR_IS_OK(werr)) {
149                         DEBUG(0,("Failed to delete repsTo for %s: %s\n",
150                                  GUID_string(mem_ctx, &req->dest_dsa_guid),
151                                  win_errstr(werr)));
152                         goto failed;
153                 }
154         }
155
156         if (req->options & DRSUAPI_DRS_ADD_REF) {
157                 struct repsFromTo1 dest;
158                 struct repsFromTo1OtherInfo oi;
159                 
160                 ZERO_STRUCT(dest);
161                 ZERO_STRUCT(oi);
162
163                 oi.dns_name = req->dest_dsa_dns_name;
164                 dest.other_info          = &oi;
165                 dest.source_dsa_obj_guid = req->dest_dsa_guid;
166                 dest.replica_flags       = req->options;
167
168                 werr = uref_add_dest(sam_ctx, mem_ctx, dn, &dest, req->options);
169                 if (!W_ERROR_IS_OK(werr)) {
170                         DEBUG(0,("Failed to add repsTo for %s: %s\n",
171                                  GUID_string(mem_ctx, &dest.source_dsa_obj_guid),
172                                  win_errstr(werr)));
173                         goto failed;
174                 }
175         }
176
177         if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
178                 DEBUG(0,(__location__ ": Failed to commit transaction on samdb: %s\n",
179                          ldb_errstring(sam_ctx)));
180                 return WERR_DS_DRA_INTERNAL_ERROR;              
181         }
182
183         return WERR_OK;
184
185 failed:
186         ldb_transaction_cancel(sam_ctx);
187         return werr;
188 }
189
190 /* 
191   drsuapi_DsReplicaUpdateRefs
192 */
193 WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
194                                           struct drsuapi_DsReplicaUpdateRefs *r)
195 {
196         struct dcesrv_handle *h;
197         struct drsuapi_bind_state *b_state;
198         struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
199         WERROR werr;
200         int ret;
201         enum security_user_level security_level;
202
203         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
204         b_state = h->data;
205
206         if (r->in.level != 1) {
207                 DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
208                 return WERR_DS_DRA_INVALID_PARAMETER;
209         }
210         req = &r->in.req.req1;
211         werr = drs_security_access_check(b_state->sam_ctx,
212                                          mem_ctx,
213                                          dce_call->conn->auth_state.session_info->security_token,
214                                          req->naming_context,
215                                          GUID_DRS_MANAGE_TOPOLOGY);
216
217         if (!W_ERROR_IS_OK(werr)) {
218                 return werr;
219         }
220
221         security_level = security_session_user_level(dce_call->conn->auth_state.session_info, NULL);
222         if (security_level < SECURITY_ADMINISTRATOR) {
223                 /* check that they are using an DSA objectGUID that they own */
224                 ret = dsdb_validate_dsa_guid(b_state->sam_ctx,
225                                              &req->dest_dsa_guid,
226                                              &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]);
227                 if (ret != LDB_SUCCESS) {
228                         DEBUG(0,(__location__ ": Refusing DsReplicaUpdateRefs for sid %s with GUID %s\n",
229                                  dom_sid_string(mem_ctx,
230                                                 &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]),
231                                  GUID_string(mem_ctx, &req->dest_dsa_guid)));
232                         return WERR_DS_DRA_ACCESS_DENIED;
233                 }
234         }
235
236         werr = drsuapi_UpdateRefs(b_state, mem_ctx, req);
237
238 #if 0
239         NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsReplicaUpdateRefs, NDR_BOTH, r);
240 #endif
241
242         return werr;
243 }