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/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"
34 NTSTATUS
libnet_SamSync_netlogon(struct libnet_context
*ctx
, TALLOC_CTX
*mem_ctx
, struct libnet_SamSync
*r
)
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
};
50 samsync_ctx
= talloc_named(mem_ctx
, 0, "SamSync top context");
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
;
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
);
66 machine_account
= r
->in
.machine_account
;
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
) {
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
;
80 c
= talloc_zero(samsync_ctx
, struct libnet_RpcConnect
);
82 r
->out
.error_string
= NULL
;
83 talloc_free(samsync_ctx
);
84 return NT_STATUS_NO_MEMORY
;
87 c
->level
= LIBNET_RPC_CONNECT_DC_INFO
;
88 if (r
->in
.binding_string
) {
89 c
->in
.binding
= r
->in
.binding_string
;
93 c
->in
.name
= cli_credentials_get_domain(machine_account
);
96 /* prepare connect to the NETLOGON pipe of PDC */
97 c
->in
.dcerpc_iface
= &ndr_table_netlogon
;
99 /* We must do this as the machine, not as any command-line
100 * user. So we override the credentials in the
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
;
108 *machine_net_ctx
= *ctx
;
109 machine_net_ctx
->cred
= machine_account
;
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
);
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
);
123 talloc_free(samsync_ctx
);
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 */
131 nt_status
= dcerpc_secondary_connection(c
->out
.dcerpc_pipe
, &p
,
132 c
->out
.dcerpc_pipe
->binding
);
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
);
142 nt_status
= dcerpc_bind_auth_schannel(samsync_ctx
, p
, &ndr_table_netlogon
,
143 machine_account
, ctx
->lp_ctx
, DCERPC_AUTH_LEVEL_PRIVACY
);
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
);
153 state
= talloc(samsync_ctx
, struct libnet_SamSync_state
);
155 r
->out
.error_string
= NULL
;
156 talloc_free(samsync_ctx
);
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
;
167 /* initialise the callback layer. It may wish to contact the
168 * server with ldap, now we know the name */
172 nt_status
= r
->in
.init_fn(samsync_ctx
,
176 if (!NT_STATUS_IS_OK(nt_status
)) {
177 r
->out
.error_string
= talloc_steal(mem_ctx
, error_string
);
178 talloc_free(samsync_ctx
);
183 /* get NETLOGON credentials */
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
);
192 /* Setup details for the synchronisation */
194 ZERO_STRUCT(return_authenticator
);
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
;
203 for (i
=0;i
< ARRAY_SIZE(database_ids
); i
++) {
205 uint32_t sync_context
= 0;
207 dbsync
.in
.database_id
= database_ids
[i
];
208 dbsync
.in
.sync_context
= &sync_context
;
209 dbsync
.out
.sync_context
= &sync_context
;
213 loop_ctx
= talloc_named(samsync_ctx
, 0, "DatabaseSync loop context");
214 netlogon_creds_client_authenticator(creds
, &credential
);
216 dbsync
.in
.credential
= &credential
;
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
);
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
;
232 dbsync
.in
.sync_context
= dbsync
.out
.sync_context
;
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
,
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
);
250 /* Now call the callback. This will
251 * do something like print the data or
253 nt_status
= r
->in
.delta_fn(delta_ctx
,
255 dbsync
.in
.database_id
,
256 &delta_enum_array
->delta_enum
[d
],
258 if (!NT_STATUS_IS_OK(nt_status
)) {
259 r
->out
.error_string
= talloc_steal(mem_ctx
, error_string
);
260 talloc_free(samsync_ctx
);
263 talloc_free(delta_ctx
);
265 talloc_free(loop_ctx
);
266 } while (NT_STATUS_EQUAL(dbsync_nt_status
, STATUS_MORE_ENTRIES
));
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
;
273 nt_status
= NT_STATUS_OK
;
275 talloc_free(samsync_ctx
);