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 "rpc_server/common/common.h" 26#include "dsdb/samdb/samdb.h" 27#include "lib/ldb/include/ldb_errors.h" 28#include "param/param.h" 29#include "librpc/gen_ndr/ndr_drsblobs.h" 30#include "auth/auth.h" 31#include "rpc_server/drsuapi/dcesrv_drsuapi.h" 32#include "libcli/security/security.h" 33#include "librpc/gen_ndr/ndr_drsblobs.h" 34#include "librpc/gen_ndr/ndr_drsuapi.h" 35 36 37/* 38 add special SPNs needed for DRS replication to machine accounts when 39 an AddEntry is done to create a nTDSDSA object 40 */ 41static WERROR drsuapi_add_SPNs(struct drsuapi_bind_state *b_state, 42 struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, 43 const struct drsuapi_DsReplicaObjectListItem *first_object) 44{ 45 int ret; 46 const struct drsuapi_DsReplicaObjectListItem *obj; 47 const char *attrs[] = { "serverReference", "objectGUID", NULL }; 48 49 for (obj = first_object; obj; obj=obj->next_object) { 50 const char *dn_string = obj->object.identifier->dn; 51 struct ldb_dn *dn = ldb_dn_new(mem_ctx, b_state->sam_ctx, dn_string); 52 struct ldb_result *res; 53 struct ldb_dn *ref_dn; 54 struct GUID ntds_guid; 55 struct ldb_message *msg; 56 struct ldb_message_element *el; 57 const char *ntds_guid_str; 58 const char *dom_string; 59 60 DEBUG(6,(__location__ ": Adding SPNs for %s\n", 61 ldb_dn_get_linearized(dn))); 62 63 ret = ldb_search(b_state->sam_ctx, mem_ctx, &res, 64 dn, LDB_SCOPE_BASE, attrs, 65 "(objectClass=ntDSDSA)"); 66 if (ret != LDB_SUCCESS || res->count < 1) { 67 DEBUG(0,(__location__ ": Failed to find dn '%s'\n", dn_string)); 68 return WERR_DS_DRA_INTERNAL_ERROR; 69 } 70 71 ref_dn = samdb_result_dn(b_state->sam_ctx, mem_ctx, res->msgs[0], "serverReference", NULL); 72 if (ref_dn == NULL) { 73 /* we only add SPNs for objects with a 74 serverReference */ 75 continue; 76 } 77 78 DEBUG(6,(__location__ ": serverReference %s\n", 79 ldb_dn_get_linearized(ref_dn))); 80 81 ntds_guid = samdb_result_guid(res->msgs[0], "objectGUID"); 82 83 ntds_guid_str = GUID_string(res, &ntds_guid); 84 85 dom_string = lp_realm(dce_call->conn->dce_ctx->lp_ctx); 86 87 /* 88 * construct a modify request to add the new SPNs to 89 * the machine account 90 */ 91 msg = ldb_msg_new(mem_ctx); 92 if (msg == NULL) { 93 return WERR_NOMEM; 94 } 95 96 msg->dn = ref_dn; 97 ret = ldb_msg_add_empty(msg, "servicePrincipalName", 98 LDB_FLAG_MOD_ADD, &el); 99 if (ret != LDB_SUCCESS) { 100 return WERR_NOMEM; 101 } 102 103 el->num_values = 2; 104 el->values = talloc_array(msg->elements, struct ldb_val, 2); 105 if (el->values == NULL) { 106 return WERR_NOMEM; 107 } 108 /* the magic constant is the GUID of the DRSUAPI RPC 109 interface */ 110 el->values[0].data = (uint8_t *)talloc_asprintf(el->values, 111 "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s", 112 ntds_guid_str, dom_string); 113 el->values[0].length = strlen((char *)el->values[0].data); 114 el->values[1].data = (uint8_t *)talloc_asprintf(el->values, "ldap/%s._msdcs.%s", 115 ntds_guid_str, dom_string); 116 el->values[1].length = strlen((char *)el->values[1].data); 117 118 ret = ldb_modify(b_state->sam_ctx, msg); 119 if (ret != LDB_SUCCESS) { 120 DEBUG(0,(__location__ ": Failed to add SPNs - %s\n", 121 ldb_errstring(b_state->sam_ctx))); 122 return WERR_DS_DRA_INTERNAL_ERROR; 123 } 124 } 125 126 return WERR_OK; 127} 128 129 130 131 132/* 133 drsuapi_DsAddEntry 134*/ 135WERROR dcesrv_drsuapi_DsAddEntry(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, 136 struct drsuapi_DsAddEntry *r) 137{ 138 WERROR status; 139 struct drsuapi_bind_state *b_state; 140 struct dcesrv_handle *h; 141 uint32_t num = 0; 142 struct drsuapi_DsReplicaObjectIdentifier2 *ids = NULL; 143 int ret; 144 const struct drsuapi_DsReplicaObjectListItem *first_object; 145 146 if (DEBUGLVL(4)) { 147 NDR_PRINT_FUNCTION_DEBUG(drsuapi_DsAddEntry, NDR_IN, r); 148 } 149 150 /* TODO: check which out level the client supports */ 151 152 ZERO_STRUCTP(r->out.ctr); 153 *r->out.level_out = 3; 154 r->out.ctr->ctr3.level = 1; 155 r->out.ctr->ctr3.error = talloc_zero(mem_ctx, union drsuapi_DsAddEntryError); 156 157 DCESRV_PULL_HANDLE_WERR(h, r->in.bind_handle, DRSUAPI_BIND_HANDLE); 158 b_state = h->data; 159 160 status = drs_security_level_check(dce_call, "DsAddEntry"); 161 if (!W_ERROR_IS_OK(status)) { 162 return status; 163 } 164 165 switch (r->in.level) { 166 case 2: 167 ret = ldb_transaction_start(b_state->sam_ctx); 168 if (ret != LDB_SUCCESS) { 169 return WERR_DS_DRA_INTERNAL_ERROR; 170 } 171 172 173 first_object = &r->in.req->req2.first_object; 174 175 status = dsdb_origin_objects_commit(b_state->sam_ctx, 176 mem_ctx, 177 first_object, 178 &num, 179 &ids); 180 if (!W_ERROR_IS_OK(status)) { 181 r->out.ctr->ctr3.error->info1.status = status; 182 ldb_transaction_cancel(b_state->sam_ctx); 183 return status; 184 } 185 186 r->out.ctr->ctr3.count = num; 187 r->out.ctr->ctr3.objects = ids; 188 189 break; 190 default: 191 return WERR_FOOBAR; 192 } 193 194 /* if any of the added entries are nTDSDSA objects then we 195 * need to add the SPNs to the machine account 196 */ 197 status = drsuapi_add_SPNs(b_state, dce_call, mem_ctx, first_object); 198 if (!W_ERROR_IS_OK(status)) { 199 r->out.ctr->ctr3.error->info1.status = status; 200 ldb_transaction_cancel(b_state->sam_ctx); 201 return status; 202 } 203 204 ret = ldb_transaction_commit(b_state->sam_ctx); 205 if (ret != LDB_SUCCESS) { 206 return WERR_DS_DRA_INTERNAL_ERROR; 207 } 208 209 return WERR_OK; 210} 211