s4/net_drs: 'net drs' utility initial creation
[samba.git] / source4 / utils / net / drs / net_drs.c
1 /*
2    Samba Unix/Linux SMB client library
3
4    Implements functions offered by repadmin.exe tool under Windows
5
6    Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
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 #include "includes.h"
23 #include "utils/net/net.h"
24 #include "librpc/gen_ndr/ndr_drsuapi_c.h"
25 #include "net_drs.h"
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb_wrap.h"
28
29 #include <unistd.h>
30
31
32 /**
33  * 'net drs' supported sub-commands
34  */
35 static const struct net_functable net_drs_functable[] = {
36         { "bind", "Display replication features for a domain controller\n", net_drs_bind_cmd, net_drs_bind_usage },
37         { NULL, NULL }
38 };
39
40 /**
41  * 'net drs' entry point
42  */
43 int net_drs(struct net_context *ctx, int argc, const char **argv)
44 {
45         return net_run_function(ctx, argc, argv, net_drs_functable, net_drs_usage);
46 }
47
48 /**
49  * 'net drs' usage message
50  */
51 int net_drs_usage(struct net_context *ctx, int argc, const char **argv)
52 {
53         d_printf("net drs <command> [options]\n");
54         d_printf("\n");
55         d_printf("Currently implemented commands:\n");
56         d_printf("  bind - Display DC replication features");
57         return 0;
58 }
59
60 /**
61  * Create drsuapi connection to remote DC
62  * and fill-in DC capabilities
63  */
64 static bool net_drs_DsBind(struct net_drs_context *drs_ctx)
65 {
66         NTSTATUS status;
67         struct GUID bind_guid;
68         struct drsuapi_DsBind req;
69         struct drsuapi_DsBindInfoCtr in_bind_ctr;
70         union drsuapi_DsBindInfo *bind_info;
71
72         SMB_ASSERT(drs_ctx->binding != NULL);
73
74         status = dcerpc_pipe_connect_b(drs_ctx,
75                                        &drs_ctx->drs_pipe,
76                                        drs_ctx->binding,
77                                        &ndr_table_drsuapi,
78                                        drs_ctx->net_ctx->credentials,
79                                        drs_ctx->net_ctx->event_ctx,
80                                        drs_ctx->net_ctx->lp_ctx);
81         if (!NT_STATUS_IS_OK(status)) {
82                 d_printf("Failed to connect to server: %s\n", nt_errstr(status));
83                 return false;
84         }
85
86         ZERO_STRUCT(in_bind_ctr);
87         in_bind_ctr.length = 48;
88         in_bind_ctr.info.info48.pid = (uint32_t)getpid();
89         GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
90         req.in.bind_guid = &bind_guid;
91         req.in.bind_info = &in_bind_ctr;
92         req.out.bind_handle = &drs_ctx->bind_handle;
93
94         status = dcerpc_drsuapi_DsBind(drs_ctx->drs_pipe, drs_ctx, &req);
95         if (!NT_STATUS_IS_OK(status)) {
96                 const char *errstr = nt_errstr(status);
97                 if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
98                         errstr = dcerpc_errstr(drs_ctx, drs_ctx->drs_pipe->last_fault_code);
99                 }
100                 d_printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr);
101                 return false;
102         } else if (!W_ERROR_IS_OK(req.out.result)) {
103                 d_printf("DsBind failed - %s\n", win_errstr(req.out.result));
104                 return false;
105         }
106
107         /* fill-in remote DC capabilities */
108         ZERO_STRUCT(drs_ctx->info48);
109         bind_info = &req.out.bind_info->info;
110         drs_ctx->bind_info_len = req.out.bind_info->length;
111         switch (drs_ctx->bind_info_len) {
112         case 48:
113                 drs_ctx->info48.supported_extensions_ext = bind_info->info48.supported_extensions_ext;
114                 drs_ctx->info48.config_dn_guid = bind_info->info48.config_dn_guid;
115         case 28:
116                 drs_ctx->info48.repl_epoch = bind_info->info28.repl_epoch;
117         case 24:
118                 drs_ctx->info48.supported_extensions = bind_info->info24.supported_extensions;
119                 drs_ctx->info48.site_guid = bind_info->info24.site_guid;
120                 drs_ctx->info48.pid = bind_info->info24.pid;
121                 break;
122         default:
123                 d_printf("Error: server returned BindInfo length %d", req.out.bind_info->length);
124                 return false;
125         }
126
127         return true;
128 }
129
130 /**
131  * Close DRSUAPI connection to remote DC
132  */
133 static bool net_drs_DsUnbind(struct net_drs_context *drs_ctx)
134 {
135         struct drsuapi_DsUnbind r;
136         struct policy_handle bind_handle;
137
138         ZERO_STRUCT(r);
139         r.out.bind_handle = &bind_handle;
140
141         r.in.bind_handle = &drs_ctx->bind_handle;
142         dcerpc_drsuapi_DsUnbind(drs_ctx->drs_pipe, drs_ctx, &r);
143
144         return true;
145 }
146
147
148 /**
149  * Open secured LDAP connection to remote DC
150  */
151 static bool net_drs_ldap_connect(struct net_drs_context *drs_ctx)
152 {
153         char *url;
154         bool bret = true;
155
156         url = talloc_asprintf(drs_ctx, "ldap://%s/", drs_ctx->dc_name);
157         if (!url) {
158                 d_printf(__location__ ": Have no memory");
159                 return false;
160         }
161
162         drs_ctx->ldap.ldb = ldb_wrap_connect(drs_ctx,
163                                              drs_ctx->net_ctx->event_ctx, drs_ctx->net_ctx->lp_ctx,
164                                              url,
165                                              NULL,
166                                              drs_ctx->net_ctx->credentials,
167                                              0);
168         if (drs_ctx->ldap.ldb == NULL) {
169                 d_printf("Unable to connect to LDAP %s", url);
170                 bret = false;
171         }
172
173         talloc_free(url);
174
175         return bret;
176 }
177
178 /**
179  * fetch RootDSE record
180  */
181 static bool net_drs_ldap_rootdse(struct net_drs_context *drs_ctx)
182 {
183         int ret;
184         struct ldb_result *r;
185         struct ldb_dn *basedn;
186         static const char *attrs[] = {
187                 "*",
188                 NULL
189         };
190
191         SMB_ASSERT(drs_ctx->ldap.ldb != NULL);
192
193         basedn = ldb_dn_new(drs_ctx, drs_ctx->ldap.ldb, NULL);
194         if (!basedn) {
195                 d_printf(__location__ ": No memory");
196                 return false;
197         }
198
199         ret = ldb_search(drs_ctx->ldap.ldb, drs_ctx, &r,
200                          basedn, LDB_SCOPE_BASE, attrs,
201                          "(objectClass=*)");
202         talloc_free(basedn);
203         if (ret != LDB_SUCCESS) {
204                 d_printf("RootDSE search failed: %s", ldb_errstring(drs_ctx->ldap.ldb));
205                 talloc_free(r);
206                 return false;
207         } else if (r->count != 1) {
208                 d_printf("RootDSE search returned more than one record!");
209                 talloc_free(r);
210                 return false;
211         }
212
213         drs_ctx->ldap.rootdse = r->msgs[0];
214
215         return true;
216 }
217
218 /**
219  * parses binding from command line
220  * and gets target DC name
221  */
222 static bool net_drs_parse_binding(struct net_drs_context *drs_ctx, const char *dc_binding)
223 {
224         NTSTATUS status;
225
226         status = dcerpc_parse_binding(drs_ctx, dc_binding, &drs_ctx->binding);
227         if (!NT_STATUS_IS_OK(status)) {
228                 d_printf("Bad binding supplied %s\n", dc_binding);
229                 return false;
230         }
231
232         drs_ctx->binding->transport = NCACN_IP_TCP;
233         drs_ctx->binding->flags |= DCERPC_SIGN | DCERPC_SEAL;
234
235         /* cache target DC name */
236         drs_ctx->dc_name = drs_ctx->binding->target_hostname;
237
238         return true;
239 }
240
241 /**
242  * Free DRSUAPI connection upon net_drs_context
243  * destruction
244  */
245 static int net_drs_context_destructor(struct net_drs_context *drs_ctx)
246 {
247         if (drs_ctx->drs_pipe) {
248                 net_drs_DsUnbind(drs_ctx);
249         }
250         return 0;
251 }
252
253 /**
254  * Create net_drs_context context to be used
255  * by 'net drs' sub-commands
256  */
257 bool net_drs_create_context(struct net_context *net_ctx,
258                             const char *dc_binding,
259                             struct net_drs_context **_drs_ctx)
260 {
261         struct net_drs_context *drs_ctx;
262
263         drs_ctx = talloc_zero(net_ctx, struct net_drs_context);
264         if (!drs_ctx) {
265                 d_printf(__location__ ": No memory");
266                 return false;
267         }
268
269         drs_ctx->net_ctx = net_ctx;
270
271         if (!net_drs_parse_binding(drs_ctx, dc_binding)) {
272                 goto failed;
273         }
274
275         /* LDAP connect */
276         if (!net_drs_ldap_connect(drs_ctx)) {
277                 goto failed;
278         }
279         /* fetch RootDSE */
280         if (!net_drs_ldap_rootdse(drs_ctx)) {
281                 goto failed;
282         }
283
284         /* DRSUAPI connection */
285         if (!net_drs_DsBind(drs_ctx)) {
286                 goto failed;
287         }
288
289         /* set destructor to free any open connections */
290         talloc_set_destructor(drs_ctx, net_drs_context_destructor);
291
292         *_drs_ctx = drs_ctx;
293         return true;
294
295 failed:
296         talloc_free(drs_ctx);
297         return false;
298 }