1/* 2 Unix SMB/CIFS mplementation. 3 KCC service periodic handling 4 5 Copyright (C) Andrew Tridgell 2009 6 based on repl service code 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 23#include "includes.h" 24#include "lib/events/events.h" 25#include "dsdb/samdb/samdb.h" 26#include "auth/auth.h" 27#include "smbd/service.h" 28#include "lib/messaging/irpc.h" 29#include "dsdb/kcc/kcc_service.h" 30#include "lib/ldb/include/ldb_errors.h" 31#include "../lib/util/dlinklist.h" 32#include "librpc/gen_ndr/ndr_misc.h" 33#include "librpc/gen_ndr/ndr_drsuapi.h" 34#include "librpc/gen_ndr/ndr_drsblobs.h" 35#include "param/param.h" 36 37/* 38 * see if a repsFromToBlob is in a list 39 */ 40static bool reps_in_list(struct repsFromToBlob *r, struct repsFromToBlob *reps, uint32_t count) 41{ 42 int i; 43 for (i=0; i<count; i++) { 44 if (strcmp(r->ctr.ctr1.other_info->dns_name, 45 reps[i].ctr.ctr1.other_info->dns_name) == 0 && 46 GUID_compare(&r->ctr.ctr1.source_dsa_obj_guid, 47 &reps[i].ctr.ctr1.source_dsa_obj_guid) == 0) { 48 return true; 49 } 50 } 51 return false; 52} 53 54 55/* 56 * add any missing repsFrom structures to our partitions 57 */ 58static NTSTATUS kccsrv_add_repsFrom(struct kccsrv_service *s, TALLOC_CTX *mem_ctx, 59 struct repsFromToBlob *reps, uint32_t count) 60{ 61 struct kccsrv_partition *p; 62 63 /* update the repsFrom on all partitions */ 64 for (p=s->partitions; p; p=p->next) { 65 struct repsFromToBlob *old_reps; 66 uint32_t old_count; 67 WERROR werr; 68 int i; 69 bool modified = false; 70 71 werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsFrom", &old_reps, &old_count); 72 if (!W_ERROR_IS_OK(werr)) { 73 DEBUG(0,(__location__ ": Failed to load repsFrom from %s - %s\n", 74 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb))); 75 return NT_STATUS_INTERNAL_DB_CORRUPTION; 76 } 77 78 /* add any new ones */ 79 for (i=0; i<count; i++) { 80 if (!reps_in_list(&reps[i], old_reps, old_count)) { 81 old_reps = talloc_realloc(mem_ctx, old_reps, struct repsFromToBlob, old_count+1); 82 NT_STATUS_HAVE_NO_MEMORY(old_reps); 83 old_reps[old_count] = reps[i]; 84 old_count++; 85 modified = true; 86 } 87 } 88 89 /* remove any stale ones */ 90 for (i=0; i<old_count; i++) { 91 if (!reps_in_list(&old_reps[i], reps, count)) { 92 memmove(&old_reps[i], &old_reps[i+1], (old_count-(i+1))*sizeof(old_reps[0])); 93 old_count--; 94 i--; 95 modified = true; 96 } 97 } 98 99 if (modified) { 100 werr = dsdb_savereps(s->samdb, mem_ctx, p->dn, "repsFrom", old_reps, old_count); 101 if (!W_ERROR_IS_OK(werr)) { 102 DEBUG(0,(__location__ ": Failed to save repsFrom to %s - %s\n", 103 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb))); 104 return NT_STATUS_INTERNAL_DB_CORRUPTION; 105 } 106 } 107 } 108 109 return NT_STATUS_OK; 110 111} 112 113/* 114 this is the core of our initial simple KCC 115 We just add a repsFrom entry for all DCs we find that have nTDSDSA 116 objects, except for ourselves 117 */ 118static NTSTATUS kccsrv_simple_update(struct kccsrv_service *s, TALLOC_CTX *mem_ctx) 119{ 120 struct ldb_result *res; 121 int ret, i; 122 const char *attrs[] = { "objectGUID", "invocationID", NULL }; 123 struct repsFromToBlob *reps = NULL; 124 uint32_t count = 0; 125 126 ret = ldb_search(s->samdb, mem_ctx, &res, s->config_dn, LDB_SCOPE_SUBTREE, 127 attrs, "objectClass=nTDSDSA"); 128 if (ret != LDB_SUCCESS) { 129 DEBUG(0,(__location__ ": Failed nTDSDSA search - %s\n", ldb_errstring(s->samdb))); 130 return NT_STATUS_INTERNAL_DB_CORRUPTION; 131 } 132 133 for (i=0; i<res->count; i++) { 134 struct repsFromTo1 *r1; 135 struct GUID ntds_guid, invocation_id; 136 137 ntds_guid = samdb_result_guid(res->msgs[i], "objectGUID"); 138 if (GUID_compare(&ntds_guid, &s->ntds_guid) == 0) { 139 /* don't replicate with ourselves */ 140 continue; 141 } 142 143 invocation_id = samdb_result_guid(res->msgs[i], "invocationID"); 144 145 reps = talloc_realloc(mem_ctx, reps, struct repsFromToBlob, count+1); 146 NT_STATUS_HAVE_NO_MEMORY(reps); 147 148 ZERO_STRUCT(reps[count]); 149 reps[count].version = 1; 150 r1 = &reps[count].ctr.ctr1; 151 152 r1->other_info = talloc_zero(reps, struct repsFromTo1OtherInfo); 153 r1->other_info->dns_name = talloc_asprintf(r1->other_info, "%s._msdcs.%s", 154 GUID_string(mem_ctx, &ntds_guid), 155 lp_realm(s->task->lp_ctx)); 156 r1->source_dsa_obj_guid = ntds_guid; 157 r1->source_dsa_invocation_id = invocation_id; 158 r1->replica_flags = 159 DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE | 160 DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP | 161 DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS; 162 memset(r1->schedule, 0x11, sizeof(r1->schedule)); 163 count++; 164 } 165 166 return kccsrv_add_repsFrom(s, mem_ctx, reps, count); 167} 168 169 170static void kccsrv_periodic_run(struct kccsrv_service *service); 171 172static void kccsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te, 173 struct timeval t, void *ptr) 174{ 175 struct kccsrv_service *service = talloc_get_type(ptr, struct kccsrv_service); 176 WERROR status; 177 178 service->periodic.te = NULL; 179 180 kccsrv_periodic_run(service); 181 182 status = kccsrv_periodic_schedule(service, service->periodic.interval); 183 if (!W_ERROR_IS_OK(status)) { 184 task_server_terminate(service->task, win_errstr(status), true); 185 return; 186 } 187} 188 189WERROR kccsrv_periodic_schedule(struct kccsrv_service *service, uint32_t next_interval) 190{ 191 TALLOC_CTX *tmp_mem; 192 struct tevent_timer *new_te; 193 struct timeval next_time; 194 195 /* prevent looping */ 196 if (next_interval == 0) next_interval = 1; 197 198 next_time = timeval_current_ofs(next_interval, 50); 199 200 if (service->periodic.te) { 201 /* 202 * if the timestamp of the new event is higher, 203 * as current next we don't need to reschedule 204 */ 205 if (timeval_compare(&next_time, &service->periodic.next_event) > 0) { 206 return WERR_OK; 207 } 208 } 209 210 /* reset the next scheduled timestamp */ 211 service->periodic.next_event = next_time; 212 213 new_te = event_add_timed(service->task->event_ctx, service, 214 service->periodic.next_event, 215 kccsrv_periodic_handler_te, service); 216 W_ERROR_HAVE_NO_MEMORY(new_te); 217 218 tmp_mem = talloc_new(service); 219 DEBUG(2,("kccsrv_periodic_schedule(%u) %sscheduled for: %s\n", 220 next_interval, 221 (service->periodic.te?"re":""), 222 nt_time_string(tmp_mem, timeval_to_nttime(&next_time)))); 223 talloc_free(tmp_mem); 224 225 talloc_free(service->periodic.te); 226 service->periodic.te = new_te; 227 228 return WERR_OK; 229} 230 231static void kccsrv_periodic_run(struct kccsrv_service *service) 232{ 233 TALLOC_CTX *mem_ctx; 234 NTSTATUS status; 235 236 DEBUG(2,("kccsrv_periodic_run(): simple update\n")); 237 238 mem_ctx = talloc_new(service); 239 status = kccsrv_simple_update(service, mem_ctx); 240 if (!NT_STATUS_IS_OK(status)) { 241 DEBUG(0,("kccsrv_simple_update failed - %s\n", nt_errstr(status))); 242 } 243 talloc_free(mem_ctx); 244} 245