drs-fsmo: Improve handling of FSMO role takeover.
[Samba/gebeck_regimport.git] / source3 / utils / net_rpc_trust.c
blobd15d10c0d421c780b715d186baf7e76cc7224df6
1 /*
2 Samba Unix/Linux SMB client library
3 Distributed SMB/CIFS Server Management Utility
4 Copyright (C) 2011 Sumit Bose (sbose@redhat.com)
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.
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.
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/>. */
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 #include "libsmb/libsmb.h"
31 #define ARG_OTHERSERVER "otherserver="
32 #define ARG_OTHERUSER "otheruser="
33 #define ARG_OTHERDOMAINSID "otherdomainsid="
34 #define ARG_OTHERDOMAIN "otherdomain="
35 #define ARG_OTHERNETBIOSDOMAIN "other_netbios_domain="
36 #define ARG_TRUSTPW "trustpw="
38 enum trust_op {
39 TRUST_CREATE,
40 TRUST_DELETE
43 struct other_dom_data {
44 char *host;
45 char *user_name;
46 char *domain_sid_str;
47 char *dns_domain_name;
48 char *domain_name;
51 struct dom_data {
52 struct dom_sid *domsid;
53 char *dns_domain_name;
54 char *domain_name;
57 static NTSTATUS close_handle(TALLOC_CTX *mem_ctx,
58 struct dcerpc_binding_handle *bind_hnd,
59 struct policy_handle *pol_hnd)
61 NTSTATUS status;
62 NTSTATUS result;
64 status = dcerpc_lsa_Close(bind_hnd, mem_ctx, pol_hnd, &result);
65 if (!NT_STATUS_IS_OK(status)) {
66 DEBUG(0, ("dcerpc_lsa_Close failed with error [%s].\n",
67 nt_errstr(status)));
68 return status;
70 if (!NT_STATUS_IS_OK(result)) {
71 DEBUG(0, ("lsa close failed with error [%s].\n",
72 nt_errstr(result)));
73 return result;
76 return NT_STATUS_OK;
79 static NTSTATUS delete_trust(TALLOC_CTX *mem_ctx,
80 struct dcerpc_binding_handle *bind_hnd,
81 struct policy_handle *pol_hnd,
82 struct dom_sid *domsid)
84 NTSTATUS status;
85 struct lsa_DeleteTrustedDomain dr;
87 dr.in.handle = pol_hnd;
88 dr.in.dom_sid = domsid;
90 status = dcerpc_lsa_DeleteTrustedDomain_r(bind_hnd, mem_ctx, &dr);
91 if (!NT_STATUS_IS_OK(status)) {
92 DEBUG(0, ("dcerpc_lsa_DeleteTrustedDomain_r failed with [%s]\n",
93 nt_errstr(status)));
94 return status;
96 if (!NT_STATUS_IS_OK(dr.out.result)) {
97 DEBUG(0, ("DeleteTrustedDomain returned [%s]\n",
98 nt_errstr(dr.out.result)));
99 return dr.out.result;
102 return NT_STATUS_OK;
105 static NTSTATUS create_trust(TALLOC_CTX *mem_ctx,
106 struct dcerpc_binding_handle *bind_hnd,
107 struct policy_handle *pol_hnd,
108 const char *trust_name,
109 const char *trust_name_dns,
110 struct dom_sid *domsid,
111 struct lsa_TrustDomainInfoAuthInfoInternal *authinfo)
113 NTSTATUS status;
114 struct lsa_CreateTrustedDomainEx2 r;
115 struct lsa_TrustDomainInfoInfoEx trustinfo;
116 struct policy_handle trustdom_handle;
118 trustinfo.sid = domsid;
119 trustinfo.netbios_name.string = trust_name;
120 trustinfo.domain_name.string = trust_name_dns;
122 trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND |
123 LSA_TRUST_DIRECTION_OUTBOUND;
125 trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL;
127 trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
129 r.in.policy_handle = pol_hnd;
130 r.in.info = &trustinfo;
131 r.in.auth_info_internal = authinfo;
132 r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH |
133 LSA_TRUSTED_QUERY_DOMAIN_NAME;
134 r.out.trustdom_handle = &trustdom_handle;
136 status = dcerpc_lsa_CreateTrustedDomainEx2_r(bind_hnd, mem_ctx, &r);
137 if (!NT_STATUS_IS_OK(status)) {
138 DEBUG(0, ("dcerpc_lsa_CreateTrustedDomainEx2_r failed "
139 "with error [%s].\n", nt_errstr(status)));
140 return status;
142 if (!NT_STATUS_IS_OK(r.out.result)) {
143 DEBUG(0, ("CreateTrustedDomainEx2_r returned [%s].\n",
144 nt_errstr(r.out.result)));
145 return r.out.result;
148 return NT_STATUS_OK;
151 static NTSTATUS get_domain_info(TALLOC_CTX *mem_ctx,
152 struct dcerpc_binding_handle *bind_hdn,
153 struct policy_handle *pol_hnd,
154 struct dom_data *dom_data)
156 NTSTATUS status;
157 struct lsa_QueryInfoPolicy2 qr;
159 qr.in.handle = pol_hnd;
160 qr.in.level = LSA_POLICY_INFO_DNS;
162 status = dcerpc_lsa_QueryInfoPolicy2_r(bind_hdn, mem_ctx, &qr);
163 if (!NT_STATUS_IS_OK(status)) {
164 DEBUG(0, ("dcerpc_lsa_QueryInfoPolicy2_r failed "
165 "with error [%s].\n", nt_errstr(status)));
166 return status;
169 if (!NT_STATUS_IS_OK(qr.out.result)) {
170 DEBUG(0, ("QueryInfoPolicy2 returned [%s].\n",
171 nt_errstr(qr.out.result)));
172 return qr.out.result;
175 dom_data->domain_name = talloc_strdup(mem_ctx,
176 (*qr.out.info)->dns.name.string);
177 dom_data->dns_domain_name = talloc_strdup(mem_ctx,
178 (*qr.out.info)->dns.dns_domain.string);
179 dom_data->domsid = dom_sid_dup(mem_ctx, (*qr.out.info)->dns.sid);
180 if (dom_data->domain_name == NULL ||
181 dom_data->dns_domain_name == NULL ||
182 dom_data->domsid == NULL) {
183 DEBUG(0, ("Copying domain data failed.\n"));
184 return NT_STATUS_NO_MEMORY;
187 DEBUG(0, ("Got the following domain info [%s][%s][%s].\n",
188 dom_data->domain_name, dom_data->dns_domain_name,
189 sid_string_talloc(mem_ctx, dom_data->domsid)));
191 return NT_STATUS_OK;
194 static NTSTATUS connect_and_get_info(TALLOC_CTX *mem_ctx,
195 struct net_context *net_ctx,
196 struct cli_state **cli,
197 struct rpc_pipe_client **pipe_hnd,
198 struct policy_handle *pol_hnd,
199 struct dom_data *dom_data,
200 DATA_BLOB *session_key)
202 NTSTATUS status;
203 NTSTATUS result;
205 status = net_make_ipc_connection_ex(net_ctx, NULL, NULL, NULL,
206 NET_FLAGS_PDC, cli);
207 if (!NT_STATUS_IS_OK(status)) {
208 DEBUG(0, ("Failed to connect to [%s] with error [%s]\n",
209 net_ctx->opt_host, nt_errstr(status)));
210 return status;
213 status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc.syntax_id, pipe_hnd);
214 if (!NT_STATUS_IS_OK(status)) {
215 DEBUG(0, ("Failed to initialise lsa pipe with error [%s]\n",
216 nt_errstr(status)));
217 return status;
220 status = dcerpc_lsa_open_policy2((*pipe_hnd)->binding_handle,
221 mem_ctx,
222 (*pipe_hnd)->srv_name_slash,
223 false,
224 (LSA_POLICY_VIEW_LOCAL_INFORMATION |
225 LSA_POLICY_TRUST_ADMIN |
226 LSA_POLICY_CREATE_SECRET),
227 pol_hnd,
228 &result);
229 if (!NT_STATUS_IS_OK(status)) {
230 DEBUG(0, ("Failed to open policy handle with error [%s]\n",
231 nt_errstr(status)));
232 return status;
234 if (!NT_STATUS_IS_OK(result)) {
235 DEBUG(0, ("lsa_open_policy2 with error [%s]\n",
236 nt_errstr(result)));
237 return result;
240 status = get_domain_info(mem_ctx, (*pipe_hnd)->binding_handle,
241 pol_hnd, dom_data);
242 if (!NT_STATUS_IS_OK(status)) {
243 DEBUG(0, ("get_domain_info failed with error [%s].\n",
244 nt_errstr(status)));
245 return status;
248 status = cli_get_session_key(mem_ctx, *pipe_hnd, session_key);
249 if (!NT_STATUS_IS_OK(status)) {
250 DEBUG(0,("Error getting session_key of LSA pipe. Error was %s\n",
251 nt_errstr(status)));
252 return status;
255 return NT_STATUS_OK;
258 static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx,
259 const char *password,
260 DATA_BLOB *auth_blob)
262 struct trustDomainPasswords auth_struct;
263 struct AuthenticationInformation *auth_info_array;
264 enum ndr_err_code ndr_err;
265 size_t converted_size;
267 generate_random_buffer(auth_struct.confounder,
268 sizeof(auth_struct.confounder));
270 auth_info_array = talloc_array(mem_ctx,
271 struct AuthenticationInformation, 1);
272 if (auth_info_array == NULL) {
273 return false;
276 auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
277 if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password,
278 strlen(password),
279 &auth_info_array[0].AuthInfo.clear.password,
280 &converted_size)) {
281 return false;
284 auth_info_array[0].AuthInfo.clear.size = converted_size;
286 auth_struct.outgoing.count = 1;
287 auth_struct.outgoing.current.count = 1;
288 auth_struct.outgoing.current.array = auth_info_array;
289 auth_struct.outgoing.previous.count = 0;
290 auth_struct.outgoing.previous.array = NULL;
292 auth_struct.incoming.count = 1;
293 auth_struct.incoming.current.count = 1;
294 auth_struct.incoming.current.array = auth_info_array;
295 auth_struct.incoming.previous.count = 0;
296 auth_struct.incoming.previous.array = NULL;
298 ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct,
299 (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
300 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
301 return false;
304 return true;
307 static int parse_trust_args(TALLOC_CTX *mem_ctx, int argc, const char **argv, struct other_dom_data **_o, char **_trustpw)
309 size_t c;
310 struct other_dom_data *o = NULL;
311 char *trustpw = NULL;
312 int ret = EFAULT;
314 if (argc == 0) {
315 return EINVAL;
318 o = talloc_zero(mem_ctx, struct other_dom_data);
319 if (o == NULL) {
320 DEBUG(0, ("talloc_zero failed.\n"));
321 return ENOMEM;
324 for (c = 0; c < argc; c++) {
325 if (strnequal(argv[c], ARG_OTHERSERVER, sizeof(ARG_OTHERSERVER)-1)) {
326 o->host = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERSERVER)-1);
327 if (o->host == NULL) {
328 ret = ENOMEM;
329 goto failed;
331 } else if (strnequal(argv[c], ARG_OTHERUSER, sizeof(ARG_OTHERUSER)-1)) {
332 o->user_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERUSER)-1);
333 if (o->user_name == NULL) {
334 ret = ENOMEM;
335 goto failed;
337 } else if (strnequal(argv[c], ARG_OTHERDOMAINSID, sizeof(ARG_OTHERDOMAINSID)-1)) {
338 o->domain_sid_str = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAINSID)-1);
339 if (o->domain_sid_str == NULL) {
340 ret = ENOMEM;
341 goto failed;
343 } else if (strnequal(argv[c], ARG_OTHERDOMAIN, sizeof(ARG_OTHERDOMAIN)-1)) {
344 o->dns_domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAIN)-1);
345 if (o->dns_domain_name == NULL) {
346 ret = ENOMEM;
347 goto failed;
349 } else if (strnequal(argv[c], ARG_OTHERNETBIOSDOMAIN, sizeof(ARG_OTHERNETBIOSDOMAIN)-1)) {
350 o->domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERNETBIOSDOMAIN)-1);
351 if (o->domain_name == NULL) {
352 ret = ENOMEM;
353 goto failed;
355 } else if (strnequal(argv[c], ARG_TRUSTPW, sizeof(ARG_TRUSTPW)-1)) {
356 trustpw = talloc_strdup(mem_ctx, argv[c] + sizeof(ARG_TRUSTPW)-1);
357 if (trustpw == NULL) {
358 ret = ENOMEM;
359 goto failed;
361 } else {
362 DEBUG(0, ("Unsupported option [%s].\n", argv[c]));
363 ret = EINVAL;
364 goto failed;
368 *_o = o;
369 *_trustpw = trustpw;
371 return 0;
373 failed:
374 talloc_free(o);
375 talloc_free(trustpw);
376 return ret;
379 static void print_trust_delete_usage(void)
381 d_printf( "%s\n"
382 "net rpc trust delete [options]\n"
383 "\nOptions:\n"
384 "\totherserver=DC in other domain\n"
385 "\totheruser=Admin user in other domain\n"
386 "\totherdomainsid=SID of other domain\n"
387 "\nExamples:\n"
388 "\tnet rpc trust delete otherserver=oname otheruser=ouser -S lname -U luser\n"
389 "\tnet rpc trust delete otherdomainsid=S-... -S lname -U luser\n"
390 " %s\n",
391 _("Usage:"),
392 _("Remove trust between two domains"));
395 static void print_trust_usage(void)
397 d_printf( "%s\n"
398 "net rpc trust create [options]\n"
399 "\nOptions:\n"
400 "\totherserver=DC in other domain\n"
401 "\totheruser=Admin user in other domain\n"
402 "\totherdomainsid=SID of other domain\n"
403 "\tother_netbios_domain=NetBIOS/short name of other domain\n"
404 "\totherdomain=Full/DNS name of other domain\n"
405 "\ttrustpw=Trust password\n"
406 "\nExamples:\n"
407 "\tnet rpc trust create otherserver=oname otheruser=ouser -S lname -U luser\n"
408 "\tnet rpc trust create otherdomainsid=S-... other_netbios_domain=odom otherdomain=odom.org trustpw=secret -S lname -U luser\n"
409 " %s\n",
410 _("Usage:"),
411 _("Create trust between two domains"));
414 static int rpc_trust_common(struct net_context *net_ctx, int argc,
415 const char **argv, enum trust_op op)
417 TALLOC_CTX *mem_ctx;
418 NTSTATUS status;
419 int ret;
420 int success = -1;
421 struct cli_state *cli[2] = {NULL, NULL};
422 struct rpc_pipe_client *pipe_hnd[2] = {NULL, NULL};
423 DATA_BLOB session_key[2];
424 struct policy_handle pol_hnd[2];
425 struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
426 DATA_BLOB auth_blob;
427 char *trust_pw = NULL;
428 struct other_dom_data *other_dom_data;
429 struct net_context *other_net_ctx = NULL;
430 struct dom_data dom_data[2];
431 void (*usage)(void);
433 ZERO_STRUCT(session_key);
435 switch (op) {
436 case TRUST_CREATE:
437 usage = print_trust_usage;
438 break;
439 case TRUST_DELETE:
440 usage = print_trust_delete_usage;
441 break;
442 default:
443 DEBUG(0, ("Unsupported trust operation.\n"));
444 return -1;
447 if (net_ctx->display_usage) {
448 usage();
449 return 0;
452 mem_ctx = talloc_init("trust op");
453 if (mem_ctx == NULL) {
454 DEBUG(0, ("talloc_init failed.\n"));
455 return -1;
458 ret = parse_trust_args(mem_ctx, argc, argv, &other_dom_data, &trust_pw);
459 if (ret != 0) {
460 if (ret == EINVAL) {
461 usage();
462 } else {
463 DEBUG(0, ("Failed to parse arguments.\n"));
465 goto done;
468 if (other_dom_data->host != 0) {
469 other_net_ctx = talloc_zero(other_dom_data, struct net_context);
470 if (other_net_ctx == NULL) {
471 DEBUG(0, ("talloc_zero failed.\n"));
472 goto done;
475 other_net_ctx->opt_host = other_dom_data->host;
476 other_net_ctx->opt_user_name = other_dom_data->user_name;
477 } else {
478 dom_data[1].domsid = dom_sid_parse_talloc(mem_ctx,
479 other_dom_data->domain_sid_str);
480 dom_data[1].domain_name = other_dom_data->domain_name;
481 dom_data[1].dns_domain_name = other_dom_data->dns_domain_name;
483 if (dom_data[1].domsid == NULL ||
484 (op == TRUST_CREATE &&
485 (dom_data[1].domain_name == NULL ||
486 dom_data[1].dns_domain_name == NULL))) {
487 DEBUG(0, ("Missing required argument.\n"));
488 usage();
489 goto done;
493 status = connect_and_get_info(mem_ctx, net_ctx, &cli[0], &pipe_hnd[0],
494 &pol_hnd[0], &dom_data[0], &session_key[0]);
495 if (!NT_STATUS_IS_OK(status)) {
496 DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
497 nt_errstr(status)));
498 goto done;
501 if (other_net_ctx != NULL) {
502 status = connect_and_get_info(mem_ctx, other_net_ctx,
503 &cli[1], &pipe_hnd[1],
504 &pol_hnd[1], &dom_data[1],
505 &session_key[1]);
506 if (!NT_STATUS_IS_OK(status)) {
507 DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
508 nt_errstr(status)));
509 goto done;
513 if (op == TRUST_CREATE) {
514 if (trust_pw == NULL) {
515 if (other_net_ctx == NULL) {
516 DEBUG(0, ("Missing either trustpw or otherhost.\n"));
517 goto done;
520 DEBUG(0, ("Using random trust password.\n"));
521 /* FIXME: why only 8 characters work? Would it be possible to use a
522 * random binary password? */
523 trust_pw = generate_random_str(mem_ctx, 8);
524 if (trust_pw == NULL) {
525 DEBUG(0, ("generate_random_str failed.\n"));
526 goto done;
528 } else {
529 DEBUG(0, ("Using user provided password.\n"));
532 if (!get_trust_domain_passwords_auth_blob(mem_ctx, trust_pw,
533 &auth_blob)) {
534 DEBUG(0, ("get_trust_domain_passwords_auth_blob failed\n"));
535 goto done;
538 authinfo.auth_blob.data = (uint8_t *)talloc_memdup(
539 mem_ctx,
540 auth_blob.data,
541 auth_blob.length);
542 if (authinfo.auth_blob.data == NULL) {
543 goto done;
545 authinfo.auth_blob.size = auth_blob.length;
547 arcfour_crypt_blob(authinfo.auth_blob.data,
548 authinfo.auth_blob.size,
549 &session_key[0]);
551 status = create_trust(mem_ctx, pipe_hnd[0]->binding_handle,
552 &pol_hnd[0],
553 dom_data[1].domain_name,
554 dom_data[1].dns_domain_name,
555 dom_data[1].domsid,
556 &authinfo);
557 if (!NT_STATUS_IS_OK(status)) {
558 DEBUG(0, ("create_trust failed with error [%s].\n",
559 nt_errstr(status)));
560 goto done;
563 if (other_net_ctx != NULL) {
564 talloc_free(authinfo.auth_blob.data);
565 authinfo.auth_blob.data = (uint8_t *)talloc_memdup(
566 mem_ctx,
567 auth_blob.data,
568 auth_blob.length);
569 if (authinfo.auth_blob.data == NULL) {
570 goto done;
572 authinfo.auth_blob.size = auth_blob.length;
574 arcfour_crypt_blob(authinfo.auth_blob.data,
575 authinfo.auth_blob.size,
576 &session_key[1]);
578 status = create_trust(mem_ctx,
579 pipe_hnd[1]->binding_handle,
580 &pol_hnd[1],
581 dom_data[0].domain_name,
582 dom_data[0].dns_domain_name,
583 dom_data[0].domsid, &authinfo);
584 if (!NT_STATUS_IS_OK(status)) {
585 DEBUG(0, ("create_trust failed with error [%s].\n",
586 nt_errstr(status)));
587 goto done;
590 } else if (op == TRUST_DELETE) {
591 status = delete_trust(mem_ctx, pipe_hnd[0]->binding_handle,
592 &pol_hnd[0], dom_data[1].domsid);
593 if (!NT_STATUS_IS_OK(status)) {
594 DEBUG(0, ("delete_trust failed with [%s].\n",
595 nt_errstr(status)));
596 goto done;
599 if (other_net_ctx != NULL) {
600 status = delete_trust(mem_ctx,
601 pipe_hnd[1]->binding_handle,
602 &pol_hnd[1], dom_data[0].domsid);
603 if (!NT_STATUS_IS_OK(status)) {
604 DEBUG(0, ("delete_trust failed with [%s].\n",
605 nt_errstr(status)));
606 goto done;
611 status = close_handle(mem_ctx, pipe_hnd[0]->binding_handle,
612 &pol_hnd[0]);
613 if (!NT_STATUS_IS_OK(status)) {
614 DEBUG(0, ("close_handle failed with error [%s].\n",
615 nt_errstr(status)));
616 goto done;
619 if (other_net_ctx != NULL) {
620 status = close_handle(mem_ctx, pipe_hnd[1]->binding_handle,
621 &pol_hnd[1]);
622 if (!NT_STATUS_IS_OK(status)) {
623 DEBUG(0, ("close_handle failed with error [%s].\n",
624 nt_errstr(status)));
625 goto done;
629 success = 0;
631 done:
632 data_blob_clear_free(&session_key[0]);
633 data_blob_clear_free(&session_key[1]);
634 cli_shutdown(cli[0]);
635 cli_shutdown(cli[1]);
636 talloc_destroy(mem_ctx);
637 return success;
640 static int rpc_trust_create(struct net_context *net_ctx, int argc,
641 const char **argv)
643 return rpc_trust_common(net_ctx, argc, argv, TRUST_CREATE);
646 static int rpc_trust_delete(struct net_context *net_ctx, int argc,
647 const char **argv)
649 return rpc_trust_common(net_ctx, argc, argv, TRUST_DELETE);
652 int net_rpc_trust(struct net_context *c, int argc, const char **argv)
654 struct functable func[] = {
656 "create",
657 rpc_trust_create,
658 NET_TRANSPORT_RPC,
659 N_("Create trusts"),
660 N_("net rpc trust create\n"
661 " Create trusts")
664 "delete",
665 rpc_trust_delete,
666 NET_TRANSPORT_RPC,
667 N_("Remove trusts"),
668 N_("net rpc trust delete\n"
669 " Remove trusts")
671 {NULL, NULL, 0, NULL, NULL}
674 return net_run_function(c, argc, argv, "net rpc trust", func);