2 Samba Unix/Linux SMB client library
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.
25 #include "utils/net.h"
29 int net_ads_usage(int argc, const char **argv)
31 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
32 d_printf(" Join the local machine to a ADS realm\n");
34 d_printf(" Remove the local machine from a ADS realm\n");
35 d_printf("testjoin\n");
36 d_printf(" Validates the machine account in the domain\n");
38 d_printf(" List, add, or delete users in the realm\n");
40 d_printf(" List, add, or delete groups in the realm\n");
42 d_printf(" Displays details regarding a specific AD server\n");
44 d_printf(" Display details regarding the machine's account in AD\n");
46 d_printf(" Performs CLDAP query of AD domain controllers\n");
47 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
48 d_printf(" Change a user's password using an admin account\n");
49 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
50 d_printf("changetrustpw\n");
51 d_printf(" Change the trust account password of this machine in the AD tree\n");
52 d_printf("printer [info | publish | remove] <printername> <servername>\n");
53 d_printf(" Lookup, add, or remove directory entry for a printer\n");
54 d_printf("{search,dn,sid}\n");
55 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
57 d_printf(" Manage a local keytab file based on the machine account in AD\n");
62 /* when we do not have sufficient input parameters to contact a remote domain
63 * we always fall back to our own realm - Guenther*/
65 static const char *assume_own_realm(void)
67 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
75 do a cldap netlogon query
77 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
79 struct cldap_netlogon_reply reply;
81 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
82 d_fprintf(stderr, "CLDAP query failed!\n");
86 d_printf("Information for Domain Controller: %s\n\n",
87 inet_ntoa(ads->ldap_ip));
89 d_printf("Response Type: ");
91 case SAMLOGON_AD_UNK_R:
92 d_printf("SAMLOGON\n");
95 d_printf("SAMLOGON_USER\n");
98 d_printf("0x%x\n", reply.type);
101 d_printf("GUID: %s\n",
102 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
105 "\tIs a GC of the forest: %s\n"
106 "\tIs an LDAP server: %s\n"
107 "\tSupports DS: %s\n"
108 "\tIs running a KDC: %s\n"
109 "\tIs running time services: %s\n"
110 "\tIs the closest DC: %s\n"
111 "\tIs writable: %s\n"
112 "\tHas a hardware clock: %s\n"
113 "\tIs a non-domain NC serviced by LDAP server: %s\n",
114 (reply.flags & ADS_PDC) ? "yes" : "no",
115 (reply.flags & ADS_GC) ? "yes" : "no",
116 (reply.flags & ADS_LDAP) ? "yes" : "no",
117 (reply.flags & ADS_DS) ? "yes" : "no",
118 (reply.flags & ADS_KDC) ? "yes" : "no",
119 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
120 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
121 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
122 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
123 (reply.flags & ADS_NDNC) ? "yes" : "no");
125 printf("Forest:\t\t\t%s\n", reply.forest);
126 printf("Domain:\t\t\t%s\n", reply.domain);
127 printf("Domain Controller:\t%s\n", reply.hostname);
129 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
130 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
132 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
133 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
135 printf("Site Name:\t\t%s\n", reply.site_name);
136 printf("Site Name (2):\t\t%s\n", reply.site_name_2);
138 d_printf("NT Version: %d\n", reply.version);
139 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
140 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
147 this implements the CLDAP based netlogon lookup requests
148 for finding the domain controller of a ADS domain
150 static int net_ads_lookup(int argc, const char **argv)
154 const char *realm = assume_own_realm();
156 ads = ads_init(realm, opt_target_workgroup, opt_host);
158 ads->auth.flags |= ADS_AUTH_NO_BIND;
161 status = ads_connect(ads);
162 if (!ADS_ERR_OK(status) || !ads) {
163 d_fprintf(stderr, "Didn't find the cldap server!\n");
167 if (!ads->config.realm) {
168 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
169 ads->ldap_port = 389;
172 return net_ads_cldap_netlogon(ads);
177 static int net_ads_info(int argc, const char **argv)
180 const char *realm = assume_own_realm();
182 if ( (ads = ads_init(realm, opt_target_workgroup, opt_host)) != NULL ) {
183 ads->auth.flags |= ADS_AUTH_NO_BIND;
188 if (!ads || !ads->config.realm) {
189 d_fprintf(stderr, "Didn't find the ldap server!\n");
193 /* Try to set the server's current time since we didn't do a full
194 TCP LDAP session initially */
196 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
197 d_fprintf( stderr, "Failed to get server's current time!\n");
200 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
201 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
202 d_printf("Realm: %s\n", ads->config.realm);
203 d_printf("Bind Path: %s\n", ads->config.bind_path);
204 d_printf("LDAP port: %d\n", ads->ldap_port);
205 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
207 d_printf("KDC server: %s\n", ads->auth.kdc_server );
208 d_printf("Server time offset: %d\n", ads->auth.time_offset );
213 static void use_in_memory_ccache(void) {
214 /* Use in-memory credentials cache so we do not interfere with
215 * existing credentials */
216 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
219 static ADS_STRUCT *ads_startup(BOOL only_own_domain)
223 BOOL need_password = False;
224 BOOL second_time = False;
226 const char *realm = NULL;
228 /* lp_realm() should be handled by a command line param,
229 However, the join requires that realm be set in smb.conf
230 and compares our realm with the remote server's so this is
231 ok until someone needs more flexibility */
233 if (only_own_domain) {
237 ads = ads_init(realm, opt_target_workgroup, opt_host);
239 if (!opt_user_name) {
240 opt_user_name = "administrator";
243 if (opt_user_specified) {
244 need_password = True;
248 if (!opt_password && need_password && !opt_machine_pass) {
250 asprintf(&prompt,"%s's password: ", opt_user_name);
251 opt_password = getpass(prompt);
256 use_in_memory_ccache();
257 ads->auth.password = smb_xstrdup(opt_password);
260 ads->auth.user_name = smb_xstrdup(opt_user_name);
263 * If the username is of the form "name@realm",
264 * extract the realm and convert to upper case.
265 * This is only used to establish the connection.
267 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
269 ads->auth.realm = smb_xstrdup(cp);
270 strupper_m(ads->auth.realm);
273 status = ads_connect(ads);
275 if (!ADS_ERR_OK(status)) {
276 if (!need_password && !second_time) {
277 need_password = True;
281 d_printf("%s.\n", ads_errstr(status));
291 Check to see if connection can be made via ads.
292 ads_startup() stores the password in opt_password if it needs to so
293 that rpc or rap can use it without re-prompting.
295 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
300 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
304 ads->auth.flags |= ADS_AUTH_NO_BIND;
306 status = ads_connect(ads);
307 if ( !ADS_ERR_OK(status) ) {
315 int net_ads_check_our_domain(void)
317 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
320 int net_ads_check(void)
322 return net_ads_check_int(NULL, opt_workgroup, opt_host);
325 determine the netbios workgroup name for a domain
327 static int net_ads_workgroup(int argc, const char **argv)
331 const char *realm = assume_own_realm();
332 struct cldap_netlogon_reply reply;
334 ads = ads_init(realm, opt_target_workgroup, opt_host);
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");
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");
355 d_printf("Workgroup: %s\n", reply.netbios_domain);
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], '$')) {
372 d_printf("%-21.21s %s\n",
373 disp_fields[0], disp_fields[1]);
375 d_printf("%s\n", disp_fields[0]);
378 SAFE_FREE(disp_fields[0]);
379 SAFE_FREE(disp_fields[1]);
382 if (!values) /* must be new field, indicate string field */
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]);
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)
405 if (argc < 1) return net_ads_user_usage(argc, argv);
407 if (!(ads = ads_startup(False))) {
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));
418 if (ads_count_replies(ads, res)) {
419 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
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],
435 /* if no password is to be set, we're done */
437 d_printf("User %s added\n", argv[0]);
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);
447 if (ADS_ERR_OK(status)) {
448 d_printf("User %s added\n", argv[0]);
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);
466 ads_msgfree(ads, res);
471 static int ads_user_info(int argc, const char **argv)
476 const char *attrs[] = {"memberOf", NULL};
477 char *searchstring=NULL;
482 return net_ads_user_usage(argc, argv);
485 escaped_user = escape_ldap_string_alloc(argv[0]);
488 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
492 if (!(ads = ads_startup(False))) {
493 SAFE_FREE(escaped_user);
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));
504 SAFE_FREE(escaped_user);
508 grouplist = ldap_get_values((LDAP *)ads->ld,
509 (LDAPMessage *)res, "memberOf");
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);
524 SAFE_FREE(escaped_user);
528 static int ads_user_delete(int argc, const char **argv)
536 return net_ads_user_usage(argc, argv);
539 if (!(ads = ads_startup(False))) {
543 rc = ads_find_user_acct(ads, &res, argv[0]);
544 if (!ADS_ERR_OK(rc)) {
545 d_printf("User %s does not exist.\n", argv[0]);
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]);
558 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
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},
574 const char *shortattrs[] = {"sAMAccountName", NULL};
575 const char *longattrs[] = {"sAMAccountName", "description", NULL};
576 char *disp_fields[2] = {NULL, NULL};
579 if (!(ads = ads_startup(False))) {
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,
589 "(objectCategory=user)",
590 opt_long_list_entries ? longattrs :
591 shortattrs, usergrp_display,
594 return ADS_ERR_OK(rc) ? 0 : -1;
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)
613 return net_ads_group_usage(argc, argv);
616 if (!(ads = ads_startup(False))) {
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));
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);
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]);
643 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
649 ads_msgfree(ads, res);
654 static int ads_group_delete(int argc, const char **argv)
662 return net_ads_group_usage(argc, argv);
665 if (!(ads = ads_startup(False))) {
669 rc = ads_find_user_acct(ads, &res, argv[0]);
670 if (!ADS_ERR_OK(rc)) {
671 d_printf("Group %s does not exist.\n", argv[0]);
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]);
684 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
690 int net_ads_group(int argc, const char **argv)
692 struct functable func[] = {
693 {"ADD", ads_group_add},
694 {"DELETE", ads_group_delete},
699 const char *shortattrs[] = {"sAMAccountName", NULL};
700 const char *longattrs[] = {"sAMAccountName", "description", NULL};
701 char *disp_fields[2] = {NULL, NULL};
704 if (!(ads = ads_startup(False))) {
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,
713 "(objectCategory=group)",
714 opt_long_list_entries ? longattrs :
715 shortattrs, usergrp_display,
719 return ADS_ERR_OK(rc) ? 0 : -1;
721 return net_run_function(argc, argv, func, net_ads_group_usage);
724 static int net_ads_status(int argc, const char **argv)
730 if (!(ads = ads_startup(True))) {
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));
741 if (ads_count_replies(ads, res) == 0) {
742 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
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;
762 struct cli_state *cli = NULL;
764 DOM_SID *dom_sid = NULL;
766 if (!secrets_init()) {
767 DEBUG(1,("Failed to initialise secrets database\n"));
771 if (!(ctx = talloc_init("net_ads_leave"))) {
772 d_fprintf(stderr, "Could not initialise talloc context.\n");
776 /* The finds a DC and takes care of getting the
777 user creds if necessary */
779 if (!(ads = ads_startup(True))) {
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)) )
791 saf_store( cli->server_domain, cli->desthost );
793 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
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);
803 d_printf("Disabled account for '%s' in realm '%s'\n",
804 global_myname(), ads->config.realm);
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"));
827 net_use_machine_password();
829 if (!(ads = ads_startup(True))) {
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");
850 printf("Join is OK\n");
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");
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()));
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");
878 if (!secrets_init()) {
879 DEBUG(1,("Failed to initialise secrets database\n"));
886 /*******************************************************************
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 )
894 struct cli_state *cli = NULL;
896 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
899 saf_store( cli->server_domain, cli->desthost );
901 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
904 if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
905 password, ND_TYPE_AD )) )
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);
928 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
931 LDAPMessage *res = NULL;
932 char *dn_string = NULL;
933 const char *machine_name = global_myname();
936 if ( !machine_name ) {
937 return ADS_ERROR(LDAP_NO_MEMORY);
942 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
943 if (!ADS_ERR_OK(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"));
956 new_dn = talloc_strdup(ctx, dn_string);
957 ads_memfree(ads_s, dn_string);
959 return ADS_ERROR(LDAP_NO_MEMORY);
962 /* Windows only creates HOST/shortname & HOST/fqdn. */
964 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
967 servicePrincipalName[0] = psp;
969 name_to_fqdn(my_fqdn, machine_name);
971 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
973 servicePrincipalName[1] = psp;
975 if (!(mods = ads_init_mods(ctx))) {
979 /* fields of primary importance */
981 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
982 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
984 status = ads_gen_mod(ads_s, new_dn, mods);
987 ads_msgfree(ads_s, res);
992 /*******************************************************************
993 Set a machines dNSHostName and servicePrincipalName attributes
994 ********************************************************************/
996 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
998 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1001 LDAPMessage *res = NULL;
1002 char *dn_string = NULL;
1003 const char *machine_name = global_myname();
1006 if ( !machine_name ) {
1007 return ADS_ERROR(LDAP_NO_MEMORY);
1012 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
1013 if (!ADS_ERR_OK(status))
1016 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1017 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1018 return ADS_ERROR(LDAP_NO_MEMORY);
1021 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1022 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1026 new_dn = talloc_strdup(ctx, dn_string);
1027 ads_memfree(ads_s, dn_string);
1029 return ADS_ERROR(LDAP_NO_MEMORY);
1032 /* now do the mods */
1034 if (!(mods = ads_init_mods(ctx))) {
1038 /* fields of primary importance */
1040 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1042 status = ads_gen_mod(ads_s, new_dn, mods);
1045 ads_msgfree(ads_s, res);
1050 /*******************************************************************
1051 join a domain using ADS (LDAP mods)
1052 ********************************************************************/
1054 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1056 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1058 LDAPMessage *res = NULL;
1060 ou_str = ads_ou_string(ads, ou);
1061 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1064 rc = ads_search_dn(ads, &res, dn, NULL);
1065 ads_msgfree(ads, res);
1067 if (ADS_ERR_OK(rc)) {
1068 /* Attempt to create the machine account and bail if this fails.
1069 Assume that the admin wants exactly what they requested */
1071 rc = ads_create_machine_acct( ads, global_myname(), dn );
1072 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1082 /************************************************************************
1083 ************************************************************************/
1085 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1091 LDAPMessage *res = NULL;
1092 const char *machine_name = global_myname();
1094 status = ads_domain_func_level( ads, &domain_func );
1095 if ( !ADS_ERR_OK(status) ) {
1096 DEBUG(2,("Failed to determine domain functional level!\n"));
1100 /* go ahead and setup the default salt */
1102 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1103 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1107 fstrcpy( salt, std_salt );
1108 SAFE_FREE( std_salt );
1110 /* if it's a Windows functional domain, we have to look for the UPN */
1112 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1116 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1117 if (!ADS_ERR_OK(status)) {
1121 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1122 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1126 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1128 fstrcpy( salt, upn );
1131 ads_msgfree(ads, res);
1134 return kerberos_secrets_store_des_salt( salt );
1137 /*********************************************************
1138 utility function to parse an integer parameter from
1140 **********************************************************/
1141 static char* get_string_param( const char* param )
1145 if ( (p = strchr( param, '=' )) == NULL )
1151 /*******************************************************************
1152 ********************************************************************/
1154 static int net_ads_join_usage(int argc, const char **argv)
1156 d_printf("net ads join [options]\n");
1157 d_printf("Valid options:\n");
1158 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1159 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1160 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1161 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1162 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1167 /*******************************************************************
1168 ********************************************************************/
1170 int net_ads_join(int argc, const char **argv)
1172 ADS_STRUCT *ads = NULL;
1174 char *machine_account = NULL;
1175 const char *short_domain_name = NULL;
1176 char *tmp_password, *password;
1177 struct cldap_netlogon_reply cldap_reply;
1179 DOM_SID *domain_sid = NULL;
1180 BOOL createupn = False;
1181 const char *machineupn = NULL;
1182 const char *create_in_ou = NULL;
1185 if ( check_ads_config() != 0 ) {
1186 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1190 if ( (ads = ads_startup(True)) == NULL ) {
1194 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1195 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1196 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1201 if (!(ctx = talloc_init("net_ads_join"))) {
1202 d_fprintf(stderr, "Could not initialise talloc context.\n");
1206 /* process additional command line args */
1208 for ( i=0; i<argc; i++ ) {
1209 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1211 machineupn = get_string_param(argv[i]);
1213 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1214 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1215 d_fprintf(stderr, "Please supply a valid OU path\n");
1220 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1225 /* If we were given an OU, try to create the machine in
1226 the OU account first and then do the normal RPC join */
1228 if ( create_in_ou ) {
1229 status = net_precreate_machine_acct( ads, create_in_ou );
1230 if ( !ADS_ERR_OK(status) ) {
1231 d_fprintf( stderr, "Failed to pre-create the machine object "
1232 "in OU %s.\n", argv[0]);
1237 /* Do the domain join here */
1239 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1240 password = talloc_strdup(ctx, tmp_password);
1242 if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1243 /* There should be more detailed output here... */
1244 d_fprintf(stderr, "call of net_join_domain failed\n");
1248 /* Check the short name of the domain */
1250 ZERO_STRUCT( cldap_reply );
1252 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1253 ads->server.realm, &cldap_reply ) )
1255 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1256 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1257 d_printf("The workgroup in smb.conf does not match the short\n");
1258 d_printf("domain name obtained from the server.\n");
1259 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1260 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1263 short_domain_name = lp_workgroup();
1266 d_printf("Using short domain name -- %s\n", short_domain_name);
1268 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1269 value from smb.conf and the string returned from the server. The former is
1270 neede to bootstrap winbindd's first connection to the DC to get the real
1271 short domain name --jerry */
1273 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1274 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1279 /* Verify that everything is ok */
1281 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1282 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1286 /* create the dNSHostName & servicePrincipalName values */
1288 status = net_set_machine_spn( ctx, ads );
1289 if ( !ADS_ERR_OK(status) ) {
1291 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1292 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1293 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1295 /* Disable the machine account in AD. Better to fail than to leave
1296 a confused admin. */
1298 if ( net_ads_leave( 0, NULL ) != 0 ) {
1299 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1302 /* clear out the machine password */
1304 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1305 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1310 if ( !net_derive_salting_principal( ctx, ads ) ) {
1311 DEBUG(1,("Failed to determine salting principal\n"));
1318 /* default to using the short UPN name */
1319 if ( !machineupn ) {
1320 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1321 ads->config.realm );
1325 status = net_set_machine_upn( ctx, ads, machineupn );
1326 if ( !ADS_ERR_OK(status) ) {
1327 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1331 /* Now build the keytab, using the same ADS connection */
1332 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1333 DEBUG(1,("Error creating host keytab!\n"));
1336 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1338 SAFE_FREE(machine_account);
1345 /* issue an overall failure message at the end. */
1346 d_printf("Failed to join domain!\n");
1351 /*******************************************************************
1352 ********************************************************************/
1354 int net_ads_printer_usage(int argc, const char **argv)
1357 "\nnet ads printer search <printer>"
1358 "\n\tsearch for a printer in the directory\n"
1359 "\nnet ads printer info <printer> <server>"
1360 "\n\tlookup info in directory for printer on server"
1361 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1362 "\nnet ads printer publish <printername>"
1363 "\n\tpublish printer in directory"
1364 "\n\t(note: printer name is required)\n"
1365 "\nnet ads printer remove <printername>"
1366 "\n\tremove printer from directory"
1367 "\n\t(note: printer name is required)\n");
1371 /*******************************************************************
1372 ********************************************************************/
1374 static int net_ads_printer_search(int argc, const char **argv)
1380 if (!(ads = ads_startup(False))) {
1384 rc = ads_find_printers(ads, &res);
1386 if (!ADS_ERR_OK(rc)) {
1387 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1388 ads_msgfree(ads, res);
1393 if (ads_count_replies(ads, res) == 0) {
1394 d_fprintf(stderr, "No results found\n");
1395 ads_msgfree(ads, res);
1401 ads_msgfree(ads, res);
1406 static int net_ads_printer_info(int argc, const char **argv)
1410 const char *servername, *printername;
1413 if (!(ads = ads_startup(False))) {
1418 printername = argv[0];
1424 servername = argv[1];
1426 servername = global_myname();
1429 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1431 if (!ADS_ERR_OK(rc)) {
1432 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1433 ads_msgfree(ads, res);
1438 if (ads_count_replies(ads, res) == 0) {
1439 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1440 ads_msgfree(ads, res);
1446 ads_msgfree(ads, res);
1452 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1453 void *buf, size_t len)
1458 static int net_ads_printer_publish(int argc, const char **argv)
1462 const char *servername, *printername;
1463 struct cli_state *cli;
1464 struct rpc_pipe_client *pipe_hnd;
1465 struct in_addr server_ip;
1467 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1468 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1469 char *prt_dn, *srv_dn, **srv_cn;
1472 if (!(ads = ads_startup(True))) {
1477 return net_ads_printer_usage(argc, argv);
1480 printername = argv[0];
1483 servername = argv[1];
1485 servername = global_myname();
1488 /* Get printer data from SPOOLSS */
1490 resolve_name(servername, &server_ip, 0x20);
1492 nt_status = cli_full_connection(&cli, global_myname(), servername,
1495 opt_user_name, opt_workgroup,
1496 opt_password ? opt_password : "",
1497 CLI_FULL_CONNECTION_USE_KERBEROS,
1500 if (NT_STATUS_IS_ERR(nt_status)) {
1501 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1502 "for %s\n", servername, printername);
1507 /* Publish on AD server */
1509 ads_find_machine_acct(ads, &res, servername);
1511 if (ads_count_replies(ads, res) == 0) {
1512 d_fprintf(stderr, "Could not find machine account for server %s\n",
1518 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1519 srv_cn = ldap_explode_dn(srv_dn, 1);
1521 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1523 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1525 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1531 get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1534 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1535 if (!ADS_ERR_OK(rc)) {
1536 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1541 d_printf("published printer\n");
1547 static int net_ads_printer_remove(int argc, const char **argv)
1551 const char *servername;
1555 if (!(ads = ads_startup(True))) {
1560 return net_ads_printer_usage(argc, argv);
1564 servername = argv[1];
1566 servername = global_myname();
1569 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1571 if (!ADS_ERR_OK(rc)) {
1572 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1573 ads_msgfree(ads, res);
1578 if (ads_count_replies(ads, res) == 0) {
1579 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1580 ads_msgfree(ads, res);
1585 prt_dn = ads_get_dn(ads, res);
1586 ads_msgfree(ads, res);
1587 rc = ads_del_dn(ads, prt_dn);
1588 ads_memfree(ads, prt_dn);
1590 if (!ADS_ERR_OK(rc)) {
1591 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1600 static int net_ads_printer(int argc, const char **argv)
1602 struct functable func[] = {
1603 {"SEARCH", net_ads_printer_search},
1604 {"INFO", net_ads_printer_info},
1605 {"PUBLISH", net_ads_printer_publish},
1606 {"REMOVE", net_ads_printer_remove},
1610 return net_run_function(argc, argv, func, net_ads_printer_usage);
1614 static int net_ads_password(int argc, const char **argv)
1617 const char *auth_principal = opt_user_name;
1618 const char *auth_password = opt_password;
1620 char *new_password = NULL;
1625 if (opt_user_name == NULL || opt_password == NULL) {
1626 d_fprintf(stderr, "You must supply an administrator username/password\n");
1631 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1636 if (!strchr_m(user, '@')) {
1637 asprintf(&c, "%s@%s", argv[0], lp_realm());
1641 use_in_memory_ccache();
1642 c = strchr_m(auth_principal, '@');
1649 /* use the realm so we can eventually change passwords for users
1650 in realms other than default */
1651 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1655 /* we don't actually need a full connect, but it's the easy way to
1656 fill in the KDC's addresss */
1659 if (!ads || !ads->config.realm) {
1660 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1665 new_password = (char *)argv[1];
1667 asprintf(&prompt, "Enter new password for %s:", user);
1668 new_password = getpass(prompt);
1672 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1673 auth_password, user, new_password, ads->auth.time_offset);
1674 if (!ADS_ERR_OK(ret)) {
1675 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1680 d_printf("Password change for %s completed.\n", user);
1686 int net_ads_changetrustpw(int argc, const char **argv)
1689 char *host_principal;
1693 if (!secrets_init()) {
1694 DEBUG(1,("Failed to initialise secrets database\n"));
1698 net_use_machine_password();
1700 use_in_memory_ccache();
1702 if (!(ads = ads_startup(True))) {
1706 fstrcpy(my_name, global_myname());
1707 strlower_m(my_name);
1708 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1709 d_printf("Changing password for principal: %s\n", host_principal);
1711 ret = ads_change_trust_account_password(ads, host_principal);
1713 if (!ADS_ERR_OK(ret)) {
1714 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1716 SAFE_FREE(host_principal);
1720 d_printf("Password change for principal %s succeeded.\n", host_principal);
1722 if (lp_use_kerberos_keytab()) {
1723 d_printf("Attempting to update system keytab with new password.\n");
1724 if (ads_keytab_create_default(ads)) {
1725 d_printf("Failed to update system keytab.\n");
1730 SAFE_FREE(host_principal);
1736 help for net ads search
1738 static int net_ads_search_usage(int argc, const char **argv)
1741 "\nnet ads search <expression> <attributes...>\n"\
1742 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1743 "The expression is a standard LDAP search expression, and the\n"\
1744 "attributes are a list of LDAP fields to show in the results\n\n"\
1745 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1747 net_common_flags_usage(argc, argv);
1753 general ADS search function. Useful in diagnosing problems in ADS
1755 static int net_ads_search(int argc, const char **argv)
1759 const char *ldap_exp;
1764 return net_ads_search_usage(argc, argv);
1767 if (!(ads = ads_startup(False))) {
1774 rc = ads_do_search_all(ads, ads->config.bind_path,
1776 ldap_exp, attrs, &res);
1777 if (!ADS_ERR_OK(rc)) {
1778 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1783 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1785 /* dump the results */
1788 ads_msgfree(ads, res);
1796 help for net ads search
1798 static int net_ads_dn_usage(int argc, const char **argv)
1801 "\nnet ads dn <dn> <attributes...>\n"\
1802 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1803 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1804 "to show in the results\n\n"\
1805 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1807 net_common_flags_usage(argc, argv);
1813 general ADS search function. Useful in diagnosing problems in ADS
1815 static int net_ads_dn(int argc, const char **argv)
1824 return net_ads_dn_usage(argc, argv);
1827 if (!(ads = ads_startup(False))) {
1834 rc = ads_do_search_all(ads, dn,
1836 "(objectclass=*)", attrs, &res);
1837 if (!ADS_ERR_OK(rc)) {
1838 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1843 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1845 /* dump the results */
1848 ads_msgfree(ads, res);
1855 help for net ads sid search
1857 static int net_ads_sid_usage(int argc, const char **argv)
1860 "\nnet ads sid <sid> <attributes...>\n"\
1861 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1862 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1863 "to show in the results\n\n"\
1864 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1866 net_common_flags_usage(argc, argv);
1872 general ADS search function. Useful in diagnosing problems in ADS
1874 static int net_ads_sid(int argc, const char **argv)
1878 const char *sid_string;
1884 return net_ads_sid_usage(argc, argv);
1887 if (!(ads = ads_startup(False))) {
1891 sid_string = argv[0];
1894 if (!string_to_sid(&sid, sid_string)) {
1895 d_fprintf(stderr, "could not convert sid\n");
1900 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1901 if (!ADS_ERR_OK(rc)) {
1902 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1907 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1909 /* dump the results */
1912 ads_msgfree(ads, res);
1919 static int net_ads_keytab_usage(int argc, const char **argv)
1922 "net ads keytab <COMMAND>\n"\
1923 "<COMMAND> can be either:\n"\
1924 " CREATE Creates a fresh keytab\n"\
1925 " ADD Adds new service principal\n"\
1926 " FLUSH Flushes out all keytab entries\n"\
1927 " HELP Prints this help message\n"\
1928 "The ADD command will take arguments, the other commands\n"\
1929 "will not take any arguments. The arguments given to ADD\n"\
1930 "should be a list of principals to add. For example, \n"\
1931 " net ads keytab add srv1 srv2\n"\
1932 "will add principals for the services srv1 and srv2 to the\n"\
1933 "system's keytab.\n"\
1939 static int net_ads_keytab_flush(int argc, const char **argv)
1944 if (!(ads = ads_startup(True))) {
1947 ret = ads_keytab_flush(ads);
1952 static int net_ads_keytab_add(int argc, const char **argv)
1958 d_printf("Processing principals to add...\n");
1959 if (!(ads = ads_startup(True))) {
1962 for (i = 0; i < argc; i++) {
1963 ret |= ads_keytab_add_entry(ads, argv[i]);
1969 static int net_ads_keytab_create(int argc, const char **argv)
1974 if (!(ads = ads_startup(True))) {
1977 ret = ads_keytab_create_default(ads);
1982 int net_ads_keytab(int argc, const char **argv)
1984 struct functable func[] = {
1985 {"CREATE", net_ads_keytab_create},
1986 {"ADD", net_ads_keytab_add},
1987 {"FLUSH", net_ads_keytab_flush},
1988 {"HELP", net_ads_keytab_usage},
1992 if (!lp_use_kerberos_keytab()) {
1993 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1994 use keytab functions.\n");
1997 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2000 int net_ads_help(int argc, const char **argv)
2002 struct functable func[] = {
2003 {"USER", net_ads_user_usage},
2004 {"GROUP", net_ads_group_usage},
2005 {"PRINTER", net_ads_printer_usage},
2006 {"SEARCH", net_ads_search_usage},
2007 {"INFO", net_ads_info},
2008 {"JOIN", net_ads_join_usage},
2009 {"LEAVE", net_ads_leave},
2010 {"STATUS", net_ads_status},
2011 {"PASSWORD", net_ads_password},
2012 {"CHANGETRUSTPW", net_ads_changetrustpw},
2016 return net_run_function(argc, argv, func, net_ads_usage);
2019 int net_ads(int argc, const char **argv)
2021 struct functable func[] = {
2022 {"INFO", net_ads_info},
2023 {"JOIN", net_ads_join},
2024 {"TESTJOIN", net_ads_testjoin},
2025 {"LEAVE", net_ads_leave},
2026 {"STATUS", net_ads_status},
2027 {"USER", net_ads_user},
2028 {"GROUP", net_ads_group},
2029 {"PASSWORD", net_ads_password},
2030 {"CHANGETRUSTPW", net_ads_changetrustpw},
2031 {"PRINTER", net_ads_printer},
2032 {"SEARCH", net_ads_search},
2034 {"SID", net_ads_sid},
2035 {"WORKGROUP", net_ads_workgroup},
2036 {"LOOKUP", net_ads_lookup},
2037 {"KEYTAB", net_ads_keytab},
2038 {"HELP", net_ads_help},
2042 return net_run_function(argc, argv, func, net_ads_usage);
2047 static int net_ads_noads(void)
2049 d_fprintf(stderr, "ADS support not compiled in\n");
2053 int net_ads_keytab(int argc, const char **argv)
2055 return net_ads_noads();
2058 int net_ads_usage(int argc, const char **argv)
2060 return net_ads_noads();
2063 int net_ads_help(int argc, const char **argv)
2065 return net_ads_noads();
2068 int net_ads_changetrustpw(int argc, const char **argv)
2070 return net_ads_noads();
2073 int net_ads_join(int argc, const char **argv)
2075 return net_ads_noads();
2078 int net_ads_user(int argc, const char **argv)
2080 return net_ads_noads();
2083 int net_ads_group(int argc, const char **argv)
2085 return net_ads_noads();
2088 /* this one shouldn't display a message */
2089 int net_ads_check(void)
2094 int net_ads_check_our_domain(void)
2099 int net_ads(int argc, const char **argv)
2101 return net_ads_usage(argc, argv);
2104 #endif /* WITH_ADS */