r16465: merge a few miniro SAMBA_3_0 fixes
[Samba.git] / source / utils / net_ads.c
blob9d122a466bbe7adb320219b44a62141c6fe96ace
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 ads_destroy(&ads);
290 return NULL;
293 return ads;
298 Check to see if connection can be made via ads.
299 ads_startup() stores the password in opt_password if it needs to so
300 that rpc or rap can use it without re-prompting.
302 int net_ads_check(void)
304 ADS_STRUCT *ads;
305 ADS_STATUS status;
307 if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == NULL ) {
308 return -1;
311 ads->auth.flags |= ADS_AUTH_NO_BIND;
313 status = ads_connect(ads);
314 if ( !ADS_ERR_OK(status) ) {
315 return -1;
318 ads_destroy(&ads);
319 return 0;
323 determine the netbios workgroup name for a domain
325 static int net_ads_workgroup(int argc, const char **argv)
327 ADS_STRUCT *ads;
328 ADS_STATUS status;
329 const char *realm = NULL;
330 struct cldap_netlogon_reply reply;
332 if ( strequal(lp_workgroup(), opt_target_workgroup ) )
333 realm = lp_realm();
335 ads = ads_init(realm, opt_target_workgroup, opt_host);
336 if (ads) {
337 ads->auth.flags |= ADS_AUTH_NO_BIND;
340 status = ads_connect(ads);
341 if (!ADS_ERR_OK(status) || !ads) {
342 d_fprintf(stderr, "Didn't find the cldap server!\n");
343 return -1;
346 if (!ads->config.realm) {
347 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
348 ads->ldap_port = 389;
351 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
352 d_fprintf(stderr, "CLDAP query failed!\n");
353 return -1;
356 d_printf("Workgroup: %s\n", reply.netbios_domain);
358 ads_destroy(&ads);
360 return 0;
365 static BOOL usergrp_display(char *field, void **values, void *data_area)
367 char **disp_fields = (char **) data_area;
369 if (!field) { /* must be end of record */
370 if (disp_fields[0]) {
371 if (!strchr_m(disp_fields[0], '$')) {
372 if (disp_fields[1])
373 d_printf("%-21.21s %s\n",
374 disp_fields[0], disp_fields[1]);
375 else
376 d_printf("%s\n", disp_fields[0]);
379 SAFE_FREE(disp_fields[0]);
380 SAFE_FREE(disp_fields[1]);
381 return True;
383 if (!values) /* must be new field, indicate string field */
384 return True;
385 if (StrCaseCmp(field, "sAMAccountName") == 0) {
386 disp_fields[0] = SMB_STRDUP((char *) values[0]);
388 if (StrCaseCmp(field, "description") == 0)
389 disp_fields[1] = SMB_STRDUP((char *) values[0]);
390 return True;
393 static int net_ads_user_usage(int argc, const char **argv)
395 return net_help_user(argc, argv);
398 static int ads_user_add(int argc, const char **argv)
400 ADS_STRUCT *ads;
401 ADS_STATUS status;
402 char *upn, *userdn;
403 void *res=NULL;
404 int rc = -1;
406 if (argc < 1) return net_ads_user_usage(argc, argv);
408 if (!(ads = ads_startup())) {
409 return -1;
412 status = ads_find_user_acct(ads, &res, argv[0]);
414 if (!ADS_ERR_OK(status)) {
415 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
416 goto done;
419 if (ads_count_replies(ads, res)) {
420 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
421 goto done;
424 if (opt_container == NULL) {
425 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
428 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
430 if (!ADS_ERR_OK(status)) {
431 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
432 ads_errstr(status));
433 goto done;
436 /* if no password is to be set, we're done */
437 if (argc == 1) {
438 d_printf("User %s added\n", argv[0]);
439 rc = 0;
440 goto done;
443 /* try setting the password */
444 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
445 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
446 ads->auth.time_offset);
447 safe_free(upn);
448 if (ADS_ERR_OK(status)) {
449 d_printf("User %s added\n", argv[0]);
450 rc = 0;
451 goto done;
454 /* password didn't set, delete account */
455 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
456 argv[0], ads_errstr(status));
457 ads_msgfree(ads, res);
458 status=ads_find_user_acct(ads, &res, argv[0]);
459 if (ADS_ERR_OK(status)) {
460 userdn = ads_get_dn(ads, res);
461 ads_del_dn(ads, userdn);
462 ads_memfree(ads, userdn);
465 done:
466 if (res)
467 ads_msgfree(ads, res);
468 ads_destroy(&ads);
469 return rc;
472 static int ads_user_info(int argc, const char **argv)
474 ADS_STRUCT *ads;
475 ADS_STATUS rc;
476 void *res;
477 const char *attrs[] = {"memberOf", NULL};
478 char *searchstring=NULL;
479 char **grouplist;
480 char *escaped_user;
482 if (argc < 1) {
483 return net_ads_user_usage(argc, argv);
486 escaped_user = escape_ldap_string_alloc(argv[0]);
488 if (!escaped_user) {
489 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
490 return -1;
493 if (!(ads = ads_startup())) {
494 SAFE_FREE(escaped_user);
495 return -1;
498 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
499 rc = ads_search(ads, &res, searchstring, attrs);
500 safe_free(searchstring);
502 if (!ADS_ERR_OK(rc)) {
503 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
504 ads_destroy(&ads);
505 SAFE_FREE(escaped_user);
506 return -1;
509 grouplist = ldap_get_values(ads->ld, res, "memberOf");
511 if (grouplist) {
512 int i;
513 char **groupname;
514 for (i=0;grouplist[i];i++) {
515 groupname = ldap_explode_dn(grouplist[i], 1);
516 d_printf("%s\n", groupname[0]);
517 ldap_value_free(groupname);
519 ldap_value_free(grouplist);
522 ads_msgfree(ads, res);
523 ads_destroy(&ads);
524 SAFE_FREE(escaped_user);
525 return 0;
528 static int ads_user_delete(int argc, const char **argv)
530 ADS_STRUCT *ads;
531 ADS_STATUS rc;
532 void *res;
533 char *userdn;
535 if (argc < 1) {
536 return net_ads_user_usage(argc, argv);
539 if (!(ads = ads_startup())) {
540 return -1;
543 rc = ads_find_user_acct(ads, &res, argv[0]);
544 if (!ADS_ERR_OK(rc)) {
545 DEBUG(0, ("User %s does not exist\n", argv[0]));
546 ads_destroy(&ads);
547 return -1;
549 userdn = ads_get_dn(ads, res);
550 ads_msgfree(ads, res);
551 rc = ads_del_dn(ads, userdn);
552 ads_memfree(ads, userdn);
553 if (!ADS_ERR_OK(rc)) {
554 d_printf("User %s deleted\n", argv[0]);
555 ads_destroy(&ads);
556 return 0;
558 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
559 ads_errstr(rc));
560 ads_destroy(&ads);
561 return -1;
564 int net_ads_user(int argc, const char **argv)
566 struct functable func[] = {
567 {"ADD", ads_user_add},
568 {"INFO", ads_user_info},
569 {"DELETE", ads_user_delete},
570 {NULL, NULL}
572 ADS_STRUCT *ads;
573 ADS_STATUS rc;
574 const char *shortattrs[] = {"sAMAccountName", NULL};
575 const char *longattrs[] = {"sAMAccountName", "description", NULL};
576 char *disp_fields[2] = {NULL, NULL};
578 if (argc == 0) {
579 if (!(ads = ads_startup())) {
580 return -1;
583 if (opt_long_list_entries)
584 d_printf("\nUser name Comment"\
585 "\n-----------------------------\n");
587 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
588 LDAP_SCOPE_SUBTREE,
589 "(objectCategory=user)",
590 opt_long_list_entries ? longattrs :
591 shortattrs, usergrp_display,
592 disp_fields);
593 ads_destroy(&ads);
594 return 0;
597 return net_run_function(argc, argv, func, net_ads_user_usage);
600 static int net_ads_group_usage(int argc, const char **argv)
602 return net_help_group(argc, argv);
605 static int ads_group_add(int argc, const char **argv)
607 ADS_STRUCT *ads;
608 ADS_STATUS status;
609 void *res=NULL;
610 int rc = -1;
612 if (argc < 1) {
613 return net_ads_group_usage(argc, argv);
616 if (!(ads = ads_startup())) {
617 return -1;
620 status = ads_find_user_acct(ads, &res, argv[0]);
622 if (!ADS_ERR_OK(status)) {
623 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
624 goto done;
627 if (ads_count_replies(ads, res)) {
628 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
629 ads_msgfree(ads, res);
630 goto done;
633 if (opt_container == NULL) {
634 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
637 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
639 if (ADS_ERR_OK(status)) {
640 d_printf("Group %s added\n", argv[0]);
641 rc = 0;
642 } else {
643 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
644 ads_errstr(status));
647 done:
648 if (res)
649 ads_msgfree(ads, res);
650 ads_destroy(&ads);
651 return rc;
654 static int ads_group_delete(int argc, const char **argv)
656 ADS_STRUCT *ads;
657 ADS_STATUS rc;
658 void *res;
659 char *groupdn;
661 if (argc < 1) {
662 return net_ads_group_usage(argc, argv);
665 if (!(ads = ads_startup())) {
666 return -1;
669 rc = ads_find_user_acct(ads, &res, argv[0]);
670 if (!ADS_ERR_OK(rc)) {
671 DEBUG(0, ("Group %s does not exist\n", argv[0]));
672 ads_destroy(&ads);
673 return -1;
675 groupdn = ads_get_dn(ads, res);
676 ads_msgfree(ads, res);
677 rc = ads_del_dn(ads, groupdn);
678 ads_memfree(ads, groupdn);
679 if (!ADS_ERR_OK(rc)) {
680 d_printf("Group %s deleted\n", argv[0]);
681 ads_destroy(&ads);
682 return 0;
684 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
685 ads_errstr(rc));
686 ads_destroy(&ads);
687 return -1;
690 int net_ads_group(int argc, const char **argv)
692 struct functable func[] = {
693 {"ADD", ads_group_add},
694 {"DELETE", ads_group_delete},
695 {NULL, NULL}
697 ADS_STRUCT *ads;
698 ADS_STATUS rc;
699 const char *shortattrs[] = {"sAMAccountName", NULL};
700 const char *longattrs[] = {"sAMAccountName", "description", NULL};
701 char *disp_fields[2] = {NULL, NULL};
703 if (argc == 0) {
704 if (!(ads = ads_startup())) {
705 return -1;
708 if (opt_long_list_entries)
709 d_printf("\nGroup name Comment"\
710 "\n-----------------------------\n");
711 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
712 LDAP_SCOPE_SUBTREE,
713 "(objectCategory=group)",
714 opt_long_list_entries ? longattrs :
715 shortattrs, usergrp_display,
716 disp_fields);
718 ads_destroy(&ads);
719 return 0;
721 return net_run_function(argc, argv, func, net_ads_group_usage);
724 static int net_ads_status(int argc, const char **argv)
726 ADS_STRUCT *ads;
727 ADS_STATUS rc;
728 void *res;
730 if (!(ads = ads_startup())) {
731 return -1;
734 rc = ads_find_machine_acct(ads, &res, global_myname());
735 if (!ADS_ERR_OK(rc)) {
736 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
737 ads_destroy(&ads);
738 return -1;
741 if (ads_count_replies(ads, res) == 0) {
742 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
743 ads_destroy(&ads);
744 return -1;
747 ads_dump(ads, res);
748 ads_destroy(&ads);
749 return 0;
752 /*******************************************************************
753 Leave an AD domain. Windows XP disables the machine account.
754 We'll try the same. The old code would do an LDAP delete.
755 That only worked using the machine creds because added the machine
756 with full control to the computer object's ACL.
757 *******************************************************************/
758 static int net_ads_leave(int argc, const char **argv)
760 ADS_STRUCT *ads = NULL;
761 int ret = -1;
762 struct cli_state *cli = NULL;
763 TALLOC_CTX *ctx;
764 DOM_SID *dom_sid = NULL;
766 if (!secrets_init()) {
767 DEBUG(1,("Failed to initialise secrets database\n"));
768 return -1;
771 if (!(ctx = talloc_init("net_ads_leave"))) {
772 DEBUG(0, ("Could not initialise talloc context\n"));
773 return -1;
776 /* The finds a DC and takes care of getting the
777 user creds if necessary */
779 if (!(ads = ads_startup())) {
780 return -1;
783 /* make RPC calls here */
785 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
786 ads->config.ldap_server_name)) )
788 goto done;
791 saf_store( cli->server_domain, cli->desthost );
793 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
794 goto done;
797 if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
798 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
799 global_myname(), ads->config.realm);
800 goto done;
803 d_printf("Disabled account for '%s' in realm '%s'\n",
804 global_myname(), ads->config.realm);
806 ret = 0;
808 done:
809 if ( cli )
810 cli_shutdown(cli);
812 ads_destroy(&ads);
813 TALLOC_FREE( ctx );
815 return ret;
818 static int net_ads_join_ok(void)
820 ADS_STRUCT *ads = NULL;
822 if (!secrets_init()) {
823 DEBUG(1,("Failed to initialise secrets database\n"));
824 return -1;
827 net_use_machine_password();
829 if (!(ads = ads_startup())) {
830 return -1;
833 ads_destroy(&ads);
834 return 0;
838 check that an existing join is OK
840 int net_ads_testjoin(int argc, const char **argv)
842 use_in_memory_ccache();
844 /* Display success or failure */
845 if (net_ads_join_ok() != 0) {
846 fprintf(stderr,"Join to domain is not valid\n");
847 return -1;
850 printf("Join is OK\n");
851 return 0;
854 /*******************************************************************
855 Simple configu checks before beginning the join
856 ********************************************************************/
858 static int check_ads_config( void )
860 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
861 d_printf("Host is not configured as a member server.\n");
862 return -1;
865 if (strlen(global_myname()) > 15) {
866 d_printf("Our netbios name can be at most 15 chars long, "
867 "\"%s\" is %u chars long\n",
868 global_myname(), (unsigned int)strlen(global_myname()));
869 return -1;
872 if ( lp_security() == SEC_ADS && !*lp_realm()) {
873 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
874 "join to succeed.\n");
875 return -1;
878 if (!secrets_init()) {
879 DEBUG(1,("Failed to initialise secrets database\n"));
880 return -1;
883 return 0;
886 /*******************************************************************
887 Do the domain join
888 ********************************************************************/
890 static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
891 struct in_addr *ip, DOM_SID **dom_sid, const char *password )
893 int ret = -1;
894 struct cli_state *cli = NULL;
896 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
897 goto done;
899 saf_store( cli->server_domain, cli->desthost );
901 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
902 goto done;
904 if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
905 password, ND_TYPE_AD )) )
907 goto done;
910 ret = 0;
912 done:
913 if ( cli )
914 cli_shutdown(cli);
916 return ret;
919 /*******************************************************************
920 Set a machines dNSHostName and servicePrincipalName attributes
921 ********************************************************************/
923 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
925 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
926 char *host_upn, *new_dn;
927 ADS_MODLIST mods;
928 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
929 char *psp;
930 fstring my_fqdn;
931 LDAPMessage *res = NULL;
932 char *dn_string = NULL;
933 const char *machine_name = global_myname();
934 int count;
936 if ( !machine_name ) {
937 return ADS_ERROR(LDAP_NO_MEMORY);
940 /* Find our DN */
942 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
943 if (!ADS_ERR_OK(status))
944 return status;
946 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
947 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
948 return ADS_ERROR(LDAP_NO_MEMORY);
951 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
952 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
953 goto done;
956 new_dn = talloc_strdup(ctx, dn_string);
957 ads_memfree(ads_s, dn_string);
958 if (!new_dn) {
959 return ADS_ERROR(LDAP_NO_MEMORY);
962 /* Windows only creates HOST/shortname & HOST/fqdn. We create
963 the UPN as well so that 'kinit -k' will work. You can only
964 request a TGT for entries with a UPN in AD. */
966 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
967 goto done;
968 strupper_m(psp);
969 servicePrincipalName[0] = psp;
971 name_to_fqdn(my_fqdn, machine_name);
972 strlower_m(my_fqdn);
973 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
974 goto done;
975 servicePrincipalName[1] = psp;
977 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
978 goto done;
980 /* now do the mods */
982 if (!(mods = ads_init_mods(ctx))) {
983 goto done;
986 /* fields of primary importance */
988 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
989 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
991 status = ads_gen_mod(ads_s, new_dn, mods);
993 done:
994 ads_msgfree(ads_s, res);
996 return status;
1000 /*******************************************************************
1001 join a domain using ADS (LDAP mods)
1002 ********************************************************************/
1004 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1006 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1007 char *dn, *ou_str;
1008 LDAPMessage *res = NULL;
1010 ou_str = ads_ou_string(ads, ou);
1011 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1012 free(ou_str);
1014 rc = ads_search_dn(ads, (void**)&res, dn, NULL);
1015 ads_msgfree(ads, res);
1017 if (ADS_ERR_OK(rc)) {
1018 /* Attempt to create the machine account and bail if this fails.
1019 Assume that the admin wants exactly what they requested */
1021 rc = ads_create_machine_acct( ads, global_myname(), dn );
1022 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1023 rc = ADS_SUCCESS;
1027 SAFE_FREE( dn );
1029 return rc;
1032 /*******************************************************************
1033 join a domain using ADS (LDAP mods)
1034 ********************************************************************/
1036 int net_ads_join(int argc, const char **argv)
1038 ADS_STRUCT *ads;
1039 ADS_STATUS status;
1040 char *machine_account = NULL;
1041 const char *short_domain_name = NULL;
1042 char *tmp_password, *password;
1043 struct cldap_netlogon_reply cldap_reply;
1044 TALLOC_CTX *ctx;
1045 DOM_SID *domain_sid = NULL;
1047 if ( check_ads_config() != 0 ) {
1048 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1049 return -1;
1052 if ( (ads = ads_startup()) == NULL ) {
1053 return -1;
1056 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1057 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1058 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1059 lp_realm());
1060 ads_destroy(&ads);
1061 return -1;
1064 if (!(ctx = talloc_init("net_ads_join"))) {
1065 DEBUG(0, ("Could not initialise talloc context\n"));
1066 return -1;
1069 /* If we were given an OU, try to create the machine in the OU account
1070 first and then do the normal RPC join */
1072 if ( argc > 0 ) {
1073 status = net_precreate_machine_acct( ads, argv[0] );
1074 if ( !ADS_ERR_OK(status) ) {
1075 d_fprintf( stderr, "Failed to pre-create the machine object "
1076 "in OU %s.\n", argv[0]);
1077 ads_destroy( &ads );
1078 return -1;
1082 /* Do the domain join here */
1084 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1085 password = talloc_strdup(ctx, tmp_password);
1087 if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1088 d_fprintf(stderr, "Failed to join domain!\n");
1089 return -1;
1092 /* Check the short name of the domain */
1094 ZERO_STRUCT( cldap_reply );
1096 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1097 ads->server.realm, &cldap_reply ) )
1099 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1100 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1101 d_printf("The workgroup in smb.conf does not match the short\n");
1102 d_printf("domain name obtained from the server.\n");
1103 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1104 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1106 } else {
1107 short_domain_name = lp_workgroup();
1110 d_printf("Using short domain name -- %s\n", short_domain_name);
1112 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1113 value from smb.conf and the string returned from the server. The former is
1114 neede to bootstrap winbindd's first connection to the DC to get the real
1115 short domain name --jerry */
1117 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1118 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1120 ads_destroy(&ads);
1121 return -1;
1124 /* Verify that everything is ok */
1126 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1127 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1128 return -1;
1131 /* create the dNSHostName & servicePrincipalName values */
1133 status = net_set_machine_spn( ctx, ads );
1134 if ( !ADS_ERR_OK(status) ) {
1135 d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
1136 d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
1137 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1139 /* don't fail */
1142 #if defined(HAVE_KRB5)
1143 if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
1144 d_fprintf(stderr, "asprintf failed\n");
1145 ads_destroy(&ads);
1146 return -1;
1149 if (!kerberos_derive_salting_principal(machine_account)) {
1150 DEBUG(1,("Failed to determine salting principal\n"));
1151 ads_destroy(&ads);
1152 return -1;
1155 if (!kerberos_derive_cifs_salting_principals()) {
1156 DEBUG(1,("Failed to determine salting principals\n"));
1157 ads_destroy(&ads);
1158 return -1;
1161 /* Now build the keytab, using the same ADS connection */
1162 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1163 DEBUG(1,("Error creating host keytab!\n"));
1165 #endif
1167 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1169 SAFE_FREE(machine_account);
1170 TALLOC_FREE( ctx );
1171 ads_destroy(&ads);
1173 return 0;
1176 /*******************************************************************
1177 ********************************************************************/
1179 int net_ads_printer_usage(int argc, const char **argv)
1181 d_printf(
1182 "\nnet ads printer search <printer>"
1183 "\n\tsearch for a printer in the directory\n"
1184 "\nnet ads printer info <printer> <server>"
1185 "\n\tlookup info in directory for printer on server"
1186 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1187 "\nnet ads printer publish <printername>"
1188 "\n\tpublish printer in directory"
1189 "\n\t(note: printer name is required)\n"
1190 "\nnet ads printer remove <printername>"
1191 "\n\tremove printer from directory"
1192 "\n\t(note: printer name is required)\n");
1193 return -1;
1196 /*******************************************************************
1197 ********************************************************************/
1199 static int net_ads_printer_search(int argc, const char **argv)
1201 ADS_STRUCT *ads;
1202 ADS_STATUS rc;
1203 void *res = NULL;
1205 if (!(ads = ads_startup())) {
1206 return -1;
1209 rc = ads_find_printers(ads, &res);
1211 if (!ADS_ERR_OK(rc)) {
1212 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1213 ads_msgfree(ads, res);
1214 ads_destroy(&ads);
1215 return -1;
1218 if (ads_count_replies(ads, res) == 0) {
1219 d_fprintf(stderr, "No results found\n");
1220 ads_msgfree(ads, res);
1221 ads_destroy(&ads);
1222 return -1;
1225 ads_dump(ads, res);
1226 ads_msgfree(ads, res);
1227 ads_destroy(&ads);
1228 return 0;
1231 static int net_ads_printer_info(int argc, const char **argv)
1233 ADS_STRUCT *ads;
1234 ADS_STATUS rc;
1235 const char *servername, *printername;
1236 void *res = NULL;
1238 if (!(ads = ads_startup())) {
1239 return -1;
1242 if (argc > 0) {
1243 printername = argv[0];
1244 } else {
1245 printername = "*";
1248 if (argc > 1) {
1249 servername = argv[1];
1250 } else {
1251 servername = global_myname();
1254 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1256 if (!ADS_ERR_OK(rc)) {
1257 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1258 ads_msgfree(ads, res);
1259 ads_destroy(&ads);
1260 return -1;
1263 if (ads_count_replies(ads, res) == 0) {
1264 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1265 ads_msgfree(ads, res);
1266 ads_destroy(&ads);
1267 return -1;
1270 ads_dump(ads, res);
1271 ads_msgfree(ads, res);
1272 ads_destroy(&ads);
1274 return 0;
1277 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1278 void *buf, size_t len)
1280 return;
1283 static int net_ads_printer_publish(int argc, const char **argv)
1285 ADS_STRUCT *ads;
1286 ADS_STATUS rc;
1287 const char *servername, *printername;
1288 struct cli_state *cli;
1289 struct rpc_pipe_client *pipe_hnd;
1290 struct in_addr server_ip;
1291 NTSTATUS nt_status;
1292 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1293 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1294 char *prt_dn, *srv_dn, **srv_cn;
1295 void *res = NULL;
1297 if (!(ads = ads_startup())) {
1298 return -1;
1301 if (argc < 1) {
1302 return net_ads_printer_usage(argc, argv);
1305 printername = argv[0];
1307 if (argc == 2) {
1308 servername = argv[1];
1309 } else {
1310 servername = global_myname();
1313 /* Get printer data from SPOOLSS */
1315 resolve_name(servername, &server_ip, 0x20);
1317 nt_status = cli_full_connection(&cli, global_myname(), servername,
1318 &server_ip, 0,
1319 "IPC$", "IPC",
1320 opt_user_name, opt_workgroup,
1321 opt_password ? opt_password : "",
1322 CLI_FULL_CONNECTION_USE_KERBEROS,
1323 Undefined, NULL);
1325 if (NT_STATUS_IS_ERR(nt_status)) {
1326 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1327 "for %s\n", servername, printername);
1328 ads_destroy(&ads);
1329 return -1;
1332 /* Publish on AD server */
1334 ads_find_machine_acct(ads, &res, servername);
1336 if (ads_count_replies(ads, res) == 0) {
1337 d_fprintf(stderr, "Could not find machine account for server %s\n",
1338 servername);
1339 ads_destroy(&ads);
1340 return -1;
1343 srv_dn = ldap_get_dn(ads->ld, res);
1344 srv_cn = ldap_explode_dn(srv_dn, 1);
1346 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1348 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1349 if (!pipe_hnd) {
1350 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1351 servername);
1352 ads_destroy(&ads);
1353 return -1;
1356 get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1357 printername);
1359 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1360 if (!ADS_ERR_OK(rc)) {
1361 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1362 ads_destroy(&ads);
1363 return -1;
1366 d_printf("published printer\n");
1367 ads_destroy(&ads);
1369 return 0;
1372 static int net_ads_printer_remove(int argc, const char **argv)
1374 ADS_STRUCT *ads;
1375 ADS_STATUS rc;
1376 const char *servername;
1377 char *prt_dn;
1378 void *res = NULL;
1380 if (!(ads = ads_startup())) {
1381 return -1;
1384 if (argc < 1) {
1385 return net_ads_printer_usage(argc, argv);
1388 if (argc > 1) {
1389 servername = argv[1];
1390 } else {
1391 servername = global_myname();
1394 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1396 if (!ADS_ERR_OK(rc)) {
1397 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1398 ads_msgfree(ads, res);
1399 ads_destroy(&ads);
1400 return -1;
1403 if (ads_count_replies(ads, res) == 0) {
1404 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1405 ads_msgfree(ads, res);
1406 ads_destroy(&ads);
1407 return -1;
1410 prt_dn = ads_get_dn(ads, res);
1411 ads_msgfree(ads, res);
1412 rc = ads_del_dn(ads, prt_dn);
1413 ads_memfree(ads, prt_dn);
1415 if (!ADS_ERR_OK(rc)) {
1416 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1417 ads_destroy(&ads);
1418 return -1;
1421 ads_destroy(&ads);
1422 return 0;
1425 static int net_ads_printer(int argc, const char **argv)
1427 struct functable func[] = {
1428 {"SEARCH", net_ads_printer_search},
1429 {"INFO", net_ads_printer_info},
1430 {"PUBLISH", net_ads_printer_publish},
1431 {"REMOVE", net_ads_printer_remove},
1432 {NULL, NULL}
1435 return net_run_function(argc, argv, func, net_ads_printer_usage);
1439 static int net_ads_password(int argc, const char **argv)
1441 ADS_STRUCT *ads;
1442 const char *auth_principal = opt_user_name;
1443 const char *auth_password = opt_password;
1444 char *realm = NULL;
1445 char *new_password = NULL;
1446 char *c, *prompt;
1447 const char *user;
1448 ADS_STATUS ret;
1450 if (opt_user_name == NULL || opt_password == NULL) {
1451 d_fprintf(stderr, "You must supply an administrator username/password\n");
1452 return -1;
1455 if (argc < 1) {
1456 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1457 return -1;
1460 user = argv[0];
1461 if (!strchr_m(user, '@')) {
1462 asprintf(&c, "%s@%s", argv[0], lp_realm());
1463 user = c;
1466 use_in_memory_ccache();
1467 c = strchr_m(auth_principal, '@');
1468 if (c) {
1469 realm = ++c;
1470 } else {
1471 realm = lp_realm();
1474 /* use the realm so we can eventually change passwords for users
1475 in realms other than default */
1476 if (!(ads = ads_init(realm, opt_workgroup, NULL))) {
1477 return -1;
1480 /* we don't actually need a full connect, but it's the easy way to
1481 fill in the KDC's addresss */
1482 ads_connect(ads);
1484 if (!ads || !ads->config.realm) {
1485 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1486 return -1;
1489 if (argv[1]) {
1490 new_password = (char *)argv[1];
1491 } else {
1492 asprintf(&prompt, "Enter new password for %s:", user);
1493 new_password = getpass(prompt);
1494 free(prompt);
1497 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1498 auth_password, user, new_password, ads->auth.time_offset);
1499 if (!ADS_ERR_OK(ret)) {
1500 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1501 ads_destroy(&ads);
1502 return -1;
1505 d_printf("Password change for %s completed.\n", user);
1506 ads_destroy(&ads);
1508 return 0;
1511 int net_ads_changetrustpw(int argc, const char **argv)
1513 ADS_STRUCT *ads;
1514 char *host_principal;
1515 fstring my_name;
1516 ADS_STATUS ret;
1518 if (!secrets_init()) {
1519 DEBUG(1,("Failed to initialise secrets database\n"));
1520 return -1;
1523 net_use_machine_password();
1525 use_in_memory_ccache();
1527 if (!(ads = ads_startup())) {
1528 return -1;
1531 fstrcpy(my_name, global_myname());
1532 strlower_m(my_name);
1533 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1534 d_printf("Changing password for principal: %s\n", host_principal);
1536 ret = ads_change_trust_account_password(ads, host_principal);
1538 if (!ADS_ERR_OK(ret)) {
1539 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1540 ads_destroy(&ads);
1541 SAFE_FREE(host_principal);
1542 return -1;
1545 d_printf("Password change for principal %s succeeded.\n", host_principal);
1547 if (lp_use_kerberos_keytab()) {
1548 d_printf("Attempting to update system keytab with new password.\n");
1549 if (ads_keytab_create_default(ads)) {
1550 d_printf("Failed to update system keytab.\n");
1554 ads_destroy(&ads);
1555 SAFE_FREE(host_principal);
1557 return 0;
1561 help for net ads search
1563 static int net_ads_search_usage(int argc, const char **argv)
1565 d_printf(
1566 "\nnet ads search <expression> <attributes...>\n"\
1567 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1568 "The expression is a standard LDAP search expression, and the\n"\
1569 "attributes are a list of LDAP fields to show in the results\n\n"\
1570 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1572 net_common_flags_usage(argc, argv);
1573 return -1;
1578 general ADS search function. Useful in diagnosing problems in ADS
1580 static int net_ads_search(int argc, const char **argv)
1582 ADS_STRUCT *ads;
1583 ADS_STATUS rc;
1584 const char *ldap_exp;
1585 const char **attrs;
1586 void *res = NULL;
1588 if (argc < 1) {
1589 return net_ads_search_usage(argc, argv);
1592 if (!(ads = ads_startup())) {
1593 return -1;
1596 ldap_exp = argv[0];
1597 attrs = (argv + 1);
1599 rc = ads_do_search_all(ads, ads->config.bind_path,
1600 LDAP_SCOPE_SUBTREE,
1601 ldap_exp, attrs, &res);
1602 if (!ADS_ERR_OK(rc)) {
1603 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1604 ads_destroy(&ads);
1605 return -1;
1608 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1610 /* dump the results */
1611 ads_dump(ads, res);
1613 ads_msgfree(ads, res);
1614 ads_destroy(&ads);
1616 return 0;
1621 help for net ads search
1623 static int net_ads_dn_usage(int argc, const char **argv)
1625 d_printf(
1626 "\nnet ads dn <dn> <attributes...>\n"\
1627 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1628 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1629 "to show in the results\n\n"\
1630 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1632 net_common_flags_usage(argc, argv);
1633 return -1;
1638 general ADS search function. Useful in diagnosing problems in ADS
1640 static int net_ads_dn(int argc, const char **argv)
1642 ADS_STRUCT *ads;
1643 ADS_STATUS rc;
1644 const char *dn;
1645 const char **attrs;
1646 void *res = NULL;
1648 if (argc < 1) {
1649 return net_ads_dn_usage(argc, argv);
1652 if (!(ads = ads_startup())) {
1653 return -1;
1656 dn = argv[0];
1657 attrs = (argv + 1);
1659 rc = ads_do_search_all(ads, dn,
1660 LDAP_SCOPE_BASE,
1661 "(objectclass=*)", attrs, &res);
1662 if (!ADS_ERR_OK(rc)) {
1663 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1664 ads_destroy(&ads);
1665 return -1;
1668 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1670 /* dump the results */
1671 ads_dump(ads, res);
1673 ads_msgfree(ads, res);
1674 ads_destroy(&ads);
1676 return 0;
1680 help for net ads sid search
1682 static int net_ads_sid_usage(int argc, const char **argv)
1684 d_printf(
1685 "\nnet ads sid <sid> <attributes...>\n"\
1686 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1687 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1688 "to show in the results\n\n"\
1689 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1691 net_common_flags_usage(argc, argv);
1692 return -1;
1697 general ADS search function. Useful in diagnosing problems in ADS
1699 static int net_ads_sid(int argc, const char **argv)
1701 ADS_STRUCT *ads;
1702 ADS_STATUS rc;
1703 const char *sid_string;
1704 const char **attrs;
1705 void *res = NULL;
1706 DOM_SID sid;
1708 if (argc < 1) {
1709 return net_ads_sid_usage(argc, argv);
1712 if (!(ads = ads_startup())) {
1713 return -1;
1716 sid_string = argv[0];
1717 attrs = (argv + 1);
1719 if (!string_to_sid(&sid, sid_string)) {
1720 d_fprintf(stderr, "could not convert sid\n");
1721 ads_destroy(&ads);
1722 return -1;
1725 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1726 if (!ADS_ERR_OK(rc)) {
1727 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1728 ads_destroy(&ads);
1729 return -1;
1732 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1734 /* dump the results */
1735 ads_dump(ads, res);
1737 ads_msgfree(ads, res);
1738 ads_destroy(&ads);
1740 return 0;
1744 static int net_ads_keytab_usage(int argc, const char **argv)
1746 d_printf(
1747 "net ads keytab <COMMAND>\n"\
1748 "<COMMAND> can be either:\n"\
1749 " CREATE Creates a fresh keytab\n"\
1750 " ADD Adds new service principal\n"\
1751 " FLUSH Flushes out all keytab entries\n"\
1752 " HELP Prints this help message\n"\
1753 "The ADD command will take arguments, the other commands\n"\
1754 "will not take any arguments. The arguments given to ADD\n"\
1755 "should be a list of principals to add. For example, \n"\
1756 " net ads keytab add srv1 srv2\n"\
1757 "will add principals for the services srv1 and srv2 to the\n"\
1758 "system's keytab.\n"\
1759 "\n"
1761 return -1;
1764 static int net_ads_keytab_flush(int argc, const char **argv)
1766 int ret;
1767 ADS_STRUCT *ads;
1769 if (!(ads = ads_startup())) {
1770 return -1;
1772 ret = ads_keytab_flush(ads);
1773 ads_destroy(&ads);
1774 return ret;
1777 static int net_ads_keytab_add(int argc, const char **argv)
1779 int i;
1780 int ret = 0;
1781 ADS_STRUCT *ads;
1783 d_printf("Processing principals to add...\n");
1784 if (!(ads = ads_startup())) {
1785 return -1;
1787 for (i = 0; i < argc; i++) {
1788 ret |= ads_keytab_add_entry(ads, argv[i]);
1790 ads_destroy(&ads);
1791 return ret;
1794 static int net_ads_keytab_create(int argc, const char **argv)
1796 ADS_STRUCT *ads;
1797 int ret;
1799 if (!(ads = ads_startup())) {
1800 return -1;
1802 ret = ads_keytab_create_default(ads);
1803 ads_destroy(&ads);
1804 return ret;
1807 int net_ads_keytab(int argc, const char **argv)
1809 struct functable func[] = {
1810 {"CREATE", net_ads_keytab_create},
1811 {"ADD", net_ads_keytab_add},
1812 {"FLUSH", net_ads_keytab_flush},
1813 {"HELP", net_ads_keytab_usage},
1814 {NULL, NULL}
1817 if (!lp_use_kerberos_keytab()) {
1818 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1819 use keytab functions.\n");
1822 return net_run_function(argc, argv, func, net_ads_keytab_usage);
1825 int net_ads_help(int argc, const char **argv)
1827 struct functable func[] = {
1828 {"USER", net_ads_user_usage},
1829 {"GROUP", net_ads_group_usage},
1830 {"PRINTER", net_ads_printer_usage},
1831 {"SEARCH", net_ads_search_usage},
1832 #if 0
1833 {"INFO", net_ads_info},
1834 {"JOIN", net_ads_join},
1835 {"JOIN2", net_ads_join2},
1836 {"LEAVE", net_ads_leave},
1837 {"STATUS", net_ads_status},
1838 {"PASSWORD", net_ads_password},
1839 {"CHANGETRUSTPW", net_ads_changetrustpw},
1840 #endif
1841 {NULL, NULL}
1844 return net_run_function(argc, argv, func, net_ads_usage);
1847 int net_ads(int argc, const char **argv)
1849 struct functable func[] = {
1850 {"INFO", net_ads_info},
1851 {"JOIN", net_ads_join},
1852 {"TESTJOIN", net_ads_testjoin},
1853 {"LEAVE", net_ads_leave},
1854 {"STATUS", net_ads_status},
1855 {"USER", net_ads_user},
1856 {"GROUP", net_ads_group},
1857 {"PASSWORD", net_ads_password},
1858 {"CHANGETRUSTPW", net_ads_changetrustpw},
1859 {"PRINTER", net_ads_printer},
1860 {"SEARCH", net_ads_search},
1861 {"DN", net_ads_dn},
1862 {"SID", net_ads_sid},
1863 {"WORKGROUP", net_ads_workgroup},
1864 {"LOOKUP", net_ads_lookup},
1865 {"KEYTAB", net_ads_keytab},
1866 {"HELP", net_ads_help},
1867 {NULL, NULL}
1870 return net_run_function(argc, argv, func, net_ads_usage);
1873 #else
1875 static int net_ads_noads(void)
1877 d_fprintf(stderr, "ADS support not compiled in\n");
1878 return -1;
1881 int net_ads_keytab(int argc, const char **argv)
1883 return net_ads_noads();
1886 int net_ads_usage(int argc, const char **argv)
1888 return net_ads_noads();
1891 int net_ads_help(int argc, const char **argv)
1893 return net_ads_noads();
1896 int net_ads_changetrustpw(int argc, const char **argv)
1898 return net_ads_noads();
1901 int net_ads_join(int argc, const char **argv)
1903 return net_ads_noads();
1906 int net_ads_user(int argc, const char **argv)
1908 return net_ads_noads();
1911 int net_ads_group(int argc, const char **argv)
1913 return net_ads_noads();
1916 /* this one shouldn't display a message */
1917 int net_ads_check(void)
1919 return -1;
1922 int net_ads(int argc, const char **argv)
1924 return net_ads_usage(argc, argv);
1927 #endif