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