s4: fixed format of repsTo in samdb
[amitay/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 "librpc/gen_ndr/ndr_drsuapi.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "rpc_server/common/common.h"
26 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
27 #include "dsdb/samdb/samdb.h"
28 #include "lib/ldb/include/ldb_errors.h"
29 #include "param/param.h"
30 #include "librpc/gen_ndr/ndr_drsblobs.h"
31 #include "auth/auth.h"
32
33 struct repsTo {
34         uint32_t count;
35         struct repsFromToBlob *r;
36 };
37
38 /*
39   load the repsTo structure for a given partition GUID
40  */
41 static WERROR uref_loadreps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct GUID *guid,
42                             struct repsTo *reps)
43 {
44         struct ldb_dn *dn;
45         const char *attrs[] = { "repsTo", NULL };
46         struct ldb_result *res;
47         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
48         int i;
49         struct ldb_message_element *el;
50
51         if (dsdb_find_dn_by_guid(sam_ctx, tmp_ctx, GUID_string(tmp_ctx, guid), &dn) != LDB_SUCCESS) {
52                 DEBUG(0,("drsuapi_addref: failed to find partition with GUID %s\n",
53                          GUID_string(tmp_ctx, guid)));
54                 talloc_free(tmp_ctx);
55                 return WERR_DS_DRA_BAD_NC;
56         }
57
58         /* TODO: possibly check in the rootDSE to see that this DN is
59          * one of our partition roots */         
60
61         if (ldb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, NULL) != LDB_SUCCESS) {
62                 DEBUG(0,("drsuapi_addref: failed to read partition object\n"));
63                 talloc_free(tmp_ctx);
64                 return WERR_DS_DRA_INTERNAL_ERROR;
65         }
66
67         ZERO_STRUCTP(reps);
68
69         el = ldb_msg_find_element(res->msgs[0], "repsTo");
70         if (el == NULL) {
71                 talloc_free(tmp_ctx);
72                 return WERR_OK;
73         }
74
75         reps->count = el->num_values;
76         reps->r = talloc_array(mem_ctx, struct repsFromToBlob, reps->count);
77         if (reps->r == NULL) {
78                 talloc_free(tmp_ctx);
79                 return WERR_DS_DRA_INTERNAL_ERROR;
80         }
81
82         for (i=0; i<reps->count; i++) {
83                 enum ndr_err_code ndr_err;
84                 ndr_err = ndr_pull_struct_blob(&el->values[i], 
85                                                mem_ctx, lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")),
86                                                &reps->r[i], 
87                                                (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
88                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
89                         talloc_free(tmp_ctx);
90                         return WERR_DS_DRA_INTERNAL_ERROR;
91                 }
92         }
93
94         talloc_free(tmp_ctx);
95         
96         return WERR_OK;
97 }
98
99 /*
100   save the repsTo structure for a given partition GUID
101  */
102 static WERROR uref_savereps(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct GUID *guid,
103                             struct repsTo *reps)
104 {
105         struct ldb_dn *dn;
106         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
107         struct ldb_message *msg;
108         struct ldb_message_element *el;
109         int i;
110
111         if (dsdb_find_dn_by_guid(sam_ctx, tmp_ctx, GUID_string(tmp_ctx, guid), &dn) != LDB_SUCCESS) {
112                 DEBUG(0,("drsuapi_addref: failed to find partition with GUID %s\n",
113                          GUID_string(tmp_ctx, guid)));
114                 talloc_free(tmp_ctx);
115                 return WERR_DS_DRA_BAD_NC;
116         }
117
118         msg = ldb_msg_new(tmp_ctx);
119         msg->dn = dn;
120         if (ldb_msg_add_empty(msg, "repsTo", LDB_FLAG_MOD_REPLACE, &el) != LDB_SUCCESS) {
121                 goto failed;
122         }
123
124         el->values = talloc_array(msg, struct ldb_val, reps->count);
125         if (!el->values) {
126                 goto failed;
127         }
128
129         for (i=0; i<reps->count; i++) {
130                 struct ldb_val v;
131                 enum ndr_err_code ndr_err;
132
133                 ndr_err = ndr_push_struct_blob(&v, tmp_ctx, lp_iconv_convenience(ldb_get_opaque(sam_ctx, "loadparm")),
134                                                &reps->r[i], 
135                                                (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
136                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137                         goto failed;
138                 }
139
140                 el->num_values++;
141                 el->values[i] = v;
142         }
143
144         if (ldb_modify(sam_ctx, msg) != LDB_SUCCESS) {
145                 DEBUG(0,("Failed to store repsTo - %s\n", ldb_errstring(sam_ctx)));
146                 goto failed;
147         }
148
149         talloc_free(tmp_ctx);
150         
151         return WERR_OK;
152
153 failed:
154         talloc_free(tmp_ctx);
155         return WERR_DS_DRA_INTERNAL_ERROR;
156 }
157
158 /*
159   add a replication destination for a given partition GUID
160  */
161 static WERROR uref_add_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
162                             struct GUID *guid, struct repsFromTo1 *dest)
163 {
164         struct repsTo reps;
165         WERROR werr;
166
167         werr = uref_loadreps(sam_ctx, mem_ctx, guid, &reps);
168         if (!W_ERROR_IS_OK(werr)) {
169                 return werr;
170         }
171
172         reps.r = talloc_realloc(mem_ctx, reps.r, struct repsFromToBlob, reps.count+1);
173         if (reps.r == NULL) {
174                 return WERR_DS_DRA_INTERNAL_ERROR;
175         }
176         ZERO_STRUCT(reps.r[reps.count]);
177         reps.r[reps.count].version = 1;
178         reps.r[reps.count].ctr.ctr1 = *dest;
179         reps.count++;
180
181         werr = uref_savereps(sam_ctx, mem_ctx, guid, &reps);
182         if (!W_ERROR_IS_OK(werr)) {
183                 return werr;
184         }
185
186         return WERR_OK; 
187 }
188
189 /*
190   delete a replication destination for a given partition GUID
191  */
192 static WERROR uref_del_dest(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 
193                             struct GUID *guid, struct GUID *dest_guid)
194 {
195         struct repsTo reps;
196         WERROR werr;
197         int i;
198
199         werr = uref_loadreps(sam_ctx, mem_ctx, guid, &reps);
200         if (!W_ERROR_IS_OK(werr)) {
201                 return werr;
202         }
203
204         for (i=0; i<reps.count; i++) {
205                 if (GUID_compare(dest_guid, &reps.r[i].ctr.ctr1.source_dsa_obj_guid) == 0) {
206                         if (i+1 < reps.count) {
207                                 memmove(&reps.r[i], &reps.r[i+1], sizeof(reps.r[i])*(reps.count-(i+1)));
208                         }
209                         reps.count--;
210                 }
211         }
212
213         werr = uref_savereps(sam_ctx, mem_ctx, guid, &reps);
214         if (!W_ERROR_IS_OK(werr)) {
215                 return werr;
216         }
217
218         return WERR_OK; 
219 }
220
221 /* 
222   drsuapi_DsReplicaUpdateRefs
223 */
224 WERROR dcesrv_drsuapi_DsReplicaUpdateRefs(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
225                                           struct drsuapi_DsReplicaUpdateRefs *r)
226 {
227         struct drsuapi_DsReplicaUpdateRefsRequest1 *req;
228         struct ldb_context *sam_ctx;
229         WERROR werr;
230
231         if (r->in.level != 1) {
232                 DEBUG(0,("DrReplicUpdateRefs - unsupported level %u\n", r->in.level));
233                 return WERR_DS_DRA_INVALID_PARAMETER;
234         }
235
236         req = &r->in.req.req1;
237         DEBUG(4,("DrReplicUpdateRefs for host '%s' with GUID %s options 0x%08x nc=%s\n",
238                  req->dest_dsa_dns_name, GUID_string(mem_ctx, &req->dest_dsa_guid),
239                  req->options,
240                  drs_ObjectIdentifier_to_string(mem_ctx, req->naming_context)));
241
242         /* TODO: We need to authenticate this operation pretty carefully */
243         sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, 
244                                 system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
245         if (!sam_ctx) {
246                 return WERR_DS_DRA_INTERNAL_ERROR;              
247         }
248
249         if (ldb_transaction_start(sam_ctx) != LDB_SUCCESS) {
250                 DEBUG(0,(__location__ ": Failed to start transaction on samdb\n"));
251                 return WERR_DS_DRA_INTERNAL_ERROR;              
252         }
253
254         if (req->options & DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE) {
255                 werr = uref_del_dest(sam_ctx, mem_ctx, &req->naming_context->guid, &req->dest_dsa_guid);
256                 if (!W_ERROR_IS_OK(werr)) {
257                         DEBUG(0,("Failed to delete repsTo for %s\n",
258                                  GUID_string(dce_call, &req->dest_dsa_guid)));
259                         goto failed;
260                 }
261         }
262
263         if (req->options & DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE) {
264                 struct repsFromTo1 dest;
265                 struct repsFromTo1OtherInfo oi;
266                 
267                 ZERO_STRUCT(dest);
268                 ZERO_STRUCT(oi);
269
270                 oi.dns_name = req->dest_dsa_dns_name;
271                 dest.other_info          = &oi;
272                 dest.source_dsa_obj_guid = req->dest_dsa_guid;
273                 dest.replica_flags       = req->options;
274
275                 werr = uref_add_dest(sam_ctx, mem_ctx, &req->naming_context->guid, &dest);
276                 if (!W_ERROR_IS_OK(werr)) {
277                         DEBUG(0,("Failed to delete repsTo for %s\n",
278                                  GUID_string(dce_call, &dest.source_dsa_obj_guid)));
279                         goto failed;
280                 }
281         }
282
283         if (ldb_transaction_commit(sam_ctx) != LDB_SUCCESS) {
284                 DEBUG(0,(__location__ ": Failed to commit transaction on samdb\n"));
285                 return WERR_DS_DRA_INTERNAL_ERROR;              
286         }
287
288         talloc_free(sam_ctx);
289         return WERR_OK;
290
291 failed:
292         ldb_transaction_cancel(sam_ctx);
293         talloc_free(sam_ctx);
294         return werr;
295 }
296