1/* 2 Unix SMB/CIFS implementation. 3 4 Extract the user/system database from a remote SamSync server 5 6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 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 "libnet/libnet.h" 25#include "libcli/auth/libcli_auth.h" 26#include "../libcli/samsync/samsync.h" 27#include "auth/gensec/gensec.h" 28#include "auth/credentials/credentials.h" 29#include "auth/gensec/schannel_proto.h" 30#include "librpc/gen_ndr/ndr_netlogon.h" 31#include "librpc/gen_ndr/ndr_netlogon_c.h" 32#include "param/param.h" 33 34NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r) 35{ 36 NTSTATUS nt_status, dbsync_nt_status; 37 TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx; 38 struct netlogon_creds_CredentialState *creds; 39 struct netr_DatabaseSync dbsync; 40 struct netr_Authenticator credential, return_authenticator; 41 struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; 42 struct cli_credentials *machine_account; 43 struct dcerpc_pipe *p; 44 struct libnet_context *machine_net_ctx; 45 struct libnet_RpcConnect *c; 46 struct libnet_SamSync_state *state; 47 const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 48 int i; 49 50 samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context"); 51 52 if (!r->in.machine_account) { 53 machine_account = cli_credentials_init(samsync_ctx); 54 if (!machine_account) { 55 talloc_free(samsync_ctx); 56 return NT_STATUS_NO_MEMORY; 57 } 58 cli_credentials_set_conf(machine_account, ctx->lp_ctx); 59 nt_status = cli_credentials_set_machine_account(machine_account, ctx->lp_ctx); 60 if (!NT_STATUS_IS_OK(nt_status)) { 61 r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?"); 62 talloc_free(samsync_ctx); 63 return nt_status; 64 } 65 } else { 66 machine_account = r->in.machine_account; 67 } 68 69 /* We cannot do this unless we are a BDC. Check, before we get odd errors later */ 70 if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) { 71 r->out.error_string 72 = talloc_asprintf(mem_ctx, 73 "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC", 74 cli_credentials_get_domain(machine_account), 75 cli_credentials_get_secure_channel_type(machine_account)); 76 talloc_free(samsync_ctx); 77 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 78 } 79 80 c = talloc_zero(samsync_ctx, struct libnet_RpcConnect); 81 if (!c) { 82 r->out.error_string = NULL; 83 talloc_free(samsync_ctx); 84 return NT_STATUS_NO_MEMORY; 85 } 86 87 c->level = LIBNET_RPC_CONNECT_DC_INFO; 88 if (r->in.binding_string) { 89 c->in.binding = r->in.binding_string; 90 c->in.name = NULL; 91 } else { 92 c->in.binding = NULL; 93 c->in.name = cli_credentials_get_domain(machine_account); 94 } 95 96 /* prepare connect to the NETLOGON pipe of PDC */ 97 c->in.dcerpc_iface = &ndr_table_netlogon; 98 99 /* We must do this as the machine, not as any command-line 100 * user. So we override the credentials in the 101 * libnet_context */ 102 machine_net_ctx = talloc(samsync_ctx, struct libnet_context); 103 if (!machine_net_ctx) { 104 r->out.error_string = NULL; 105 talloc_free(samsync_ctx); 106 return NT_STATUS_NO_MEMORY; 107 } 108 *machine_net_ctx = *ctx; 109 machine_net_ctx->cred = machine_account; 110 111 /* connect to the NETLOGON pipe of the PDC */ 112 nt_status = libnet_RpcConnect(machine_net_ctx, samsync_ctx, c); 113 if (!NT_STATUS_IS_OK(nt_status)) { 114 if (r->in.binding_string) { 115 r->out.error_string = talloc_asprintf(mem_ctx, 116 "Connection to NETLOGON pipe of DC %s failed: %s", 117 r->in.binding_string, c->out.error_string); 118 } else { 119 r->out.error_string = talloc_asprintf(mem_ctx, 120 "Connection to NETLOGON pipe of DC for %s failed: %s", 121 c->in.name, c->out.error_string); 122 } 123 talloc_free(samsync_ctx); 124 return nt_status; 125 } 126 127 /* This makes a new pipe, on which we can do schannel. We 128 * should do this in the RpcConnect code, but the abstaction 129 * layers do not suit yet */ 130 131 nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p, 132 c->out.dcerpc_pipe->binding); 133 134 if (!NT_STATUS_IS_OK(nt_status)) { 135 r->out.error_string = talloc_asprintf(mem_ctx, 136 "Secondary connection to NETLOGON pipe of DC %s failed: %s", 137 dcerpc_server_name(p), nt_errstr(nt_status)); 138 talloc_free(samsync_ctx); 139 return nt_status; 140 } 141 142 nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &ndr_table_netlogon, 143 machine_account, ctx->lp_ctx, DCERPC_AUTH_LEVEL_PRIVACY); 144 145 if (!NT_STATUS_IS_OK(nt_status)) { 146 r->out.error_string = talloc_asprintf(mem_ctx, 147 "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s", 148 dcerpc_server_name(p), nt_errstr(nt_status)); 149 talloc_free(samsync_ctx); 150 return nt_status; 151 } 152 153 state = talloc(samsync_ctx, struct libnet_SamSync_state); 154 if (!state) { 155 r->out.error_string = NULL; 156 talloc_free(samsync_ctx); 157 return nt_status; 158 } 159 160 state->domain_name = c->out.domain_name; 161 state->domain_sid = c->out.domain_sid; 162 state->realm = c->out.realm; 163 state->domain_guid = c->out.guid; 164 state->machine_net_ctx = machine_net_ctx; 165 state->netlogon_pipe = p; 166 167 /* initialise the callback layer. It may wish to contact the 168 * server with ldap, now we know the name */ 169 170 if (r->in.init_fn) { 171 char *error_string; 172 nt_status = r->in.init_fn(samsync_ctx, 173 r->in.fn_ctx, 174 state, 175 &error_string); 176 if (!NT_STATUS_IS_OK(nt_status)) { 177 r->out.error_string = talloc_steal(mem_ctx, error_string); 178 talloc_free(samsync_ctx); 179 return nt_status; 180 } 181 } 182 183 /* get NETLOGON credentials */ 184 185 nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds); 186 if (!NT_STATUS_IS_OK(nt_status)) { 187 r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer"); 188 talloc_free(samsync_ctx); 189 return nt_status; 190 } 191 192 /* Setup details for the synchronisation */ 193 194 ZERO_STRUCT(return_authenticator); 195 196 dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p)); 197 dbsync.in.computername = cli_credentials_get_workstation(machine_account); 198 dbsync.in.preferredmaximumlength = (uint32_t)-1; 199 dbsync.in.return_authenticator = &return_authenticator; 200 dbsync.out.return_authenticator = &return_authenticator; 201 dbsync.out.delta_enum_array = &delta_enum_array; 202 203 for (i=0;i< ARRAY_SIZE(database_ids); i++) { 204 205 uint32_t sync_context = 0; 206 207 dbsync.in.database_id = database_ids[i]; 208 dbsync.in.sync_context = &sync_context; 209 dbsync.out.sync_context = &sync_context; 210 211 do { 212 int d; 213 loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context"); 214 netlogon_creds_client_authenticator(creds, &credential); 215 216 dbsync.in.credential = &credential; 217 218 dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync); 219 if (!NT_STATUS_IS_OK(dbsync_nt_status) && 220 !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) { 221 r->out.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status)); 222 talloc_free(samsync_ctx); 223 return nt_status; 224 } 225 226 if (!netlogon_creds_client_check(creds, &dbsync.out.return_authenticator->cred)) { 227 r->out.error_string = talloc_strdup(mem_ctx, "Credential chaining on incoming DatabaseSync failed"); 228 talloc_free(samsync_ctx); 229 return NT_STATUS_ACCESS_DENIED; 230 } 231 232 dbsync.in.sync_context = dbsync.out.sync_context; 233 234 /* For every single remote 'delta' entry: */ 235 for (d=0; d < delta_enum_array->num_deltas; d++) { 236 char *error_string = NULL; 237 delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context"); 238 /* 'Fix' elements, by decrypting and 239 * de-obfuscating the data */ 240 nt_status = samsync_fix_delta(delta_ctx, 241 creds, 242 dbsync.in.database_id, 243 &delta_enum_array->delta_enum[d]); 244 if (!NT_STATUS_IS_OK(nt_status)) { 245 r->out.error_string = talloc_steal(mem_ctx, error_string); 246 talloc_free(samsync_ctx); 247 return nt_status; 248 } 249 250 /* Now call the callback. This will 251 * do something like print the data or 252 * write to an ldb */ 253 nt_status = r->in.delta_fn(delta_ctx, 254 r->in.fn_ctx, 255 dbsync.in.database_id, 256 &delta_enum_array->delta_enum[d], 257 &error_string); 258 if (!NT_STATUS_IS_OK(nt_status)) { 259 r->out.error_string = talloc_steal(mem_ctx, error_string); 260 talloc_free(samsync_ctx); 261 return nt_status; 262 } 263 talloc_free(delta_ctx); 264 } 265 talloc_free(loop_ctx); 266 } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)); 267 268 if (!NT_STATUS_IS_OK(dbsync_nt_status)) { 269 r->out.error_string = talloc_asprintf(mem_ctx, "libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here", nt_errstr(nt_status)); 270 talloc_free(samsync_ctx); 271 return dbsync_nt_status; 272 } 273 nt_status = NT_STATUS_OK; 274 } 275 talloc_free(samsync_ctx); 276 return nt_status; 277} 278 279