fuzzing: fix fuzz_stable_sort_r_unstable comparison
[samba.git] / source4 / rpc_server / drsuapi / addentry.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    implement the DsAddEntry call
5
6    Copyright (C) Stefan Metzmacher 2009
7    Copyright (C) Andrew Tridgell   2009
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "rpc_server/dcerpc_server.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "dsdb/common/util.h"
27 #include "param/param.h"
28 #include "libcli/security/security.h"
29 #include "libcli/security/session.h"
30 #include "rpc_server/drsuapi/dcesrv_drsuapi.h"
31 #include "librpc/gen_ndr/ndr_drsuapi.h"
32
33 #undef DBGC_CLASS
34 #define DBGC_CLASS            DBGC_DRS_REPL
35
36 /*
37   add special SPNs needed for DRS replication to machine accounts when
38   an AddEntry is done to create a nTDSDSA object
39  */
40 static WERROR drsuapi_add_SPNs(struct drsuapi_bind_state *b_state,
41                                struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
42                                const struct drsuapi_DsReplicaObjectListItem *first_object)
43 {
44         int ret;
45         const struct drsuapi_DsReplicaObjectListItem *obj;
46         const char *attrs[] = { "serverReference", "objectGUID", NULL };
47
48         for (obj = first_object; obj; obj=obj->next_object) {
49                 const char *dn_string = obj->object.identifier->dn;
50                 struct ldb_dn *dn = ldb_dn_new(mem_ctx, b_state->sam_ctx, dn_string);
51                 struct ldb_result *res, *res2;
52                 struct ldb_dn *ref_dn;
53                 struct GUID ntds_guid;
54                 struct ldb_message *msg;
55                 struct ldb_message_element *el;
56                 const char *ntds_guid_str;
57                 const char *dom_string;
58                 const char *attrs2[] = { "dNSHostName", "cn", NULL };
59                 const char *dNSHostName, *cn;
60
61                 DEBUG(6,(__location__ ": Adding SPNs for %s\n", 
62                          ldb_dn_get_linearized(dn)));
63                  
64                 ret = ldb_search(b_state->sam_ctx, mem_ctx, &res,
65                                  dn, LDB_SCOPE_BASE, attrs,
66                                  "(objectClass=ntDSDSA)");
67                 if (ret != LDB_SUCCESS) {
68                         DEBUG(0,(__location__ ": Failed to find dn '%s'\n", dn_string));
69                         return WERR_DS_DRA_INTERNAL_ERROR;
70                 }
71
72                 if (res->count < 1) {
73                         /* we only add SPNs for nTDSDSA objects */
74                         continue;
75                 }
76
77                 ref_dn = samdb_result_dn(b_state->sam_ctx, mem_ctx, res->msgs[0], "serverReference", NULL);
78                 if (ref_dn == NULL) {
79                         /* we only add SPNs for objects with a
80                            serverReference */
81                         continue;
82                 }
83
84                 DEBUG(6,(__location__ ": serverReference %s\n", 
85                          ldb_dn_get_linearized(ref_dn)));
86
87                 ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID");
88
89                 ntds_guid_str = GUID_string(res, &ntds_guid);
90
91                 dom_string = lpcfg_dnsdomain(dce_call->conn->dce_ctx->lp_ctx);
92
93                 /* get the dNSHostName and cn */
94                 ret = ldb_search(b_state->sam_ctx, mem_ctx, &res2,
95                                  ref_dn, LDB_SCOPE_BASE, attrs2, NULL);
96                 if (ret != LDB_SUCCESS) {
97                         DEBUG(0,(__location__ ": Failed to find ref_dn '%s'\n",
98                                  ldb_dn_get_linearized(ref_dn)));
99                         return WERR_DS_DRA_INTERNAL_ERROR;
100                 }
101
102                 dNSHostName = ldb_msg_find_attr_as_string(res2->msgs[0], "dNSHostName", NULL);
103                 cn = ldb_msg_find_attr_as_string(res2->msgs[0], "cn", NULL);
104
105                 /*
106                  * construct a modify request to add the new SPNs to
107                  * the machine account
108                  */
109                 msg = ldb_msg_new(mem_ctx);
110                 if (msg == NULL) {
111                         return WERR_NOT_ENOUGH_MEMORY;
112                 }
113
114                 msg->dn = ref_dn;
115                 ret = ldb_msg_add_empty(msg, "servicePrincipalName",
116                                         LDB_FLAG_MOD_ADD, &el);
117                 if (ret != LDB_SUCCESS) {
118                         return WERR_NOT_ENOUGH_MEMORY;
119                 }
120
121
122                 ldb_msg_add_steal_string(msg, "servicePrincipalName",
123                                          talloc_asprintf(el->values,
124                                                          "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s",
125                                                          ntds_guid_str, dom_string));
126                 ldb_msg_add_steal_string(msg, "servicePrincipalName",
127                                          talloc_asprintf(el->values, "ldap/%s._msdcs.%s",
128                                                          ntds_guid_str, dom_string));
129                 if (cn) {
130                         ldb_msg_add_steal_string(msg, "servicePrincipalName",
131                                                  talloc_asprintf(el->values, "ldap/%s", cn));
132                 }
133                 if (dNSHostName) {
134                         ldb_msg_add_steal_string(msg, "servicePrincipalName",
135                                                  talloc_asprintf(el->values, "ldap/%s", dNSHostName));
136                 }
137                 if (el->num_values < 2) {
138                         return WERR_NOT_ENOUGH_MEMORY;
139                 }
140
141                 ret = dsdb_modify(b_state->sam_ctx, msg, DSDB_MODIFY_PERMISSIVE);
142                 if (ret != LDB_SUCCESS) {
143                         DEBUG(0,(__location__ ": Failed to add SPNs - %s\n",
144                                  ldb_errstring(b_state->sam_ctx)));
145                         return WERR_DS_DRA_INTERNAL_ERROR;
146                 }
147         }
148         
149         return WERR_OK;
150 }
151
152
153
154
155 /* 
156   drsuapi_DsAddEntry
157 */
158 WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
159                                  struct drsuapi_DsAddEntry *r)
160 {
161         WERROR status;
162         struct drsuapi_bind_state *b_state;
163         struct dcesrv_handle *h;
164         uint32_t num = 0;
165         struct drsuapi_DsReplicaObjectIdentifier2 *ids = NULL;
166         int ret;
167         const struct drsuapi_DsReplicaObjectListItem *first_object;
168
169         if (DEBUGLVL(4)) {
170                 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsAddEntry, NDR_IN, r);
171         }
172
173         /* TODO: check which out level the client supports */
174
175         ZERO_STRUCTP(r->out.ctr);
176         *r->out.level_out = 3;
177         r->out.ctr->ctr3.err_ver = 1;
178         r->out.ctr->ctr3.err_data = talloc_zero(mem_ctx, union drsuapi_DsAddEntry_ErrData);
179
180         DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE);
181         b_state = h->data;
182
183         status = drs_security_level_check(dce_call, "DsAddEntry", SECURITY_DOMAIN_CONTROLLER, NULL);
184         if (!W_ERROR_IS_OK(status)) {
185                 return status;
186         }
187
188         switch (r->in.level) {
189         case 2:
190                 ret = ldb_transaction_start(b_state->sam_ctx);
191                 if (ret != LDB_SUCCESS) {
192                         DBG_ERR("DsAddEntry start transaction failed: %s\n",
193                                 ldb_errstring(b_state->sam_ctx));
194                         return WERR_DS_DRA_INTERNAL_ERROR;
195                 }
196
197
198                 first_object = &r->in.req->req2.first_object;
199
200                 status = dsdb_origin_objects_commit(b_state->sam_ctx,
201                                                     mem_ctx,
202                                                     first_object,
203                                                     &num,
204                                                     DSDB_REPL_FLAG_ADD_NCNAME,
205                                                     &ids);
206                 if (!W_ERROR_IS_OK(status)) {
207                         r->out.ctr->ctr3.err_data->v1.status = status;
208                         ldb_transaction_cancel(b_state->sam_ctx);
209                         DEBUG(0,(__location__ ": DsAddEntry failed - %s\n", win_errstr(status)));
210                         return status;
211                 }
212
213                 r->out.ctr->ctr3.count = num;
214                 r->out.ctr->ctr3.objects = ids;
215
216                 break;
217         default:
218                 return WERR_FOOBAR;
219         }
220
221         /* if any of the added entries are nTDSDSA objects then we
222          * need to add the SPNs to the machine account
223          */
224         status = drsuapi_add_SPNs(b_state, dce_call, mem_ctx, first_object);
225         if (!W_ERROR_IS_OK(status)) {
226                 r->out.ctr->ctr3.err_data->v1.status = status;
227                 ldb_transaction_cancel(b_state->sam_ctx);
228                 DEBUG(0,(__location__ ": DsAddEntry add SPNs failed - %s\n", win_errstr(status)));
229                 return status;
230         }
231
232         ret = ldb_transaction_commit(b_state->sam_ctx);
233         if (ret != LDB_SUCCESS) {
234                 DEBUG(0,(__location__ ": DsAddEntry commit failed: %s\n",
235                          ldb_errstring(b_state->sam_ctx)));
236                 return WERR_DS_DRA_INTERNAL_ERROR;
237         }
238
239         return WERR_OK;
240 }