s3-net: Add net trust utility
[metze/samba/wip.git] / source3 / utils / net_rpc_trust.c
1 /*
2    Samba Unix/Linux SMB client library
3    Distributed SMB/CIFS Server Management Utility
4    Copyright (C) 2011 Sumit Bose (sbose@redhat.com)
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19
20 #include "includes.h"
21 #include "utils/net.h"
22 #include "rpc_client/cli_pipe.h"
23 #include "rpc_client/cli_lsarpc.h"
24 #include "librpc/gen_ndr/ndr_drsblobs.h"
25 #include "../librpc/gen_ndr/ndr_lsa_c.h"
26 #include "../lib/crypto/crypto.h"
27 #include "../libcli/security/dom_sid.h"
28
29
30 #define ARG_OTHERSERVER "otherserver="
31 #define ARG_OTHERUSER "otheruser="
32 #define ARG_OTHERDOMAINSID "otherdomainsid="
33 #define ARG_OTHERDOMAIN "otherdomain="
34 #define ARG_OTHERNETBIOSDOMAIN "other_netbios_domain="
35 #define ARG_TRUSTPW "trustpw="
36
37 struct other_dom_data {
38         char *host;
39         char *user_name;
40         char *domain_sid_str;
41         char *dns_domain_name;
42         char *domain_name;
43 };
44
45 struct dom_data {
46         struct dom_sid *domsid;
47         char *dns_domain_name;
48         char *domain_name;
49 };
50
51 static NTSTATUS close_handle(TALLOC_CTX *mem_ctx,
52                              struct dcerpc_binding_handle *bind_hnd,
53                              struct policy_handle *pol_hnd)
54 {
55         NTSTATUS status;
56         NTSTATUS result;
57
58         status = dcerpc_lsa_Close(bind_hnd, mem_ctx, pol_hnd, &result);
59         if (!NT_STATUS_IS_OK(status)) {
60                 DEBUG(0, ("dcerpc_lsa_Close failed with error [%s].\n",
61                           nt_errstr(status)));
62                 return status;
63         }
64         if (!NT_STATUS_IS_OK(result)) {
65                 DEBUG(0, ("lsa close failed with error [%s].\n",
66                           nt_errstr(result)));
67                 return result;
68         }
69
70         return NT_STATUS_OK;
71 }
72
73 static NTSTATUS create_trust(TALLOC_CTX *mem_ctx,
74                              struct dcerpc_binding_handle *bind_hnd,
75                              struct policy_handle *pol_hnd,
76                              const char *trust_name,
77                              const char *trust_name_dns,
78                              struct dom_sid *domsid,
79                              struct lsa_TrustDomainInfoAuthInfoInternal *authinfo)
80 {
81         NTSTATUS status;
82         struct lsa_CreateTrustedDomainEx2 r;
83         struct lsa_TrustDomainInfoInfoEx trustinfo;
84         struct policy_handle trustdom_handle;
85
86         trustinfo.sid = domsid;
87         trustinfo.netbios_name.string = trust_name;
88         trustinfo.domain_name.string = trust_name_dns;
89
90         trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND |
91                                     LSA_TRUST_DIRECTION_OUTBOUND;
92
93         trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL;
94
95         trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
96
97         r.in.policy_handle = pol_hnd;
98         r.in.info = &trustinfo;
99         r.in.auth_info = authinfo;
100         r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH |
101                            LSA_TRUSTED_QUERY_DOMAIN_NAME;
102         r.out.trustdom_handle = &trustdom_handle;
103
104         status = dcerpc_lsa_CreateTrustedDomainEx2_r(bind_hnd, mem_ctx, &r);
105         if (!NT_STATUS_IS_OK(status)) {
106                 DEBUG(0, ("dcerpc_lsa_CreateTrustedDomainEx2_r failed "
107                           "with error [%s].\n", nt_errstr(status)));
108                 return status;
109         }
110         if (!NT_STATUS_IS_OK(r.out.result)) {
111                 DEBUG(0, ("CreateTrustedDomainEx2_r returned [%s].\n",
112                           nt_errstr(r.out.result)));
113                 return r.out.result;
114         }
115
116         return NT_STATUS_OK;
117 }
118
119 static NTSTATUS get_domain_info(TALLOC_CTX *mem_ctx,
120                                 struct dcerpc_binding_handle *bind_hdn,
121                                 struct policy_handle *pol_hnd,
122                                 struct dom_data *dom_data)
123 {
124         NTSTATUS status;
125         struct lsa_QueryInfoPolicy2 qr;
126
127         qr.in.handle = pol_hnd;
128         qr.in.level = LSA_POLICY_INFO_DNS;
129
130         status = dcerpc_lsa_QueryInfoPolicy2_r(bind_hdn, mem_ctx, &qr);
131         if (!NT_STATUS_IS_OK(status)) {
132                 DEBUG(0, ("dcerpc_lsa_QueryInfoPolicy2_r failed "
133                           "with error [%s].\n", nt_errstr(status)));
134                 return status;
135         }
136
137         if (!NT_STATUS_IS_OK(qr.out.result)) {
138                 DEBUG(0, ("QueryInfoPolicy2 returned [%s].\n",
139                           nt_errstr(qr.out.result)));
140                 return qr.out.result;
141         }
142
143         dom_data->domain_name = talloc_strdup(mem_ctx,
144                                               (*qr.out.info)->dns.name.string);
145         dom_data->dns_domain_name = talloc_strdup(mem_ctx,
146                                          (*qr.out.info)->dns.dns_domain.string);
147         dom_data->domsid = dom_sid_dup(mem_ctx, (*qr.out.info)->dns.sid);
148         if (dom_data->domain_name == NULL ||
149             dom_data->dns_domain_name == NULL ||
150             dom_data->domsid == NULL) {
151                 DEBUG(0, ("Copying domain data failed.\n"));
152                 return NT_STATUS_NO_MEMORY;
153         }
154
155         DEBUG(0, ("Got the following domain info [%s][%s][%s].\n",
156                   dom_data->domain_name, dom_data->dns_domain_name,
157                   sid_string_talloc(mem_ctx, dom_data->domsid)));
158
159         return NT_STATUS_OK;
160 }
161
162 static NTSTATUS connect_and_get_info(TALLOC_CTX *mem_ctx,
163                                      struct net_context *net_ctx,
164                                      struct cli_state **cli,
165                                      struct rpc_pipe_client **pipe_hnd,
166                                      struct policy_handle *pol_hnd,
167                                      struct dom_data *dom_data)
168 {
169         NTSTATUS status;
170         NTSTATUS result;
171
172         status = net_make_ipc_connection_ex(net_ctx, NULL, NULL, NULL,
173                                             NET_FLAGS_PDC, cli);
174         if (!NT_STATUS_IS_OK(status)) {
175                 DEBUG(0, ("Failed to connect to [%s] with error [%s]\n",
176                           net_ctx->opt_host, nt_errstr(status)));
177                 return status;
178         }
179
180         status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id, pipe_hnd);
181         if (!NT_STATUS_IS_OK(status)) {
182                 DEBUG(0, ("Failed to initialise lsa pipe with error [%s]\n",
183                           nt_errstr(status)));
184                 return status;
185         }
186
187         status = dcerpc_lsa_open_policy2((*pipe_hnd)->binding_handle,
188                                          mem_ctx,
189                                          (*pipe_hnd)->srv_name_slash,
190                                          false,
191                                          (LSA_POLICY_VIEW_LOCAL_INFORMATION |
192                                           LSA_POLICY_TRUST_ADMIN |
193                                           LSA_POLICY_CREATE_SECRET),
194                                          pol_hnd,
195                                          &result);
196         if (!NT_STATUS_IS_OK(status)) {
197                 DEBUG(0, ("Failed to open policy handle with error [%s]\n",
198                           nt_errstr(status)));
199                 return status;
200         }
201         if (!NT_STATUS_IS_OK(result)) {
202                 DEBUG(0, ("lsa_open_policy2 with error [%s]\n",
203                           nt_errstr(result)));
204                 return result;
205         }
206
207         status = get_domain_info(mem_ctx, (*pipe_hnd)->binding_handle,
208                                  pol_hnd, dom_data);
209         if (!NT_STATUS_IS_OK(status)) {
210                 DEBUG(0, ("get_domain_info failed with error [%s].\n",
211                           nt_errstr(status)));
212                 return status;
213         }
214
215         return NT_STATUS_OK;
216 }
217
218 static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx,
219                                                  const char *password,
220                                                  DATA_BLOB *auth_blob)
221 {
222         struct trustDomainPasswords auth_struct;
223         struct AuthenticationInformation *auth_info_array;
224         enum ndr_err_code ndr_err;
225         size_t converted_size;
226
227         generate_random_buffer(auth_struct.confounder,
228                                sizeof(auth_struct.confounder));
229
230         auth_info_array = talloc_array(mem_ctx,
231                                        struct AuthenticationInformation, 1);
232         if (auth_info_array == NULL) {
233                 return false;
234         }
235
236         auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
237         if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password,
238                                   strlen(password),
239                                   &auth_info_array[0].AuthInfo.clear.password,
240                                   &converted_size)) {
241                 return false;
242         }
243
244         auth_info_array[0].AuthInfo.clear.size = converted_size;
245
246         auth_struct.outgoing.count = 1;
247         auth_struct.outgoing.current.count = 1;
248         auth_struct.outgoing.current.array = auth_info_array;
249         auth_struct.outgoing.previous.count = 0;
250         auth_struct.outgoing.previous.array = NULL;
251
252         auth_struct.incoming.count = 1;
253         auth_struct.incoming.current.count = 1;
254         auth_struct.incoming.current.array = auth_info_array;
255         auth_struct.incoming.previous.count = 0;
256         auth_struct.incoming.previous.array = NULL;
257
258         ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct,
259                                        (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
260         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
261                 return false;
262         }
263
264         return true;
265 }
266
267 static int parse_trust_args(TALLOC_CTX *mem_ctx, int argc, const char **argv, struct other_dom_data **_o, char **_trustpw)
268 {
269         size_t c;
270         struct other_dom_data *o = NULL;
271         char *trustpw = NULL;
272         int ret = EFAULT;
273
274         if (argc == 0) {
275                 return EINVAL;
276         }
277
278         o = talloc_zero(mem_ctx, struct other_dom_data);
279         if (o == NULL) {
280                 DEBUG(0, ("talloc_zero failed.\n"));
281                 return ENOMEM;
282         }
283
284         for (c = 0; c < argc; c++) {
285                 if (strnequal(argv[c], ARG_OTHERSERVER, sizeof(ARG_OTHERSERVER)-1)) {
286                         o->host = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERSERVER)-1);
287                         if (o->host == NULL) {
288                                 ret = ENOMEM;
289                                 goto failed;
290                         }
291                 } else if (strnequal(argv[c], ARG_OTHERUSER, sizeof(ARG_OTHERUSER)-1)) {
292                         o->user_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERUSER)-1);
293                         if (o->user_name == NULL) {
294                                 ret = ENOMEM;
295                                 goto failed;
296                         }
297                 } else if (strnequal(argv[c], ARG_OTHERDOMAINSID, sizeof(ARG_OTHERDOMAINSID)-1)) {
298                         o->domain_sid_str = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAINSID)-1);
299                         if (o->domain_sid_str == NULL) {
300                                 ret = ENOMEM;
301                                 goto failed;
302                         }
303                 } else if (strnequal(argv[c], ARG_OTHERDOMAIN, sizeof(ARG_OTHERDOMAIN)-1)) {
304                         o->dns_domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAIN)-1);
305                         if (o->dns_domain_name == NULL) {
306                                 ret = ENOMEM;
307                                 goto failed;
308                         }
309                 } else if (strnequal(argv[c], ARG_OTHERNETBIOSDOMAIN, sizeof(ARG_OTHERNETBIOSDOMAIN)-1)) {
310                         o->domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERNETBIOSDOMAIN)-1);
311                         if (o->domain_name == NULL) {
312                                 ret = ENOMEM;
313                                 goto failed;
314                         }
315                 } else if (strnequal(argv[c], ARG_TRUSTPW, sizeof(ARG_TRUSTPW)-1)) {
316                         trustpw = talloc_strdup(mem_ctx, argv[c] + sizeof(ARG_TRUSTPW)-1);
317                         if (trustpw == NULL) {
318                                 ret = ENOMEM;
319                                 goto failed;
320                         }
321                 } else {
322                         DEBUG(0, ("Unsupported option [%s].\n", argv[c]));
323                         ret = EINVAL;
324                         goto failed;
325                 }
326         }
327
328         *_o = o;
329         *_trustpw = trustpw;
330
331         return 0;
332
333 failed:
334         talloc_free(o);
335         talloc_free(trustpw);
336         return ret;
337 }
338
339 static void print_trust_usage(void)
340 {
341         d_printf(  "%s\n"
342                    "net rpc trust create [options]\n"
343                    "\nOptions:\n"
344                    "\totherserver=DC in other domain\n"
345                    "\totheruser=Admin user in other domain\n"
346                    "\totherdomainsid=SID of other domain\n"
347                    "\tother_netbios_domain=NetBIOS/short name of other domain\n"
348                    "\totherdomain=Full/DNS name of other domain\n"
349                    "\ttrustpw=Trust password\n"
350                    "\nExamples:\n"
351                    "\tnet rpc trust create otherserver=oname otheruser=ouser -S lname -U luser\n"
352                    "\tnet rpc trust create otherdomainsid=S-... other_netbios_domain=odom otherdomain=odom.org trustpw=secret -S lname -U luser\n"
353                    "  %s\n",
354                  _("Usage:"),
355                  _("Create trust between two domains"));
356 }
357
358 static int rpc_trust_create(struct net_context *net_ctx, int argc,
359                             const char **argv)
360 {
361         TALLOC_CTX *mem_ctx;
362         NTSTATUS status;
363         int ret;
364         int success = -1;
365         struct cli_state *cli[2] = {NULL, NULL};
366         struct rpc_pipe_client *pipe_hnd[2] = {NULL, NULL};
367         struct policy_handle pol_hnd[2];
368         struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
369         DATA_BLOB auth_blob;
370         char *trust_pw = NULL;
371         struct other_dom_data *other_dom_data;
372         struct net_context *other_net_ctx = NULL;
373         struct dom_data dom_data[2];
374
375         if (net_ctx->display_usage) {
376                 print_trust_usage();
377                 return 0;
378         }
379
380         mem_ctx = talloc_init("trust create");
381         if (mem_ctx == NULL) {
382                 DEBUG(0, ("talloc_init failed.\n"));
383                 return -1;
384         }
385
386         ret = parse_trust_args(mem_ctx, argc, argv, &other_dom_data, &trust_pw);
387         if (ret != 0) {
388                 if (ret == EINVAL) {
389                         print_trust_usage();
390                 } else {
391                         DEBUG(0, ("Failed to parse arguments.\n"));
392                 }
393                 goto done;
394         }
395
396         if (other_dom_data->host != 0) {
397                 other_net_ctx = talloc_zero(other_dom_data, struct net_context);
398                 if (other_net_ctx == NULL) {
399                         DEBUG(0, ("talloc_zero failed.\n"));
400                         goto done;
401                 }
402
403                 other_net_ctx->opt_host = other_dom_data->host;
404                 other_net_ctx->opt_user_name = other_dom_data->user_name;
405         } else {
406                 dom_data[1].domsid = dom_sid_parse_talloc(mem_ctx,
407                                                 other_dom_data->domain_sid_str);
408                 dom_data[1].domain_name = other_dom_data->domain_name;
409                 dom_data[1].dns_domain_name = other_dom_data->dns_domain_name;
410
411                 if (dom_data[1].domsid == NULL ||
412                     dom_data[1].domain_name == NULL ||
413                     dom_data[1].dns_domain_name == NULL) {
414                         DEBUG(0, ("Missing required argument.\n"));
415                         print_trust_usage();
416                         goto done;
417                 }
418         }
419
420         status = connect_and_get_info(mem_ctx, net_ctx, &cli[0], &pipe_hnd[0],
421                                       &pol_hnd[0], &dom_data[0]);
422         if (!NT_STATUS_IS_OK(status)) {
423                 DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
424                           nt_errstr(status)));
425                 goto done;
426         }
427
428         if (other_net_ctx != NULL) {
429                 status = connect_and_get_info(mem_ctx, other_net_ctx,
430                                               &cli[1], &pipe_hnd[1],
431                                               &pol_hnd[1], &dom_data[1]);
432                 if (!NT_STATUS_IS_OK(status)) {
433                         DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
434                                   nt_errstr(status)));
435                         goto done;
436                 }
437         }
438
439         if (trust_pw == NULL) {
440                 if (other_net_ctx == NULL) {
441                         DEBUG(0, ("Missing either trustpw or otherhost.\n"));
442                         goto done;
443                 }
444
445                 DEBUG(0, ("Using random trust password.\n"));
446 /* FIXME: why only 8 characters work? Would it be possible to use a random
447  * binary password? */
448                 trust_pw = generate_random_str(mem_ctx, 8);
449                 if (trust_pw == NULL) {
450                         DEBUG(0, ("generate_random_str failed.\n"));
451                         goto done;
452                 }
453         } else {
454                 DEBUG(0, ("Using user provided password.\n"));
455         }
456
457         if (!get_trust_domain_passwords_auth_blob(mem_ctx, trust_pw,
458                                                   &auth_blob)) {
459                 DEBUG(0, ("get_trust_domain_passwords_auth_blob failed\n"));
460                 goto done;
461         }
462
463         authinfo.auth_blob.data = talloc_memdup(mem_ctx, auth_blob.data,
464                                                 auth_blob.length);
465         if (authinfo.auth_blob.data == NULL) {
466                 goto done;
467         }
468         authinfo.auth_blob.size = auth_blob.length;
469
470         arcfour_crypt_blob(authinfo.auth_blob.data, authinfo.auth_blob.size,
471                            &cli[0]->user_session_key);
472
473         status = create_trust(mem_ctx, pipe_hnd[0]->binding_handle, &pol_hnd[0],
474                               dom_data[1].domain_name,
475                               dom_data[1].dns_domain_name, dom_data[1].domsid,
476                               &authinfo);
477         if (!NT_STATUS_IS_OK(status)) {
478                 DEBUG(0, ("create_trust failed with error [%s].\n",
479                 nt_errstr(status)));
480                 goto done;
481         }
482
483         if (other_net_ctx != NULL) {
484                 talloc_free(authinfo.auth_blob.data);
485                 authinfo.auth_blob.data = talloc_memdup(mem_ctx, auth_blob.data,
486                                                         auth_blob.length);
487                 if (authinfo.auth_blob.data == NULL) {
488                         goto done;
489                 }
490                 authinfo.auth_blob.size = auth_blob.length;
491
492                 arcfour_crypt_blob(authinfo.auth_blob.data, authinfo.auth_blob.size,
493                                    &cli[1]->user_session_key);
494
495                 status = create_trust(mem_ctx, pipe_hnd[1]->binding_handle,
496                                       &pol_hnd[1], dom_data[0].domain_name,
497                                       dom_data[0].dns_domain_name,
498                                       dom_data[0].domsid, &authinfo);
499                 if (!NT_STATUS_IS_OK(status)) {
500                         DEBUG(0, ("create_trust failed with error [%s].\n",
501                         nt_errstr(status)));
502                         goto done;
503                 }
504         }
505
506         status = close_handle(mem_ctx, pipe_hnd[0]->binding_handle,
507                               &pol_hnd[0]);
508         if (!NT_STATUS_IS_OK(status)) {
509                 DEBUG(0, ("close_handle failed with error [%s].\n",
510                           nt_errstr(status)));
511                 goto done;
512         }
513
514         if (other_net_ctx != NULL) {
515                 status = close_handle(mem_ctx, pipe_hnd[1]->binding_handle,
516                                       &pol_hnd[1]);
517                 if (!NT_STATUS_IS_OK(status)) {
518                         DEBUG(0, ("close_handle failed with error [%s].\n",
519                                   nt_errstr(status)));
520                         goto done;
521                 }
522         }
523
524         success = 0;
525
526 done:
527         cli_shutdown(cli[0]);
528         cli_shutdown(cli[1]);
529         talloc_destroy(mem_ctx);
530         return success;
531 }
532
533 int net_rpc_trust(struct net_context *c, int argc, const char **argv)
534 {
535         struct functable func[] = {
536                 {
537                         "create",
538                         rpc_trust_create,
539                         NET_TRANSPORT_RPC,
540                         N_("Create trusts"),
541                         N_("net rpc trust create\n"
542                            "    Create trusts")
543                 },
544                 {NULL, NULL, 0, NULL, NULL}
545         };
546
547         return net_run_function(c, argc, argv, "net rpc trust", func);
548 }