r15837: starting sync up for 3.0.23rc1 (in sync with SAMBA_3_0 r15822)
[Samba.git] / source / utils / net_ads.c
blobe701803d17ea227c754ae1d7630bb75db5cae716
1 /*
2 Samba Unix/Linux SMB client library
3 net ads commands
4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5 Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6 Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
7 Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "includes.h"
25 #include "utils/net.h"
27 /* Macro for checking RPC error codes to make things more readable */
29 #if 0
30 #define CHECK_RPC_ERR(rpc, msg) \
31 if (!NT_STATUS_IS_OK(result = rpc)) { \
32 DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
33 goto done; \
36 #define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
37 if (!NT_STATUS_IS_OK(result = rpc)) { \
38 DEBUG(0, debug_args); \
39 goto done; \
42 #endif
43 #ifdef HAVE_ADS
45 int net_ads_usage(int argc, const char **argv)
47 d_printf(
48 "\nnet ads join <org_unit>"\
49 "\n\tjoins the local machine to a ADS realm\n"\
50 "\nnet ads leave"\
51 "\n\tremoves the local machine from a ADS realm\n"\
52 "\nnet ads testjoin"\
53 "\n\ttests that an exiting join is OK\n"\
54 "\nnet ads user"\
55 "\n\tlist, add, or delete users in the realm\n"\
56 "\nnet ads group"\
57 "\n\tlist, add, or delete groups in the realm\n"\
58 "\nnet ads info"\
59 "\n\tshows some info on the server\n"\
60 "\nnet ads status"\
61 "\n\tdump the machine account details to stdout\n"
62 "\nnet ads lookup"\
63 "\n\tperform a CLDAP search on the server\n"
64 "\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
65 "\n\tchange a user's password using an admin account"\
66 "\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
67 "\nnet ads changetrustpw"\
68 "\n\tchange the trust account password of this machine in the AD tree\n"\
69 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
70 "\n\t lookup, add, or remove directory entry for a printer\n"\
71 "\nnet ads search"\
72 "\n\tperform a raw LDAP search and dump the results\n"
73 "\nnet ads dn"\
74 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
75 "\nnet ads sid"\
76 "\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
77 "\nnet ads keytab"\
78 "\n\tcreates and updates the kerberos system keytab file\n"
80 return -1;
85 do a cldap netlogon query
87 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
89 struct cldap_netlogon_reply reply;
91 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
92 d_fprintf(stderr, "CLDAP query failed!\n");
93 return -1;
96 d_printf("Information for Domain Controller: %s\n\n",
97 inet_ntoa(ads->ldap_ip));
99 d_printf("Response Type: ");
100 switch (reply.type) {
101 case SAMLOGON_AD_UNK_R:
102 d_printf("SAMLOGON\n");
103 break;
104 case SAMLOGON_AD_R:
105 d_printf("SAMLOGON_USER\n");
106 break;
107 default:
108 d_printf("0x%x\n", reply.type);
109 break;
111 d_printf("GUID: %s\n",
112 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
113 d_printf("Flags:\n"
114 "\tIs a PDC: %s\n"
115 "\tIs a GC of the forest: %s\n"
116 "\tIs an LDAP server: %s\n"
117 "\tSupports DS: %s\n"
118 "\tIs running a KDC: %s\n"
119 "\tIs running time services: %s\n"
120 "\tIs the closest DC: %s\n"
121 "\tIs writable: %s\n"
122 "\tHas a hardware clock: %s\n"
123 "\tIs a non-domain NC serviced by LDAP server: %s\n",
124 (reply.flags & ADS_PDC) ? "yes" : "no",
125 (reply.flags & ADS_GC) ? "yes" : "no",
126 (reply.flags & ADS_LDAP) ? "yes" : "no",
127 (reply.flags & ADS_DS) ? "yes" : "no",
128 (reply.flags & ADS_KDC) ? "yes" : "no",
129 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
130 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
131 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
132 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
133 (reply.flags & ADS_NDNC) ? "yes" : "no");
135 printf("Forest:\t\t\t%s\n", reply.forest);
136 printf("Domain:\t\t\t%s\n", reply.domain);
137 printf("Domain Controller:\t%s\n", reply.hostname);
139 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
140 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
142 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
143 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
145 printf("Site Name:\t\t%s\n", reply.site_name);
146 printf("Site Name (2):\t\t%s\n", reply.site_name_2);
148 d_printf("NT Version: %d\n", reply.version);
149 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
150 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
152 return 0;
157 this implements the CLDAP based netlogon lookup requests
158 for finding the domain controller of a ADS domain
160 static int net_ads_lookup(int argc, const char **argv)
162 ADS_STRUCT *ads;
163 ADS_STATUS status;
164 const char *realm = NULL;
166 if ( strequal(lp_workgroup(), opt_target_workgroup ) )
167 realm = lp_realm();
169 ads = ads_init(realm, opt_target_workgroup, opt_host);
170 if (ads) {
171 ads->auth.flags |= ADS_AUTH_NO_BIND;
174 status = ads_connect(ads);
175 if (!ADS_ERR_OK(status) || !ads) {
176 d_fprintf(stderr, "Didn't find the cldap server!\n");
177 return -1;
180 if (!ads->config.realm) {
181 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
182 ads->ldap_port = 389;
185 return net_ads_cldap_netlogon(ads);
190 static int net_ads_info(int argc, const char **argv)
192 ADS_STRUCT *ads;
194 if ( (ads = ads_init(lp_realm(), opt_target_workgroup, opt_host)) != NULL ) {
195 ads->auth.flags |= ADS_AUTH_NO_BIND;
198 ads_connect(ads);
200 if (!ads || !ads->config.realm) {
201 d_fprintf(stderr, "Didn't find the ldap server!\n");
202 return -1;
205 /* Try to set the server's current time since we didn't do a full
206 TCP LDAP session initially */
208 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
209 d_fprintf( stderr, "Failed to get server's current time!\n");
212 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
213 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
214 d_printf("Realm: %s\n", ads->config.realm);
215 d_printf("Bind Path: %s\n", ads->config.bind_path);
216 d_printf("LDAP port: %d\n", ads->ldap_port);
217 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
219 d_printf("KDC server: %s\n", ads->auth.kdc_server );
220 d_printf("Server time offset: %d\n", ads->auth.time_offset );
222 return 0;
225 static void use_in_memory_ccache(void) {
226 /* Use in-memory credentials cache so we do not interfere with
227 * existing credentials */
228 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
231 static ADS_STRUCT *ads_startup(void)
233 ADS_STRUCT *ads;
234 ADS_STATUS status;
235 BOOL need_password = False;
236 BOOL second_time = False;
237 char *cp;
239 /* lp_realm() should be handled by a command line param,
240 However, the join requires that realm be set in smb.conf
241 and compares our realm with the remote server's so this is
242 ok until someone needs more flexibility */
244 ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
246 if (!opt_user_name) {
247 opt_user_name = "administrator";
250 if (opt_user_specified) {
251 need_password = True;
254 retry:
255 if (!opt_password && need_password && !opt_machine_pass) {
256 char *prompt;
257 asprintf(&prompt,"%s's password: ", opt_user_name);
258 opt_password = getpass(prompt);
259 free(prompt);
262 if (opt_password) {
263 use_in_memory_ccache();
264 ads->auth.password = smb_xstrdup(opt_password);
267 ads->auth.user_name = smb_xstrdup(opt_user_name);
270 * If the username is of the form "name@realm",
271 * extract the realm and convert to upper case.
272 * This is only used to establish the connection.
274 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
275 *cp++ = '\0';
276 ads->auth.realm = smb_xstrdup(cp);
277 strupper_m(ads->auth.realm);
280 status = ads_connect(ads);
282 if (!ADS_ERR_OK(status)) {
283 if (!need_password && !second_time) {
284 need_password = True;
285 second_time = True;
286 goto retry;
287 } else {
288 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
289 return NULL;
292 return ads;
297 Check to see if connection can be made via ads.
298 ads_startup() stores the password in opt_password if it needs to so
299 that rpc or rap can use it without re-prompting.
301 int net_ads_check(void)
303 ADS_STRUCT *ads;
304 ADS_STATUS status;
306 if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == NULL ) {
307 return -1;
310 ads->auth.flags |= ADS_AUTH_NO_BIND;
312 status = ads_connect(ads);
313 if ( !ADS_ERR_OK(status) ) {
314 return -1;
317 ads_destroy(&ads);
318 return 0;
322 determine the netbios workgroup name for a domain
324 static int net_ads_workgroup(int argc, const char **argv)
326 ADS_STRUCT *ads;
327 ADS_STATUS status;
328 const char *realm = NULL;
329 struct cldap_netlogon_reply reply;
331 if ( strequal(lp_workgroup(), opt_target_workgroup ) )
332 realm = lp_realm();
334 ads = ads_init(realm, opt_target_workgroup, opt_host);
335 if (ads) {
336 ads->auth.flags |= ADS_AUTH_NO_BIND;
339 status = ads_connect(ads);
340 if (!ADS_ERR_OK(status) || !ads) {
341 d_fprintf(stderr, "Didn't find the cldap server!\n");
342 return -1;
345 if (!ads->config.realm) {
346 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
347 ads->ldap_port = 389;
350 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
351 d_fprintf(stderr, "CLDAP query failed!\n");
352 return -1;
355 d_printf("Workgroup: %s\n", reply.netbios_domain);
357 ads_destroy(&ads);
359 return 0;
364 static BOOL usergrp_display(char *field, void **values, void *data_area)
366 char **disp_fields = (char **) data_area;
368 if (!field) { /* must be end of record */
369 if (disp_fields[0]) {
370 if (!strchr_m(disp_fields[0], '$')) {
371 if (disp_fields[1])
372 d_printf("%-21.21s %s\n",
373 disp_fields[0], disp_fields[1]);
374 else
375 d_printf("%s\n", disp_fields[0]);
378 SAFE_FREE(disp_fields[0]);
379 SAFE_FREE(disp_fields[1]);
380 return True;
382 if (!values) /* must be new field, indicate string field */
383 return True;
384 if (StrCaseCmp(field, "sAMAccountName") == 0) {
385 disp_fields[0] = SMB_STRDUP((char *) values[0]);
387 if (StrCaseCmp(field, "description") == 0)
388 disp_fields[1] = SMB_STRDUP((char *) values[0]);
389 return True;
392 static int net_ads_user_usage(int argc, const char **argv)
394 return net_help_user(argc, argv);
397 static int ads_user_add(int argc, const char **argv)
399 ADS_STRUCT *ads;
400 ADS_STATUS status;
401 char *upn, *userdn;
402 void *res=NULL;
403 int rc = -1;
405 if (argc < 1) return net_ads_user_usage(argc, argv);
407 if (!(ads = ads_startup())) {
408 return -1;
411 status = ads_find_user_acct(ads, &res, argv[0]);
413 if (!ADS_ERR_OK(status)) {
414 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
415 goto done;
418 if (ads_count_replies(ads, res)) {
419 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
420 goto done;
423 if (opt_container == NULL) {
424 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
427 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
429 if (!ADS_ERR_OK(status)) {
430 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
431 ads_errstr(status));
432 goto done;
435 /* if no password is to be set, we're done */
436 if (argc == 1) {
437 d_printf("User %s added\n", argv[0]);
438 rc = 0;
439 goto done;
442 /* try setting the password */
443 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
444 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
445 ads->auth.time_offset);
446 safe_free(upn);
447 if (ADS_ERR_OK(status)) {
448 d_printf("User %s added\n", argv[0]);
449 rc = 0;
450 goto done;
453 /* password didn't set, delete account */
454 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
455 argv[0], ads_errstr(status));
456 ads_msgfree(ads, res);
457 status=ads_find_user_acct(ads, &res, argv[0]);
458 if (ADS_ERR_OK(status)) {
459 userdn = ads_get_dn(ads, res);
460 ads_del_dn(ads, userdn);
461 ads_memfree(ads, userdn);
464 done:
465 if (res)
466 ads_msgfree(ads, res);
467 ads_destroy(&ads);
468 return rc;
471 static int ads_user_info(int argc, const char **argv)
473 ADS_STRUCT *ads;
474 ADS_STATUS rc;
475 void *res;
476 const char *attrs[] = {"memberOf", NULL};
477 char *searchstring=NULL;
478 char **grouplist;
479 char *escaped_user;
481 if (argc < 1) {
482 return net_ads_user_usage(argc, argv);
485 escaped_user = escape_ldap_string_alloc(argv[0]);
487 if (!escaped_user) {
488 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
489 return -1;
492 if (!(ads = ads_startup())) {
493 SAFE_FREE(escaped_user);
494 return -1;
497 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
498 rc = ads_search(ads, &res, searchstring, attrs);
499 safe_free(searchstring);
501 if (!ADS_ERR_OK(rc)) {
502 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
503 ads_destroy(&ads);
504 SAFE_FREE(escaped_user);
505 return -1;
508 grouplist = ldap_get_values(ads->ld, res, "memberOf");
510 if (grouplist) {
511 int i;
512 char **groupname;
513 for (i=0;grouplist[i];i++) {
514 groupname = ldap_explode_dn(grouplist[i], 1);
515 d_printf("%s\n", groupname[0]);
516 ldap_value_free(groupname);
518 ldap_value_free(grouplist);
521 ads_msgfree(ads, res);
522 ads_destroy(&ads);
523 SAFE_FREE(escaped_user);
524 return 0;
527 static int ads_user_delete(int argc, const char **argv)
529 ADS_STRUCT *ads;
530 ADS_STATUS rc;
531 void *res;
532 char *userdn;
534 if (argc < 1) {
535 return net_ads_user_usage(argc, argv);
538 if (!(ads = ads_startup())) {
539 return -1;
542 rc = ads_find_user_acct(ads, &res, argv[0]);
543 if (!ADS_ERR_OK(rc)) {
544 DEBUG(0, ("User %s does not exist\n", argv[0]));
545 ads_destroy(&ads);
546 return -1;
548 userdn = ads_get_dn(ads, res);
549 ads_msgfree(ads, res);
550 rc = ads_del_dn(ads, userdn);
551 ads_memfree(ads, userdn);
552 if (!ADS_ERR_OK(rc)) {
553 d_printf("User %s deleted\n", argv[0]);
554 ads_destroy(&ads);
555 return 0;
557 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
558 ads_errstr(rc));
559 ads_destroy(&ads);
560 return -1;
563 int net_ads_user(int argc, const char **argv)
565 struct functable func[] = {
566 {"ADD", ads_user_add},
567 {"INFO", ads_user_info},
568 {"DELETE", ads_user_delete},
569 {NULL, NULL}
571 ADS_STRUCT *ads;
572 ADS_STATUS rc;
573 const char *shortattrs[] = {"sAMAccountName", NULL};
574 const char *longattrs[] = {"sAMAccountName", "description", NULL};
575 char *disp_fields[2] = {NULL, NULL};
577 if (argc == 0) {
578 if (!(ads = ads_startup())) {
579 return -1;
582 if (opt_long_list_entries)
583 d_printf("\nUser name Comment"\
584 "\n-----------------------------\n");
586 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
587 LDAP_SCOPE_SUBTREE,
588 "(objectCategory=user)",
589 opt_long_list_entries ? longattrs :
590 shortattrs, usergrp_display,
591 disp_fields);
592 ads_destroy(&ads);
593 return 0;
596 return net_run_function(argc, argv, func, net_ads_user_usage);
599 static int net_ads_group_usage(int argc, const char **argv)
601 return net_help_group(argc, argv);
604 static int ads_group_add(int argc, const char **argv)
606 ADS_STRUCT *ads;
607 ADS_STATUS status;
608 void *res=NULL;
609 int rc = -1;
611 if (argc < 1) {
612 return net_ads_group_usage(argc, argv);
615 if (!(ads = ads_startup())) {
616 return -1;
619 status = ads_find_user_acct(ads, &res, argv[0]);
621 if (!ADS_ERR_OK(status)) {
622 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
623 goto done;
626 if (ads_count_replies(ads, res)) {
627 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
628 ads_msgfree(ads, res);
629 goto done;
632 if (opt_container == NULL) {
633 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
636 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
638 if (ADS_ERR_OK(status)) {
639 d_printf("Group %s added\n", argv[0]);
640 rc = 0;
641 } else {
642 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
643 ads_errstr(status));
646 done:
647 if (res)
648 ads_msgfree(ads, res);
649 ads_destroy(&ads);
650 return rc;
653 static int ads_group_delete(int argc, const char **argv)
655 ADS_STRUCT *ads;
656 ADS_STATUS rc;
657 void *res;
658 char *groupdn;
660 if (argc < 1) {
661 return net_ads_group_usage(argc, argv);
664 if (!(ads = ads_startup())) {
665 return -1;
668 rc = ads_find_user_acct(ads, &res, argv[0]);
669 if (!ADS_ERR_OK(rc)) {
670 DEBUG(0, ("Group %s does not exist\n", argv[0]));
671 ads_destroy(&ads);
672 return -1;
674 groupdn = ads_get_dn(ads, res);
675 ads_msgfree(ads, res);
676 rc = ads_del_dn(ads, groupdn);
677 ads_memfree(ads, groupdn);
678 if (!ADS_ERR_OK(rc)) {
679 d_printf("Group %s deleted\n", argv[0]);
680 ads_destroy(&ads);
681 return 0;
683 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
684 ads_errstr(rc));
685 ads_destroy(&ads);
686 return -1;
689 int net_ads_group(int argc, const char **argv)
691 struct functable func[] = {
692 {"ADD", ads_group_add},
693 {"DELETE", ads_group_delete},
694 {NULL, NULL}
696 ADS_STRUCT *ads;
697 ADS_STATUS rc;
698 const char *shortattrs[] = {"sAMAccountName", NULL};
699 const char *longattrs[] = {"sAMAccountName", "description", NULL};
700 char *disp_fields[2] = {NULL, NULL};
702 if (argc == 0) {
703 if (!(ads = ads_startup())) {
704 return -1;
707 if (opt_long_list_entries)
708 d_printf("\nGroup name Comment"\
709 "\n-----------------------------\n");
710 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
711 LDAP_SCOPE_SUBTREE,
712 "(objectCategory=group)",
713 opt_long_list_entries ? longattrs :
714 shortattrs, usergrp_display,
715 disp_fields);
717 ads_destroy(&ads);
718 return 0;
720 return net_run_function(argc, argv, func, net_ads_group_usage);
723 static int net_ads_status(int argc, const char **argv)
725 ADS_STRUCT *ads;
726 ADS_STATUS rc;
727 void *res;
729 if (!(ads = ads_startup())) {
730 return -1;
733 rc = ads_find_machine_acct(ads, &res, global_myname());
734 if (!ADS_ERR_OK(rc)) {
735 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
736 ads_destroy(&ads);
737 return -1;
740 if (ads_count_replies(ads, res) == 0) {
741 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
742 ads_destroy(&ads);
743 return -1;
746 ads_dump(ads, res);
747 ads_destroy(&ads);
748 return 0;
751 /*******************************************************************
752 Leave an AD domain. Windows XP disables the machine account.
753 We'll try the same. The old code would do an LDAP delete.
754 That only worked using the machine creds because added the machine
755 with full control to the computer object's ACL.
756 *******************************************************************/
757 static int net_ads_leave(int argc, const char **argv)
759 ADS_STRUCT *ads = NULL;
760 int ret = -1;
761 struct cli_state *cli = NULL;
762 TALLOC_CTX *ctx;
763 DOM_SID *dom_sid = NULL;
765 if (!secrets_init()) {
766 DEBUG(1,("Failed to initialise secrets database\n"));
767 return -1;
770 if (!(ctx = talloc_init("net_ads_leave"))) {
771 DEBUG(0, ("Could not initialise talloc context\n"));
772 return -1;
775 /* The finds a DC and takes care of getting the
776 user creds if necessary */
778 if (!(ads = ads_startup())) {
779 return -1;
782 /* make RPC calls here */
784 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
785 ads->config.ldap_server_name)) )
787 goto done;
790 saf_store( cli->server_domain, cli->desthost );
792 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
793 goto done;
796 if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
797 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
798 global_myname(), ads->config.realm);
799 goto done;
802 d_printf("Disabled account for '%s' in realm '%s'\n",
803 global_myname(), ads->config.realm);
805 ret = 0;
807 done:
808 if ( cli )
809 cli_shutdown(cli);
811 ads_destroy(&ads);
812 TALLOC_FREE( ctx );
814 return ret;
817 static int net_ads_join_ok(void)
819 ADS_STRUCT *ads = NULL;
821 if (!secrets_init()) {
822 DEBUG(1,("Failed to initialise secrets database\n"));
823 return -1;
826 net_use_machine_password();
828 if (!(ads = ads_startup())) {
829 return -1;
832 ads_destroy(&ads);
833 return 0;
837 check that an existing join is OK
839 int net_ads_testjoin(int argc, const char **argv)
841 use_in_memory_ccache();
843 /* Display success or failure */
844 if (net_ads_join_ok() != 0) {
845 fprintf(stderr,"Join to domain is not valid\n");
846 return -1;
849 printf("Join is OK\n");
850 return 0;
853 /*******************************************************************
854 Simple configu checks before beginning the join
855 ********************************************************************/
857 static int check_ads_config( void )
859 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
860 d_printf("Host is not configured as a member server.\n");
861 return -1;
864 if (strlen(global_myname()) > 15) {
865 d_printf("Our netbios name can be at most 15 chars long, "
866 "\"%s\" is %d chars long\n",
867 global_myname(), strlen(global_myname()));
868 return -1;
871 if ( lp_security() == SEC_ADS && !*lp_realm()) {
872 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
873 "join to succeed.\n");
874 return -1;
877 if (!secrets_init()) {
878 DEBUG(1,("Failed to initialise secrets database\n"));
879 return -1;
882 return 0;
885 /*******************************************************************
886 Do the domain join
887 ********************************************************************/
889 static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
890 struct in_addr *ip, DOM_SID **dom_sid, const char *password )
892 int ret = -1;
893 struct cli_state *cli = NULL;
895 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
896 goto done;
898 saf_store( cli->server_domain, cli->desthost );
900 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
901 goto done;
903 if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
904 password, ND_TYPE_AD )) )
906 goto done;
909 ret = 0;
911 done:
912 if ( cli )
913 cli_shutdown(cli);
915 return ret;
918 /*******************************************************************
919 Set a machines dNSHostName and servicePrincipalName attributes
920 ********************************************************************/
922 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
924 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
925 char *host_upn, *new_dn;
926 ADS_MODLIST mods;
927 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
928 char *psp;
929 fstring my_fqdn;
930 LDAPMessage *res = NULL;
931 char *dn_string = NULL;
932 const char *machine_name = global_myname();
933 int count;
935 if ( !machine_name ) {
936 return ADS_ERROR(LDAP_NO_MEMORY);
939 /* Find our DN */
941 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
942 if (!ADS_ERR_OK(status))
943 return status;
945 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
946 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
947 return ADS_ERROR(LDAP_NO_MEMORY);
950 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
951 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
952 goto done;
955 new_dn = talloc_strdup(ctx, dn_string);
956 ads_memfree(ads_s, dn_string);
957 if (!new_dn) {
958 return ADS_ERROR(LDAP_NO_MEMORY);
961 /* Windows only creates HOST/shortname & HOST/fqdn. We create
962 the UPN as well so that 'kinit -k' will work. You can only
963 request a TGT for entries with a UPN in AD. */
965 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
966 goto done;
967 strupper_m(psp);
968 servicePrincipalName[0] = psp;
970 name_to_fqdn(my_fqdn, machine_name);
971 strlower_m(my_fqdn);
972 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
973 goto done;
974 servicePrincipalName[1] = psp;
976 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
977 goto done;
979 /* now do the mods */
981 if (!(mods = ads_init_mods(ctx))) {
982 goto done;
985 /* fields of primary importance */
987 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
988 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
990 status = ads_gen_mod(ads_s, new_dn, mods);
992 done:
993 ads_msgfree(ads_s, res);
995 return status;
999 /*******************************************************************
1000 join a domain using ADS (LDAP mods)
1001 ********************************************************************/
1003 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1005 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1006 char *dn, *ou_str;
1007 LDAPMessage *res = NULL;
1009 ou_str = ads_ou_string(ads, ou);
1010 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1011 free(ou_str);
1013 rc = ads_search_dn(ads, (void**)&res, dn, NULL);
1014 ads_msgfree(ads, res);
1016 if (ADS_ERR_OK(rc)) {
1017 /* Attempt to create the machine account and bail if this fails.
1018 Assume that the admin wants exactly what they requested */
1020 rc = ads_create_machine_acct( ads, global_myname(), dn );
1021 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1022 rc = ADS_SUCCESS;
1026 SAFE_FREE( dn );
1028 return rc;
1031 /*******************************************************************
1032 join a domain using ADS (LDAP mods)
1033 ********************************************************************/
1035 int net_ads_join(int argc, const char **argv)
1037 ADS_STRUCT *ads;
1038 ADS_STATUS status;
1039 char *machine_account = NULL;
1040 const char *short_domain_name = NULL;
1041 char *tmp_password, *password;
1042 struct cldap_netlogon_reply cldap_reply;
1043 TALLOC_CTX *ctx;
1044 DOM_SID *domain_sid = NULL;
1046 if ( check_ads_config() != 0 ) {
1047 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1048 return -1;
1051 if ( (ads = ads_startup()) == NULL ) {
1052 return -1;
1055 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1056 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1057 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1058 lp_realm());
1059 ads_destroy(&ads);
1060 return -1;
1063 if (!(ctx = talloc_init("net_ads_join"))) {
1064 DEBUG(0, ("Could not initialise talloc context\n"));
1065 return -1;
1068 /* If we were given an OU, try to create the machine in the OU account
1069 first and then do the normal RPC join */
1071 if ( argc > 0 ) {
1072 status = net_precreate_machine_acct( ads, argv[0] );
1073 if ( !ADS_ERR_OK(status) ) {
1074 d_fprintf( stderr, "Failed to pre-create the machine object "
1075 "in OU %s.\n", argv[0]);
1076 ads_destroy( &ads );
1077 return -1;
1081 /* Do the domain join here */
1083 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1084 password = talloc_strdup(ctx, tmp_password);
1086 if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1087 d_fprintf(stderr, "Failed to join domain!\n");
1088 return -1;
1091 /* Check the short name of the domain */
1093 ZERO_STRUCT( cldap_reply );
1095 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1096 ads->server.realm, &cldap_reply ) )
1098 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1099 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1100 d_printf("The workgroup in smb.conf does not match the short\n");
1101 d_printf("domain name obtained from the server.\n");
1102 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1103 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1105 } else {
1106 short_domain_name = lp_workgroup();
1109 d_printf("Using short domain name -- %s\n", short_domain_name);
1111 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1112 value from smb.conf and the string returned from the server. The former is
1113 neede to bootstrap winbindd's first connection to the DC to get the real
1114 short domain name --jerry */
1116 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1117 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1119 ads_destroy(&ads);
1120 return -1;
1123 /* Verify that everything is ok */
1125 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1126 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1127 return -1;
1130 /* create the dNSHostName & servicePrincipalName values */
1132 status = net_set_machine_spn( ctx, ads );
1133 if ( !ADS_ERR_OK(status) ) {
1134 d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
1135 d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
1136 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1138 /* don't fail */
1141 #if defined(HAVE_KRB5)
1142 if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
1143 d_fprintf(stderr, "asprintf failed\n");
1144 ads_destroy(&ads);
1145 return -1;
1148 if (!kerberos_derive_salting_principal(machine_account)) {
1149 DEBUG(1,("Failed to determine salting principal\n"));
1150 ads_destroy(&ads);
1151 return -1;
1154 if (!kerberos_derive_cifs_salting_principals()) {
1155 DEBUG(1,("Failed to determine salting principals\n"));
1156 ads_destroy(&ads);
1157 return -1;
1160 /* Now build the keytab, using the same ADS connection */
1161 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1162 DEBUG(1,("Error creating host keytab!\n"));
1164 #endif
1166 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1168 SAFE_FREE(machine_account);
1169 TALLOC_FREE( ctx );
1170 ads_destroy(&ads);
1172 return 0;
1175 /*******************************************************************
1176 ********************************************************************/
1178 int net_ads_printer_usage(int argc, const char **argv)
1180 d_printf(
1181 "\nnet ads printer search <printer>"
1182 "\n\tsearch for a printer in the directory\n"
1183 "\nnet ads printer info <printer> <server>"
1184 "\n\tlookup info in directory for printer on server"
1185 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1186 "\nnet ads printer publish <printername>"
1187 "\n\tpublish printer in directory"
1188 "\n\t(note: printer name is required)\n"
1189 "\nnet ads printer remove <printername>"
1190 "\n\tremove printer from directory"
1191 "\n\t(note: printer name is required)\n");
1192 return -1;
1195 /*******************************************************************
1196 ********************************************************************/
1198 static int net_ads_printer_search(int argc, const char **argv)
1200 ADS_STRUCT *ads;
1201 ADS_STATUS rc;
1202 void *res = NULL;
1204 if (!(ads = ads_startup())) {
1205 return -1;
1208 rc = ads_find_printers(ads, &res);
1210 if (!ADS_ERR_OK(rc)) {
1211 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1212 ads_msgfree(ads, res);
1213 ads_destroy(&ads);
1214 return -1;
1217 if (ads_count_replies(ads, res) == 0) {
1218 d_fprintf(stderr, "No results found\n");
1219 ads_msgfree(ads, res);
1220 ads_destroy(&ads);
1221 return -1;
1224 ads_dump(ads, res);
1225 ads_msgfree(ads, res);
1226 ads_destroy(&ads);
1227 return 0;
1230 static int net_ads_printer_info(int argc, const char **argv)
1232 ADS_STRUCT *ads;
1233 ADS_STATUS rc;
1234 const char *servername, *printername;
1235 void *res = NULL;
1237 if (!(ads = ads_startup())) {
1238 return -1;
1241 if (argc > 0) {
1242 printername = argv[0];
1243 } else {
1244 printername = "*";
1247 if (argc > 1) {
1248 servername = argv[1];
1249 } else {
1250 servername = global_myname();
1253 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1255 if (!ADS_ERR_OK(rc)) {
1256 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1257 ads_msgfree(ads, res);
1258 ads_destroy(&ads);
1259 return -1;
1262 if (ads_count_replies(ads, res) == 0) {
1263 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1264 ads_msgfree(ads, res);
1265 ads_destroy(&ads);
1266 return -1;
1269 ads_dump(ads, res);
1270 ads_msgfree(ads, res);
1271 ads_destroy(&ads);
1273 return 0;
1276 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1277 void *buf, size_t len)
1279 return;
1282 static int net_ads_printer_publish(int argc, const char **argv)
1284 ADS_STRUCT *ads;
1285 ADS_STATUS rc;
1286 const char *servername, *printername;
1287 struct cli_state *cli;
1288 struct rpc_pipe_client *pipe_hnd;
1289 struct in_addr server_ip;
1290 NTSTATUS nt_status;
1291 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1292 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1293 char *prt_dn, *srv_dn, **srv_cn;
1294 void *res = NULL;
1296 if (!(ads = ads_startup())) {
1297 return -1;
1300 if (argc < 1) {
1301 return net_ads_printer_usage(argc, argv);
1304 printername = argv[0];
1306 if (argc == 2) {
1307 servername = argv[1];
1308 } else {
1309 servername = global_myname();
1312 /* Get printer data from SPOOLSS */
1314 resolve_name(servername, &server_ip, 0x20);
1316 nt_status = cli_full_connection(&cli, global_myname(), servername,
1317 &server_ip, 0,
1318 "IPC$", "IPC",
1319 opt_user_name, opt_workgroup,
1320 opt_password ? opt_password : "",
1321 CLI_FULL_CONNECTION_USE_KERBEROS,
1322 Undefined, NULL);
1324 if (NT_STATUS_IS_ERR(nt_status)) {
1325 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1326 "for %s\n", servername, printername);
1327 ads_destroy(&ads);
1328 return -1;
1331 /* Publish on AD server */
1333 ads_find_machine_acct(ads, &res, servername);
1335 if (ads_count_replies(ads, res) == 0) {
1336 d_fprintf(stderr, "Could not find machine account for server %s\n",
1337 servername);
1338 ads_destroy(&ads);
1339 return -1;
1342 srv_dn = ldap_get_dn(ads->ld, res);
1343 srv_cn = ldap_explode_dn(srv_dn, 1);
1345 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1347 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1348 if (!pipe_hnd) {
1349 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1350 servername);
1351 ads_destroy(&ads);
1352 return -1;
1355 get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1356 printername);
1358 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1359 if (!ADS_ERR_OK(rc)) {
1360 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1361 ads_destroy(&ads);
1362 return -1;
1365 d_printf("published printer\n");
1366 ads_destroy(&ads);
1368 return 0;
1371 static int net_ads_printer_remove(int argc, const char **argv)
1373 ADS_STRUCT *ads;
1374 ADS_STATUS rc;
1375 const char *servername;
1376 char *prt_dn;
1377 void *res = NULL;
1379 if (!(ads = ads_startup())) {
1380 return -1;
1383 if (argc < 1) {
1384 return net_ads_printer_usage(argc, argv);
1387 if (argc > 1) {
1388 servername = argv[1];
1389 } else {
1390 servername = global_myname();
1393 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1395 if (!ADS_ERR_OK(rc)) {
1396 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1397 ads_msgfree(ads, res);
1398 ads_destroy(&ads);
1399 return -1;
1402 if (ads_count_replies(ads, res) == 0) {
1403 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1404 ads_msgfree(ads, res);
1405 ads_destroy(&ads);
1406 return -1;
1409 prt_dn = ads_get_dn(ads, res);
1410 ads_msgfree(ads, res);
1411 rc = ads_del_dn(ads, prt_dn);
1412 ads_memfree(ads, prt_dn);
1414 if (!ADS_ERR_OK(rc)) {
1415 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1416 ads_destroy(&ads);
1417 return -1;
1420 ads_destroy(&ads);
1421 return 0;
1424 static int net_ads_printer(int argc, const char **argv)
1426 struct functable func[] = {
1427 {"SEARCH", net_ads_printer_search},
1428 {"INFO", net_ads_printer_info},
1429 {"PUBLISH", net_ads_printer_publish},
1430 {"REMOVE", net_ads_printer_remove},
1431 {NULL, NULL}
1434 return net_run_function(argc, argv, func, net_ads_printer_usage);
1438 static int net_ads_password(int argc, const char **argv)
1440 ADS_STRUCT *ads;
1441 const char *auth_principal = opt_user_name;
1442 const char *auth_password = opt_password;
1443 char *realm = NULL;
1444 char *new_password = NULL;
1445 char *c, *prompt;
1446 const char *user;
1447 ADS_STATUS ret;
1449 if (opt_user_name == NULL || opt_password == NULL) {
1450 d_fprintf(stderr, "You must supply an administrator username/password\n");
1451 return -1;
1454 if (argc < 1) {
1455 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1456 return -1;
1459 user = argv[0];
1460 if (!strchr_m(user, '@')) {
1461 asprintf(&c, "%s@%s", argv[0], lp_realm());
1462 user = c;
1465 use_in_memory_ccache();
1466 c = strchr_m(auth_principal, '@');
1467 if (c) {
1468 realm = ++c;
1469 } else {
1470 realm = lp_realm();
1473 /* use the realm so we can eventually change passwords for users
1474 in realms other than default */
1475 if (!(ads = ads_init(realm, NULL, NULL))) {
1476 return -1;
1479 /* we don't actually need a full connect, but it's the easy way to
1480 fill in the KDC's addresss */
1481 ads_connect(ads);
1483 if (!ads || !ads->config.realm) {
1484 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1485 return -1;
1488 if (argv[1]) {
1489 new_password = (char *)argv[1];
1490 } else {
1491 asprintf(&prompt, "Enter new password for %s:", user);
1492 new_password = getpass(prompt);
1493 free(prompt);
1496 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1497 auth_password, user, new_password, ads->auth.time_offset);
1498 if (!ADS_ERR_OK(ret)) {
1499 d_fprintf(stderr, "Password change failed :-( ...\n");
1500 ads_destroy(&ads);
1501 return -1;
1504 d_printf("Password change for %s completed.\n", user);
1505 ads_destroy(&ads);
1507 return 0;
1510 int net_ads_changetrustpw(int argc, const char **argv)
1512 ADS_STRUCT *ads;
1513 char *host_principal;
1514 fstring my_name;
1515 ADS_STATUS ret;
1517 if (!secrets_init()) {
1518 DEBUG(1,("Failed to initialise secrets database\n"));
1519 return -1;
1522 net_use_machine_password();
1524 use_in_memory_ccache();
1526 if (!(ads = ads_startup())) {
1527 return -1;
1530 fstrcpy(my_name, global_myname());
1531 strlower_m(my_name);
1532 asprintf(&host_principal, "%s@%s", my_name, ads->config.realm);
1533 d_printf("Changing password for principal: HOST/%s\n", host_principal);
1535 ret = ads_change_trust_account_password(ads, host_principal);
1537 if (!ADS_ERR_OK(ret)) {
1538 d_fprintf(stderr, "Password change failed :-( ...\n");
1539 ads_destroy(&ads);
1540 SAFE_FREE(host_principal);
1541 return -1;
1544 d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
1546 if (lp_use_kerberos_keytab()) {
1547 d_printf("Attempting to update system keytab with new password.\n");
1548 if (ads_keytab_create_default(ads)) {
1549 d_printf("Failed to update system keytab.\n");
1553 ads_destroy(&ads);
1554 SAFE_FREE(host_principal);
1556 return 0;
1560 help for net ads search
1562 static int net_ads_search_usage(int argc, const char **argv)
1564 d_printf(
1565 "\nnet ads search <expression> <attributes...>\n"\
1566 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1567 "The expression is a standard LDAP search expression, and the\n"\
1568 "attributes are a list of LDAP fields to show in the results\n\n"\
1569 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1571 net_common_flags_usage(argc, argv);
1572 return -1;
1577 general ADS search function. Useful in diagnosing problems in ADS
1579 static int net_ads_search(int argc, const char **argv)
1581 ADS_STRUCT *ads;
1582 ADS_STATUS rc;
1583 const char *ldap_exp;
1584 const char **attrs;
1585 void *res = NULL;
1587 if (argc < 1) {
1588 return net_ads_search_usage(argc, argv);
1591 if (!(ads = ads_startup())) {
1592 return -1;
1595 ldap_exp = argv[0];
1596 attrs = (argv + 1);
1598 rc = ads_do_search_all(ads, ads->config.bind_path,
1599 LDAP_SCOPE_SUBTREE,
1600 ldap_exp, attrs, &res);
1601 if (!ADS_ERR_OK(rc)) {
1602 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1603 ads_destroy(&ads);
1604 return -1;
1607 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1609 /* dump the results */
1610 ads_dump(ads, res);
1612 ads_msgfree(ads, res);
1613 ads_destroy(&ads);
1615 return 0;
1620 help for net ads search
1622 static int net_ads_dn_usage(int argc, const char **argv)
1624 d_printf(
1625 "\nnet ads dn <dn> <attributes...>\n"\
1626 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1627 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1628 "to show in the results\n\n"\
1629 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1631 net_common_flags_usage(argc, argv);
1632 return -1;
1637 general ADS search function. Useful in diagnosing problems in ADS
1639 static int net_ads_dn(int argc, const char **argv)
1641 ADS_STRUCT *ads;
1642 ADS_STATUS rc;
1643 const char *dn;
1644 const char **attrs;
1645 void *res = NULL;
1647 if (argc < 1) {
1648 return net_ads_dn_usage(argc, argv);
1651 if (!(ads = ads_startup())) {
1652 return -1;
1655 dn = argv[0];
1656 attrs = (argv + 1);
1658 rc = ads_do_search_all(ads, dn,
1659 LDAP_SCOPE_BASE,
1660 "(objectclass=*)", attrs, &res);
1661 if (!ADS_ERR_OK(rc)) {
1662 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1663 ads_destroy(&ads);
1664 return -1;
1667 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1669 /* dump the results */
1670 ads_dump(ads, res);
1672 ads_msgfree(ads, res);
1673 ads_destroy(&ads);
1675 return 0;
1679 help for net ads sid search
1681 static int net_ads_sid_usage(int argc, const char **argv)
1683 d_printf(
1684 "\nnet ads sid <sid> <attributes...>\n"\
1685 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1686 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1687 "to show in the results\n\n"\
1688 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1690 net_common_flags_usage(argc, argv);
1691 return -1;
1696 general ADS search function. Useful in diagnosing problems in ADS
1698 static int net_ads_sid(int argc, const char **argv)
1700 ADS_STRUCT *ads;
1701 ADS_STATUS rc;
1702 const char *sid_string;
1703 const char **attrs;
1704 void *res = NULL;
1705 DOM_SID sid;
1707 if (argc < 1) {
1708 return net_ads_sid_usage(argc, argv);
1711 if (!(ads = ads_startup())) {
1712 return -1;
1715 sid_string = argv[0];
1716 attrs = (argv + 1);
1718 if (!string_to_sid(&sid, sid_string)) {
1719 d_fprintf(stderr, "could not convert sid\n");
1720 ads_destroy(&ads);
1721 return -1;
1724 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1725 if (!ADS_ERR_OK(rc)) {
1726 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1727 ads_destroy(&ads);
1728 return -1;
1731 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1733 /* dump the results */
1734 ads_dump(ads, res);
1736 ads_msgfree(ads, res);
1737 ads_destroy(&ads);
1739 return 0;
1743 static int net_ads_keytab_usage(int argc, const char **argv)
1745 d_printf(
1746 "net ads keytab <COMMAND>\n"\
1747 "<COMMAND> can be either:\n"\
1748 " CREATE Creates a fresh keytab\n"\
1749 " ADD Adds new service principal\n"\
1750 " FLUSH Flushes out all keytab entries\n"\
1751 " HELP Prints this help message\n"\
1752 "The ADD command will take arguments, the other commands\n"\
1753 "will not take any arguments. The arguments given to ADD\n"\
1754 "should be a list of principals to add. For example, \n"\
1755 " net ads keytab add srv1 srv2\n"\
1756 "will add principals for the services srv1 and srv2 to the\n"\
1757 "system's keytab.\n"\
1758 "\n"
1760 return -1;
1763 static int net_ads_keytab_flush(int argc, const char **argv)
1765 int ret;
1766 ADS_STRUCT *ads;
1768 if (!(ads = ads_startup())) {
1769 return -1;
1771 ret = ads_keytab_flush(ads);
1772 ads_destroy(&ads);
1773 return ret;
1776 static int net_ads_keytab_add(int argc, const char **argv)
1778 int i;
1779 int ret = 0;
1780 ADS_STRUCT *ads;
1782 d_printf("Processing principals to add...\n");
1783 if (!(ads = ads_startup())) {
1784 return -1;
1786 for (i = 0; i < argc; i++) {
1787 ret |= ads_keytab_add_entry(ads, argv[i]);
1789 ads_destroy(&ads);
1790 return ret;
1793 static int net_ads_keytab_create(int argc, const char **argv)
1795 ADS_STRUCT *ads;
1796 int ret;
1798 if (!(ads = ads_startup())) {
1799 return -1;
1801 ret = ads_keytab_create_default(ads);
1802 ads_destroy(&ads);
1803 return ret;
1806 int net_ads_keytab(int argc, const char **argv)
1808 struct functable func[] = {
1809 {"CREATE", net_ads_keytab_create},
1810 {"ADD", net_ads_keytab_add},
1811 {"FLUSH", net_ads_keytab_flush},
1812 {"HELP", net_ads_keytab_usage},
1813 {NULL, NULL}
1816 if (!lp_use_kerberos_keytab()) {
1817 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1818 use keytab functions.\n");
1821 return net_run_function(argc, argv, func, net_ads_keytab_usage);
1824 int net_ads_help(int argc, const char **argv)
1826 struct functable func[] = {
1827 {"USER", net_ads_user_usage},
1828 {"GROUP", net_ads_group_usage},
1829 {"PRINTER", net_ads_printer_usage},
1830 {"SEARCH", net_ads_search_usage},
1831 #if 0
1832 {"INFO", net_ads_info},
1833 {"JOIN", net_ads_join},
1834 {"JOIN2", net_ads_join2},
1835 {"LEAVE", net_ads_leave},
1836 {"STATUS", net_ads_status},
1837 {"PASSWORD", net_ads_password},
1838 {"CHANGETRUSTPW", net_ads_changetrustpw},
1839 #endif
1840 {NULL, NULL}
1843 return net_run_function(argc, argv, func, net_ads_usage);
1846 int net_ads(int argc, const char **argv)
1848 struct functable func[] = {
1849 {"INFO", net_ads_info},
1850 {"JOIN", net_ads_join},
1851 {"TESTJOIN", net_ads_testjoin},
1852 {"LEAVE", net_ads_leave},
1853 {"STATUS", net_ads_status},
1854 {"USER", net_ads_user},
1855 {"GROUP", net_ads_group},
1856 {"PASSWORD", net_ads_password},
1857 {"CHANGETRUSTPW", net_ads_changetrustpw},
1858 {"PRINTER", net_ads_printer},
1859 {"SEARCH", net_ads_search},
1860 {"DN", net_ads_dn},
1861 {"SID", net_ads_sid},
1862 {"WORKGROUP", net_ads_workgroup},
1863 {"LOOKUP", net_ads_lookup},
1864 {"KEYTAB", net_ads_keytab},
1865 {"HELP", net_ads_help},
1866 {NULL, NULL}
1869 return net_run_function(argc, argv, func, net_ads_usage);
1872 #else
1874 static int net_ads_noads(void)
1876 d_fprintf(stderr, "ADS support not compiled in\n");
1877 return -1;
1880 int net_ads_keytab(int argc, const char **argv)
1882 return net_ads_noads();
1885 int net_ads_usage(int argc, const char **argv)
1887 return net_ads_noads();
1890 int net_ads_help(int argc, const char **argv)
1892 return net_ads_noads();
1895 int net_ads_changetrustpw(int argc, const char **argv)
1897 return net_ads_noads();
1900 int net_ads_join(int argc, const char **argv)
1902 return net_ads_noads();
1905 int net_ads_user(int argc, const char **argv)
1907 return net_ads_noads();
1910 int net_ads_group(int argc, const char **argv)
1912 return net_ads_noads();
1915 /* this one shouldn't display a message */
1916 int net_ads_check(void)
1918 return -1;
1921 int net_ads(int argc, const char **argv)
1923 return net_ads_usage(argc, argv);
1926 #endif