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)
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "../utils/net.h"
28 int net_ads_usage(int argc, const char **argv)
31 "\nnet ads join <org_unit>"\
32 "\n\tjoins the local machine to a ADS realm\n"\
34 "\n\tremoves the local machine from a ADS realm\n"\
36 "\n\ttests that an exiting join is OK\n"\
38 "\n\tlist, add, or delete users in the realm\n"\
40 "\n\tlist, add, or delete groups in the realm\n"\
42 "\n\tshows some info on the server\n"\
44 "\n\tdump the machine account details to stdout\n"
45 "\nnet ads password <username@realm> -Uadmin_username@realm%%admin_pass"\
46 "\n\tchange a user's password using an admin account"\
47 "\n\t(note: use realm in UPPERCASE)\n"\
48 "\nnet ads chostpass"\
49 "\n\tchange the trust account password of this machine in the AD tree\n"\
50 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
51 "\n\t lookup, add, or remove directory entry for a printer\n"\
53 "\n\tperform a raw LDAP search and dump the results\n"
59 static int net_ads_info(int argc, const char **argv)
63 ads = ads_init(NULL, NULL, opt_host);
66 ads->auth.no_bind = 1;
71 if (!ads || !ads->config.realm) {
72 d_printf("Didn't find the ldap server!\n");
76 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
77 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
78 d_printf("Realm: %s\n", ads->config.realm);
79 d_printf("Bind Path: %s\n", ads->config.bind_path);
80 d_printf("LDAP port: %d\n", ads->ldap_port);
86 static ADS_STRUCT *ads_startup(void)
90 BOOL need_password = False;
91 BOOL second_time = False;
93 ads = ads_init(NULL, NULL, opt_host);
96 opt_user_name = "administrator";
99 if (opt_user_specified)
100 need_password = True;
103 if (!opt_password && need_password) {
105 asprintf(&prompt,"%s password: ", opt_user_name);
106 opt_password = getpass(prompt);
111 ads->auth.password = strdup(opt_password);
113 ads->auth.user_name = strdup(opt_user_name);
115 status = ads_connect(ads);
116 if (!ADS_ERR_OK(status)) {
117 if (!need_password && !second_time) {
118 need_password = True;
122 DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
131 Check to see if connection can be made via ads.
132 ads_startup() stores the password in opt_password if it needs to so
133 that rpc or rap can use it without re-prompting.
135 int net_ads_check(void)
147 determine the netbios workgroup name for a domain
149 static int net_ads_workgroup(int argc, const char **argv)
155 if (!(ads = ads_startup())) return -1;
157 if (!(ctx = talloc_init_named("net_ads_workgroup"))) {
161 if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
162 d_printf("Failed to find workgroup for realm '%s'\n",
168 d_printf("Workgroup: %s\n", workgroup);
177 static void usergrp_display(char *field, void **values, void *data_area)
179 char **disp_fields = (char **) data_area;
181 if (!field) { /* must be end of record */
182 if (!strchr_m(disp_fields[0], '$')) {
184 d_printf("%-21.21s %-50.50s\n",
185 disp_fields[0], disp_fields[1]);
187 d_printf("%s\n", disp_fields[0]);
189 SAFE_FREE(disp_fields[0]);
190 SAFE_FREE(disp_fields[1]);
193 if (!values) /* must be new field, indicate string field */
195 if (StrCaseCmp(field, "sAMAccountName") == 0) {
196 disp_fields[0] = strdup((char *) values[0]);
198 if (StrCaseCmp(field, "description") == 0)
199 disp_fields[1] = strdup((char *) values[0]);
202 static int net_ads_user_usage(int argc, const char **argv)
204 return net_help_user(argc, argv);
207 static int ads_user_add(int argc, const char **argv)
215 if (argc < 1) return net_ads_user_usage(argc, argv);
217 if (!(ads = ads_startup())) return -1;
219 status = ads_find_user_acct(ads, &res, argv[0]);
221 if (!ADS_ERR_OK(status)) {
222 d_printf("ads_user_add: %s\n", ads_errstr(status));
226 if (ads_count_replies(ads, res)) {
227 d_printf("ads_user_add: User %s already exists\n", argv[0]);
231 status = ads_add_user_acct(ads, argv[0], opt_comment);
233 if (!ADS_ERR_OK(status)) {
234 d_printf("Could not add user %s: %s\n", argv[0],
239 /* if no password is to be set, we're done */
241 d_printf("User %s added\n", argv[0]);
246 /* try setting the password */
247 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
248 status = krb5_set_password(ads->auth.kdc_server, upn, argv[1]);
250 if (ADS_ERR_OK(status)) {
251 d_printf("User %s added\n", argv[0]);
256 /* password didn't set, delete account */
257 d_printf("Could not add user %s. Error setting password %s\n",
258 argv[0], ads_errstr(status));
259 ads_msgfree(ads, res);
260 status=ads_find_user_acct(ads, &res, argv[0]);
261 if (ADS_ERR_OK(status)) {
262 userdn = ads_get_dn(ads, res);
263 ads_del_dn(ads, userdn);
264 ads_memfree(ads, userdn);
269 ads_msgfree(ads, res);
274 static int ads_user_info(int argc, const char **argv)
279 const char *attrs[] = {"memberOf", NULL};
280 char *searchstring=NULL;
283 if (argc < 1) return net_ads_user_usage(argc, argv);
285 if (!(ads = ads_startup())) return -1;
287 asprintf(&searchstring, "(sAMAccountName=%s)", argv[0]);
288 rc = ads_search(ads, &res, searchstring, attrs);
289 safe_free(searchstring);
291 if (!ADS_ERR_OK(rc)) {
292 d_printf("ads_search: %s\n", ads_errstr(rc));
296 grouplist = ldap_get_values(ads->ld, res, "memberOf");
301 for (i=0;grouplist[i];i++) {
302 groupname = ldap_explode_dn(grouplist[i], 1);
303 d_printf("%s\n", groupname[0]);
304 ldap_value_free(groupname);
306 ldap_value_free(grouplist);
309 ads_msgfree(ads, res);
315 static int ads_user_delete(int argc, const char **argv)
322 if (argc < 1) return net_ads_user_usage(argc, argv);
324 if (!(ads = ads_startup())) return -1;
326 rc = ads_find_user_acct(ads, &res, argv[0]);
327 if (!ADS_ERR_OK(rc)) {
328 DEBUG(0, ("User %s does not exist\n", argv[0]));
331 userdn = ads_get_dn(ads, res);
332 ads_msgfree(ads, res);
333 rc = ads_del_dn(ads, userdn);
334 ads_memfree(ads, userdn);
335 if (!ADS_ERR_OK(rc)) {
336 d_printf("User %s deleted\n", argv[0]);
339 d_printf("Error deleting user %s: %s\n", argv[0],
344 int net_ads_user(int argc, const char **argv)
346 struct functable func[] = {
347 {"ADD", ads_user_add},
348 {"INFO", ads_user_info},
349 {"DELETE", ads_user_delete},
354 const char *shortattrs[] = {"sAMAccountName", NULL};
355 const char *longattrs[] = {"sAMAccountName", "description", NULL};
356 char *disp_fields[2] = {NULL, NULL};
359 if (!(ads = ads_startup())) return -1;
361 if (opt_long_list_entries)
362 d_printf("\nUser name Comment"\
363 "\n-----------------------------\n");
365 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
367 "(objectclass=user)",
368 opt_long_list_entries ? longattrs :
369 shortattrs, usergrp_display,
375 return net_run_function(argc, argv, func, net_ads_user_usage);
378 static int net_ads_group_usage(int argc, const char **argv)
380 return net_help_group(argc, argv);
383 static int ads_group_add(int argc, const char **argv)
390 if (argc < 1) return net_ads_group_usage(argc, argv);
392 if (!(ads = ads_startup())) return -1;
394 status = ads_find_user_acct(ads, &res, argv[0]);
396 if (!ADS_ERR_OK(status)) {
397 d_printf("ads_group_add: %s\n", ads_errstr(status));
401 if (ads_count_replies(ads, res)) {
402 d_printf("ads_group_add: Group %s already exists\n", argv[0]);
403 ads_msgfree(ads, res);
407 status = ads_add_group_acct(ads, argv[0], opt_comment);
409 if (ADS_ERR_OK(status)) {
410 d_printf("Group %s added\n", argv[0]);
413 d_printf("Could not add group %s: %s\n", argv[0],
419 ads_msgfree(ads, res);
424 static int ads_group_delete(int argc, const char **argv)
431 if (argc < 1) return net_ads_group_usage(argc, argv);
433 if (!(ads = ads_startup())) return -1;
435 rc = ads_find_user_acct(ads, &res, argv[0]);
436 if (!ADS_ERR_OK(rc)) {
437 DEBUG(0, ("Group %s does not exist\n", argv[0]));
440 groupdn = ads_get_dn(ads, res);
441 ads_msgfree(ads, res);
442 rc = ads_del_dn(ads, groupdn);
443 ads_memfree(ads, groupdn);
444 if (!ADS_ERR_OK(rc)) {
445 d_printf("Group %s deleted\n", argv[0]);
448 d_printf("Error deleting group %s: %s\n", argv[0],
453 int net_ads_group(int argc, const char **argv)
455 struct functable func[] = {
456 {"ADD", ads_group_add},
457 {"DELETE", ads_group_delete},
462 const char *shortattrs[] = {"sAMAccountName", NULL};
463 const char *longattrs[] = {"sAMAccountName", "description", NULL};
464 char *disp_fields[2] = {NULL, NULL};
467 if (!(ads = ads_startup())) return -1;
469 if (opt_long_list_entries)
470 d_printf("\nGroup name Comment"\
471 "\n-----------------------------\n");
472 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
474 "(objectclass=group)",
475 opt_long_list_entries ? longattrs :
476 shortattrs, usergrp_display,
482 return net_run_function(argc, argv, func, net_ads_group_usage);
485 static int net_ads_status(int argc, const char **argv)
489 extern pstring global_myname;
492 if (!(ads = ads_startup())) return -1;
494 rc = ads_find_machine_acct(ads, &res, global_myname);
495 if (!ADS_ERR_OK(rc)) {
496 d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
500 if (ads_count_replies(ads, res) == 0) {
501 d_printf("No machine account for '%s' found\n", global_myname);
510 static int net_ads_leave(int argc, const char **argv)
512 ADS_STRUCT *ads = NULL;
514 extern pstring global_myname;
516 if (!secrets_init()) {
517 DEBUG(1,("Failed to initialise secrets database\n"));
522 asprintf(&opt_user_name, "%s$", global_myname);
523 opt_password = secrets_fetch_machine_password();
526 if (!(ads = ads_startup())) {
530 rc = ads_leave_realm(ads, global_myname);
531 if (!ADS_ERR_OK(rc)) {
532 d_printf("Failed to delete host '%s' from the '%s' realm.\n",
533 global_myname, ads->config.realm);
537 d_printf("Removed '%s' from realm '%s'\n", global_myname, ads->config.realm);
542 static int net_ads_join_ok(void)
544 ADS_STRUCT *ads = NULL;
545 extern pstring global_myname;
547 if (!secrets_init()) {
548 DEBUG(1,("Failed to initialise secrets database\n"));
552 asprintf(&opt_user_name, "%s$", global_myname);
553 opt_password = secrets_fetch_machine_password();
555 if (!(ads = ads_startup())) {
564 check that an existing join is OK
566 int net_ads_testjoin(int argc, const char **argv)
568 /* Display success or failure */
569 if (net_ads_join_ok() != 0) {
570 fprintf(stderr,"Join to domain is not valid\n");
574 printf("Join is OK\n");
579 join a domain using ADS
581 int net_ads_join(int argc, const char **argv)
587 extern pstring global_myname;
588 const char *org_unit = "Computers";
594 if (argc > 0) org_unit = argv[0];
596 if (!secrets_init()) {
597 DEBUG(1,("Failed to initialise secrets database\n"));
601 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
602 password = strdup(tmp_password);
604 if (!(ads = ads_startup())) return -1;
606 ou_str = ads_ou_string(org_unit);
607 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
610 rc = ads_search_dn(ads, &res, dn, NULL);
611 ads_msgfree(ads, res);
613 if (rc.error_type == ADS_ERROR_LDAP && rc.rc == LDAP_NO_SUCH_OBJECT) {
614 d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n",
620 if (!ADS_ERR_OK(rc)) {
621 d_printf("ads_join_realm: %s\n", ads_errstr(rc));
625 rc = ads_join_realm(ads, global_myname, org_unit);
626 if (!ADS_ERR_OK(rc)) {
627 d_printf("ads_join_realm: %s\n", ads_errstr(rc));
631 rc = ads_set_machine_password(ads, global_myname, password);
632 if (!ADS_ERR_OK(rc)) {
633 d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
637 rc = ads_domain_sid(ads, &dom_sid);
638 if (!ADS_ERR_OK(rc)) {
639 d_printf("ads_domain_sid: %s\n", ads_errstr(rc));
643 if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
644 DEBUG(1,("Failed to save domain sid\n"));
648 if (!secrets_store_machine_password(password)) {
649 DEBUG(1,("Failed to save machine password\n"));
653 d_printf("Joined '%s' to realm '%s'\n", global_myname, ads->config.realm);
660 int net_ads_printer_usage(int argc, const char **argv)
663 "\nnet ads printer info <printer> <server>"
664 "\n\tlookup info in directory for printer on server"
665 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
666 "\nnet ads printer publish <printername>"
667 "\n\tpublish printer in directory"
668 "\n\t(note: printer name is required)\n"
669 "\nnet ads printer remove <printername>"
670 "\n\tremove printer from directory"
671 "\n\t(note: printer name is required)\n");
675 static int net_ads_printer_info(int argc, const char **argv)
679 const char *servername, *printername;
680 extern pstring global_myname;
683 if (!(ads = ads_startup())) return -1;
686 printername = argv[0];
691 servername = argv[1];
693 servername = global_myname;
695 rc = ads_find_printer_on_server(ads, &res, printername, servername);
697 if (!ADS_ERR_OK(rc)) {
698 d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
699 ads_msgfree(ads, res);
703 if (ads_count_replies(ads, res) == 0) {
704 d_printf("Printer '%s' not found\n", printername);
705 ads_msgfree(ads, res);
710 ads_msgfree(ads, res);
715 void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
720 static int net_ads_printer_publish(int argc, const char **argv)
724 char *uncname, *servername;
725 ADS_PRINTER_ENTRY prt;
726 extern pstring global_myname;
727 char *ports[2] = {"Samba", NULL};
730 these const strings are only here as an example. The attributes
731 they represent are not implemented yet
733 const char *bins[] = {"Tray 21", NULL};
734 const char *media[] = {"Letter", NULL};
735 const char *orients[] = {"PORTRAIT", NULL};
737 if (!(ads = ads_startup())) return -1;
740 return net_ads_printer_usage(argc, argv);
742 memset(&prt, 0, sizeof(ADS_PRINTER_ENTRY));
744 /* we don't sue the servername or unc name provided by
745 get_a_printer, because the server name might be
746 localhost or an ip address */
747 prt.printerName = argv[0];
748 asprintf(&servername, "%s.%s", global_myname, ads->config.realm);
749 prt.serverName = servername;
750 prt.shortServerName = global_myname;
751 prt.versionNumber = "4";
752 asprintf(&uncname, "\\\\%s\\%s", global_myname, argv[0]);
754 prt.printBinNames = (char **) bins;
755 prt.printMediaSupported = (char **) media;
756 prt.printOrientationsSupported = (char **) orients;
757 prt.portName = (char **) ports;
758 prt.printSpooling = "PrintAfterSpooled";
760 rc = ads_add_printer(ads, &prt);
761 if (!ADS_ERR_OK(rc)) {
762 d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
766 d_printf("published printer\n");
771 static int net_ads_printer_remove(int argc, const char **argv)
775 char *servername, *prt_dn;
776 extern pstring global_myname;
779 if (!(ads = ads_startup())) return -1;
782 return net_ads_printer_usage(argc, argv);
785 servername = argv[1];
787 servername = global_myname;
789 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
791 if (!ADS_ERR_OK(rc)) {
792 d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
793 ads_msgfree(ads, res);
797 if (ads_count_replies(ads, res) == 0) {
798 d_printf("Printer '%s' not found\n", argv[1]);
799 ads_msgfree(ads, res);
803 prt_dn = ads_get_dn(ads, res);
804 ads_msgfree(ads, res);
805 rc = ads_del_dn(ads, prt_dn);
806 ads_memfree(ads, prt_dn);
808 if (!ADS_ERR_OK(rc)) {
809 d_printf("ads_del_dn: %s\n", ads_errstr(rc));
816 static int net_ads_printer(int argc, const char **argv)
818 struct functable func[] = {
819 {"INFO", net_ads_printer_info},
820 {"PUBLISH", net_ads_printer_publish},
821 {"REMOVE", net_ads_printer_remove},
825 return net_run_function(argc, argv, func, net_ads_printer_usage);
829 static int net_ads_password(int argc, const char **argv)
832 char *auth_principal = opt_user_name;
833 char *auth_password = opt_password;
835 char *new_password = NULL;
841 if ((argc != 1) || (opt_user_name == NULL) ||
842 (opt_password == NULL) || (strchr(opt_user_name, '@') == NULL) ||
843 (strchr(argv[0], '@') == NULL)) {
844 return net_ads_usage(argc, argv);
847 c = strchr(auth_principal, '@');
850 /* use the realm so we can eventually change passwords for users
851 in realms other than default */
852 if (!(ads = ads_init(realm, NULL, NULL))) return -1;
854 asprintf(&prompt, "Enter new password for %s:", argv[0]);
856 new_password = getpass(prompt);
858 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
859 auth_password, argv[0], new_password);
860 if (!ADS_ERR_OK(ret)) {
861 d_printf("Password change failed :-( ...\n");
867 d_printf("Password change for %s completed.\n", argv[0]);
875 static int net_ads_change_localhost_pass(int argc, const char **argv)
878 extern pstring global_myname;
879 char *host_principal;
883 if (!secrets_init()) {
884 DEBUG(1,("Failed to initialise secrets database\n"));
888 asprintf(&opt_user_name, "%s$", global_myname);
889 opt_password = secrets_fetch_machine_password();
891 if (!(ads = ads_startup())) {
895 hostname = strdup(global_myname);
897 asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
899 d_printf("Changing password for principal: HOST/%s\n", host_principal);
901 ret = ads_change_trust_account_password(ads, host_principal);
903 if (!ADS_ERR_OK(ret)) {
904 d_printf("Password change failed :-( ...\n");
906 SAFE_FREE(host_principal);
910 d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
912 SAFE_FREE(host_principal);
918 help for net ads search
920 static int net_ads_search_usage(int argc, const char **argv)
923 "\nnet ads search <expression> <attributes...>\n"\
924 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
925 "The expression is a standard LDAP search expression, and the\n"\
926 "attributes are a list of LDAP fields to show in the results\n\n"\
927 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
929 net_common_flags_usage(argc, argv);
935 general ADS search function. Useful in diagnosing problems in ADS
937 static int net_ads_search(int argc, const char **argv)
946 return net_ads_search_usage(argc, argv);
949 if (!(ads = ads_startup())) {
956 rc = ads_do_search_all(ads, ads->config.bind_path,
959 if (!ADS_ERR_OK(rc)) {
960 d_printf("search failed: %s\n", ads_errstr(rc));
964 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
966 /* dump the results */
969 ads_msgfree(ads, res);
976 int net_ads_help(int argc, const char **argv)
978 struct functable func[] = {
979 {"USER", net_ads_user_usage},
980 {"GROUP", net_ads_group_usage},
981 {"PRINTER", net_ads_printer_usage},
982 {"SEARCH", net_ads_search_usage},
984 {"INFO", net_ads_info},
985 {"JOIN", net_ads_join},
986 {"LEAVE", net_ads_leave},
987 {"STATUS", net_ads_status},
988 {"PASSWORD", net_ads_password},
989 {"CHOSTPASS", net_ads_change_localhost_pass},
994 return net_run_function(argc, argv, func, net_ads_usage);
997 int net_ads(int argc, const char **argv)
999 struct functable func[] = {
1000 {"INFO", net_ads_info},
1001 {"JOIN", net_ads_join},
1002 {"TESTJOIN", net_ads_testjoin},
1003 {"LEAVE", net_ads_leave},
1004 {"STATUS", net_ads_status},
1005 {"USER", net_ads_user},
1006 {"GROUP", net_ads_group},
1007 {"PASSWORD", net_ads_password},
1008 {"CHOSTPASS", net_ads_change_localhost_pass},
1009 {"PRINTER", net_ads_printer},
1010 {"SEARCH", net_ads_search},
1011 {"WORKGROUP", net_ads_workgroup},
1012 {"HELP", net_ads_help},
1016 return net_run_function(argc, argv, func, net_ads_usage);
1021 static int net_ads_noads(void)
1023 d_printf("ADS support not compiled in\n");
1027 int net_ads_usage(int argc, const char **argv)
1029 return net_ads_noads();
1032 int net_ads_help(int argc, const char **argv)
1034 return net_ads_noads();
1037 int net_ads_join(int argc, const char **argv)
1039 return net_ads_noads();
1042 int net_ads_user(int argc, const char **argv)
1044 return net_ads_noads();
1047 int net_ads_group(int argc, const char **argv)
1049 return net_ads_noads();
1052 /* this one shouldn't display a message */
1053 int net_ads_check(void)
1058 int net_ads(int argc, const char **argv)
1060 return net_ads_usage(argc, argv);