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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "utils/net.h"
28 int net_ads_usage(int argc, const char **argv)
30 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
31 d_printf(" Join the local machine to a ADS realm\n");
33 d_printf(" Remove the local machine from a ADS realm\n");
34 d_printf("testjoin\n");
35 d_printf(" Validates the machine account in the domain\n");
37 d_printf(" List, add, or delete users in the realm\n");
39 d_printf(" List, add, or delete groups in the realm\n");
41 d_printf(" Displays details regarding a specific AD server\n");
43 d_printf(" Display details regarding the machine's account in AD\n");
45 d_printf(" Performs CLDAP query of AD domain controllers\n");
46 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
47 d_printf(" Change a user's password using an admin account\n");
48 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
49 d_printf("changetrustpw\n");
50 d_printf(" Change the trust account password of this machine in the AD tree\n");
51 d_printf("printer [info | publish | remove] <printername> <servername>\n");
52 d_printf(" Lookup, add, or remove directory entry for a printer\n");
53 d_printf("{search,dn,sid}\n");
54 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
56 d_printf(" Manage a local keytab file based on the machine account in AD\n");
58 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
59 d_printf(" (using the machine credentials)\n");
64 /* when we do not have sufficient input parameters to contact a remote domain
65 * we always fall back to our own realm - Guenther*/
67 static const char *assume_own_realm(void)
69 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
77 do a cldap netlogon query
79 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
81 char addr[INET6_ADDRSTRLEN];
82 struct cldap_netlogon_reply reply;
85 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
86 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
87 d_fprintf(stderr, "CLDAP query failed!\n");
91 d_printf("Information for Domain Controller: %s\n\n",
94 d_printf("Response Type: ");
96 case SAMLOGON_AD_UNK_R:
97 d_printf("SAMLOGON\n");
100 d_printf("SAMLOGON_USER\n");
103 d_printf("0x%x\n", reply.type);
107 smb_uuid_unpack(reply.guid, &tmp_guid);
108 d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid));
112 "\tIs a GC of the forest: %s\n"
113 "\tIs an LDAP server: %s\n"
114 "\tSupports DS: %s\n"
115 "\tIs running a KDC: %s\n"
116 "\tIs running time services: %s\n"
117 "\tIs the closest DC: %s\n"
118 "\tIs writable: %s\n"
119 "\tHas a hardware clock: %s\n"
120 "\tIs a non-domain NC serviced by LDAP server: %s\n",
121 (reply.flags & ADS_PDC) ? "yes" : "no",
122 (reply.flags & ADS_GC) ? "yes" : "no",
123 (reply.flags & ADS_LDAP) ? "yes" : "no",
124 (reply.flags & ADS_DS) ? "yes" : "no",
125 (reply.flags & ADS_KDC) ? "yes" : "no",
126 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
127 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
128 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
129 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
130 (reply.flags & ADS_NDNC) ? "yes" : "no");
132 printf("Forest:\t\t\t%s\n", reply.forest);
133 printf("Domain:\t\t\t%s\n", reply.domain);
134 printf("Domain Controller:\t%s\n", reply.hostname);
136 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
137 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
139 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
140 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
142 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
143 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
145 d_printf("NT Version: %d\n", reply.version);
146 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
147 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
153 this implements the CLDAP based netlogon lookup requests
154 for finding the domain controller of a ADS domain
156 static int net_ads_lookup(int argc, const char **argv)
160 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
161 d_fprintf(stderr, "Didn't find the cldap server!\n");
165 if (!ads->config.realm) {
166 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
167 ads->ldap.port = 389;
170 return net_ads_cldap_netlogon(ads);
175 static int net_ads_info(int argc, const char **argv)
178 char addr[INET6_ADDRSTRLEN];
180 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
181 d_fprintf(stderr, "Didn't find the ldap server!\n");
185 if (!ads || !ads->config.realm) {
186 d_fprintf(stderr, "Didn't find the ldap server!\n");
190 /* Try to set the server's current time since we didn't do a full
191 TCP LDAP session initially */
193 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
194 d_fprintf( stderr, "Failed to get server's current time!\n");
197 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
199 d_printf("LDAP server: %s\n", addr);
200 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
201 d_printf("Realm: %s\n", ads->config.realm);
202 d_printf("Bind Path: %s\n", ads->config.bind_path);
203 d_printf("LDAP port: %d\n", ads->ldap.port);
204 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
206 d_printf("KDC server: %s\n", ads->auth.kdc_server );
207 d_printf("Server time offset: %d\n", ads->auth.time_offset );
212 static void use_in_memory_ccache(void) {
213 /* Use in-memory credentials cache so we do not interfere with
214 * existing credentials */
215 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
218 static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
220 ADS_STRUCT *ads = NULL;
222 bool need_password = False;
223 bool second_time = False;
225 const char *realm = NULL;
226 bool tried_closest_dc = False;
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 */
236 if (only_own_domain) {
239 realm = assume_own_realm();
242 ads = ads_init(realm, opt_target_workgroup, opt_host);
244 if (!opt_user_name) {
245 opt_user_name = "administrator";
248 if (opt_user_specified) {
249 need_password = True;
253 if (!opt_password && need_password && !opt_machine_pass) {
254 opt_password = net_prompt_pass(opt_user_name);
257 return ADS_ERROR(LDAP_NO_MEMORY);
262 use_in_memory_ccache();
263 SAFE_FREE(ads->auth.password);
264 ads->auth.password = smb_xstrdup(opt_password);
267 ads->auth.flags |= auth_flags;
268 SAFE_FREE(ads->auth.user_name);
269 ads->auth.user_name = smb_xstrdup(opt_user_name);
272 * If the username is of the form "name@realm",
273 * extract the realm and convert to upper case.
274 * This is only used to establish the connection.
276 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
278 SAFE_FREE(ads->auth.realm);
279 ads->auth.realm = smb_xstrdup(cp);
280 strupper_m(ads->auth.realm);
283 status = ads_connect(ads);
285 if (!ADS_ERR_OK(status)) {
287 if (NT_STATUS_EQUAL(ads_ntstatus(status),
288 NT_STATUS_NO_LOGON_SERVERS)) {
289 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
294 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
295 need_password = True;
304 /* when contacting our own domain, make sure we use the closest DC.
305 * This is done by reconnecting to ADS because only the first call to
306 * ads_connect will give us our own sitename */
308 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
310 tried_closest_dc = True; /* avoid loop */
312 if (!ads->config.tried_closest_dc) {
314 namecache_delete(ads->server.realm, 0x1C);
315 namecache_delete(ads->server.workgroup, 0x1C);
328 ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
330 return ads_startup_int(only_own_domain, 0, ads);
333 ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
335 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
339 Check to see if connection can be made via ads.
340 ads_startup() stores the password in opt_password if it needs to so
341 that rpc or rap can use it without re-prompting.
343 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
348 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
352 ads->auth.flags |= ADS_AUTH_NO_BIND;
354 status = ads_connect(ads);
355 if ( !ADS_ERR_OK(status) ) {
363 int net_ads_check_our_domain(void)
365 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
368 int net_ads_check(void)
370 return net_ads_check_int(NULL, opt_workgroup, opt_host);
374 determine the netbios workgroup name for a domain
376 static int net_ads_workgroup(int argc, const char **argv)
379 char addr[INET6_ADDRSTRLEN];
380 struct cldap_netlogon_reply reply;
382 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
383 d_fprintf(stderr, "Didn't find the cldap server!\n");
387 if (!ads->config.realm) {
388 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
389 ads->ldap.port = 389;
392 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
393 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
394 d_fprintf(stderr, "CLDAP query failed!\n");
398 d_printf("Workgroup: %s\n", reply.netbios_domain);
407 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
409 char **disp_fields = (char **) data_area;
411 if (!field) { /* must be end of record */
412 if (disp_fields[0]) {
413 if (!strchr_m(disp_fields[0], '$')) {
415 d_printf("%-21.21s %s\n",
416 disp_fields[0], disp_fields[1]);
418 d_printf("%s\n", disp_fields[0]);
421 SAFE_FREE(disp_fields[0]);
422 SAFE_FREE(disp_fields[1]);
425 if (!values) /* must be new field, indicate string field */
427 if (StrCaseCmp(field, "sAMAccountName") == 0) {
428 disp_fields[0] = SMB_STRDUP((char *) values[0]);
430 if (StrCaseCmp(field, "description") == 0)
431 disp_fields[1] = SMB_STRDUP((char *) values[0]);
435 static int net_ads_user_usage(int argc, const char **argv)
437 return net_help_user(argc, argv);
440 static int ads_user_add(int argc, const char **argv)
445 LDAPMessage *res=NULL;
449 if (argc < 1) return net_ads_user_usage(argc, argv);
451 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
455 status = ads_find_user_acct(ads, &res, argv[0]);
457 if (!ADS_ERR_OK(status)) {
458 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
462 if (ads_count_replies(ads, res)) {
463 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
468 ou_str = SMB_STRDUP(opt_container);
470 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
473 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
475 if (!ADS_ERR_OK(status)) {
476 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
481 /* if no password is to be set, we're done */
483 d_printf("User %s added\n", argv[0]);
488 /* try setting the password */
489 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
490 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
491 ads->auth.time_offset);
493 if (ADS_ERR_OK(status)) {
494 d_printf("User %s added\n", argv[0]);
499 /* password didn't set, delete account */
500 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
501 argv[0], ads_errstr(status));
502 ads_msgfree(ads, res);
503 status=ads_find_user_acct(ads, &res, argv[0]);
504 if (ADS_ERR_OK(status)) {
505 userdn = ads_get_dn(ads, res);
506 ads_del_dn(ads, userdn);
507 ads_memfree(ads, userdn);
512 ads_msgfree(ads, res);
518 static int ads_user_info(int argc, const char **argv)
523 const char *attrs[] = {"memberOf", NULL};
524 char *searchstring=NULL;
529 return net_ads_user_usage(argc, argv);
532 escaped_user = escape_ldap_string_alloc(argv[0]);
535 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
539 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
540 SAFE_FREE(escaped_user);
544 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
545 rc = ads_search(ads, &res, searchstring, attrs);
546 safe_free(searchstring);
548 if (!ADS_ERR_OK(rc)) {
549 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
551 SAFE_FREE(escaped_user);
555 grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
556 (LDAPMessage *)res, "memberOf");
561 for (i=0;grouplist[i];i++) {
562 groupname = ldap_explode_dn(grouplist[i], 1);
563 d_printf("%s\n", groupname[0]);
564 ldap_value_free(groupname);
566 ldap_value_free(grouplist);
569 ads_msgfree(ads, res);
571 SAFE_FREE(escaped_user);
575 static int ads_user_delete(int argc, const char **argv)
579 LDAPMessage *res = NULL;
583 return net_ads_user_usage(argc, argv);
586 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
590 rc = ads_find_user_acct(ads, &res, argv[0]);
591 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
592 d_printf("User %s does not exist.\n", argv[0]);
593 ads_msgfree(ads, res);
597 userdn = ads_get_dn(ads, res);
598 ads_msgfree(ads, res);
599 rc = ads_del_dn(ads, userdn);
600 ads_memfree(ads, userdn);
601 if (ADS_ERR_OK(rc)) {
602 d_printf("User %s deleted\n", argv[0]);
606 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
612 int net_ads_user(int argc, const char **argv)
614 struct functable func[] = {
615 {"ADD", ads_user_add},
616 {"INFO", ads_user_info},
617 {"DELETE", ads_user_delete},
622 const char *shortattrs[] = {"sAMAccountName", NULL};
623 const char *longattrs[] = {"sAMAccountName", "description", NULL};
624 char *disp_fields[2] = {NULL, NULL};
627 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
631 if (opt_long_list_entries)
632 d_printf("\nUser name Comment"\
633 "\n-----------------------------\n");
635 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
637 "(objectCategory=user)",
638 opt_long_list_entries ? longattrs :
639 shortattrs, usergrp_display,
642 return ADS_ERR_OK(rc) ? 0 : -1;
645 return net_run_function(argc, argv, func, net_ads_user_usage);
648 static int net_ads_group_usage(int argc, const char **argv)
650 return net_help_group(argc, argv);
653 static int ads_group_add(int argc, const char **argv)
657 LDAPMessage *res=NULL;
662 return net_ads_group_usage(argc, argv);
665 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
669 status = ads_find_user_acct(ads, &res, argv[0]);
671 if (!ADS_ERR_OK(status)) {
672 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
676 if (ads_count_replies(ads, res)) {
677 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
682 ou_str = SMB_STRDUP(opt_container);
684 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
687 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
689 if (ADS_ERR_OK(status)) {
690 d_printf("Group %s added\n", argv[0]);
693 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
699 ads_msgfree(ads, res);
705 static int ads_group_delete(int argc, const char **argv)
709 LDAPMessage *res = NULL;
713 return net_ads_group_usage(argc, argv);
716 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
720 rc = ads_find_user_acct(ads, &res, argv[0]);
721 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
722 d_printf("Group %s does not exist.\n", argv[0]);
723 ads_msgfree(ads, res);
727 groupdn = ads_get_dn(ads, res);
728 ads_msgfree(ads, res);
729 rc = ads_del_dn(ads, groupdn);
730 ads_memfree(ads, groupdn);
731 if (ADS_ERR_OK(rc)) {
732 d_printf("Group %s deleted\n", argv[0]);
736 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
742 int net_ads_group(int argc, const char **argv)
744 struct functable func[] = {
745 {"ADD", ads_group_add},
746 {"DELETE", ads_group_delete},
751 const char *shortattrs[] = {"sAMAccountName", NULL};
752 const char *longattrs[] = {"sAMAccountName", "description", NULL};
753 char *disp_fields[2] = {NULL, NULL};
756 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
760 if (opt_long_list_entries)
761 d_printf("\nGroup name Comment"\
762 "\n-----------------------------\n");
763 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
765 "(objectCategory=group)",
766 opt_long_list_entries ? longattrs :
767 shortattrs, usergrp_display,
771 return ADS_ERR_OK(rc) ? 0 : -1;
773 return net_run_function(argc, argv, func, net_ads_group_usage);
776 static int net_ads_status(int argc, const char **argv)
782 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
786 rc = ads_find_machine_acct(ads, &res, global_myname());
787 if (!ADS_ERR_OK(rc)) {
788 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
793 if (ads_count_replies(ads, res) == 0) {
794 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
804 /*******************************************************************
805 Leave an AD domain. Windows XP disables the machine account.
806 We'll try the same. The old code would do an LDAP delete.
807 That only worked using the machine creds because added the machine
808 with full control to the computer object's ACL.
809 *******************************************************************/
811 static int net_ads_leave(int argc, const char **argv)
813 ADS_STRUCT *ads = NULL;
817 struct cli_state *cli = NULL;
819 DOM_SID *dom_sid = NULL;
820 char *short_domain_name = NULL;
822 if (!secrets_init()) {
823 DEBUG(1,("Failed to initialise secrets database\n"));
827 if (!(ctx = talloc_init("net_ads_leave"))) {
828 d_fprintf(stderr, "Could not initialise talloc context.\n");
832 /* The finds a DC and takes care of getting the
833 user creds if necessary */
835 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
839 /* make RPC calls here */
841 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap.ss,
842 ads->config.ldap_server_name)) )
847 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
851 saf_delete( short_domain_name );
853 status = netdom_leave_domain(ctx, cli, dom_sid);
855 /* Try and delete it via LDAP - the old way we used to. */
857 adsret = ads_leave_realm(ads, global_myname());
858 if (ADS_ERR_OK(adsret)) {
859 d_printf("Deleted account for '%s' in realm '%s'\n",
860 global_myname(), ads->config.realm);
863 /* We couldn't delete it - see if the disable succeeded. */
864 if (NT_STATUS_IS_OK(status)) {
865 d_printf("Disabled account for '%s' in realm '%s'\n",
866 global_myname(), ads->config.realm);
869 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
870 global_myname(), ads->config.realm);
885 static NTSTATUS net_ads_join_ok(void)
887 ADS_STRUCT *ads = NULL;
890 if (!secrets_init()) {
891 DEBUG(1,("Failed to initialise secrets database\n"));
892 return NT_STATUS_ACCESS_DENIED;
895 net_use_krb_machine_account();
897 status = ads_startup(True, &ads);
898 if (!ADS_ERR_OK(status)) {
899 return ads_ntstatus(status);
907 check that an existing join is OK
909 int net_ads_testjoin(int argc, const char **argv)
912 use_in_memory_ccache();
914 /* Display success or failure */
915 status = net_ads_join_ok();
916 if (!NT_STATUS_IS_OK(status)) {
917 fprintf(stderr,"Join to domain is not valid: %s\n",
918 get_friendly_nt_error_msg(status));
922 printf("Join is OK\n");
926 /*******************************************************************
927 Simple configu checks before beginning the join
928 ********************************************************************/
930 static NTSTATUS check_ads_config( void )
932 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
933 d_printf("Host is not configured as a member server.\n");
934 return NT_STATUS_INVALID_DOMAIN_ROLE;
937 if (strlen(global_myname()) > 15) {
938 d_printf("Our netbios name can be at most 15 chars long, "
939 "\"%s\" is %u chars long\n", global_myname(),
940 (unsigned int)strlen(global_myname()));
941 return NT_STATUS_NAME_TOO_LONG;
944 if ( lp_security() == SEC_ADS && !*lp_realm()) {
945 d_fprintf(stderr, "realm must be set in in %s for ADS "
946 "join to succeed.\n", get_dyn_CONFIGFILE());
947 return NT_STATUS_INVALID_PARAMETER;
950 if (!secrets_init()) {
951 DEBUG(1,("Failed to initialise secrets database\n"));
952 /* This is a good bet for failure of secrets_init ... */
953 return NT_STATUS_ACCESS_DENIED;
959 /*******************************************************************
961 ********************************************************************/
963 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
964 struct sockaddr_storage *pss, char **domain,
966 const char *password)
968 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
969 struct cli_state *cli = NULL;
971 ret = connect_to_ipc_krb5(&cli, pss, servername);
972 if ( !NT_STATUS_IS_OK(ret) ) {
976 ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
977 if ( !NT_STATUS_IS_OK(ret) ) {
981 /* cli->server_domain is not filled in when using krb5
984 saf_store( *domain, cli->desthost );
986 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
995 /*******************************************************************
996 Set a machines dNSHostName and servicePrincipalName attributes
997 ********************************************************************/
999 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
1001 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1004 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1007 LDAPMessage *res = NULL;
1008 char *dn_string = NULL;
1009 const char *machine_name = global_myname();
1012 if ( !machine_name ) {
1013 return ADS_ERROR(LDAP_NO_MEMORY);
1018 status = ads_find_machine_acct(ads_s, &res, machine_name);
1019 if (!ADS_ERR_OK(status))
1022 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1023 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1024 return ADS_ERROR(LDAP_NO_MEMORY);
1027 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1028 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1032 new_dn = talloc_strdup(ctx, dn_string);
1033 ads_memfree(ads_s, dn_string);
1035 return ADS_ERROR(LDAP_NO_MEMORY);
1038 /* Windows only creates HOST/shortname & HOST/fqdn. */
1040 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1043 servicePrincipalName[0] = psp;
1045 name_to_fqdn(my_fqdn, machine_name);
1046 strlower_m(my_fqdn);
1047 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1049 servicePrincipalName[1] = psp;
1051 if (!(mods = ads_init_mods(ctx))) {
1055 /* fields of primary importance */
1057 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1058 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1060 status = ads_gen_mod(ads_s, new_dn, mods);
1063 ads_msgfree(ads_s, res);
1068 /*******************************************************************
1069 Set a machines dNSHostName and servicePrincipalName attributes
1070 ********************************************************************/
1072 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1074 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1077 LDAPMessage *res = NULL;
1078 char *dn_string = NULL;
1079 const char *machine_name = global_myname();
1082 if ( !machine_name ) {
1083 return ADS_ERROR(LDAP_NO_MEMORY);
1088 status = ads_find_machine_acct(ads_s, &res, machine_name);
1089 if (!ADS_ERR_OK(status))
1092 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1093 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1094 return ADS_ERROR(LDAP_NO_MEMORY);
1097 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1098 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1102 new_dn = talloc_strdup(ctx, dn_string);
1103 ads_memfree(ads_s, dn_string);
1105 return ADS_ERROR(LDAP_NO_MEMORY);
1108 /* now do the mods */
1110 if (!(mods = ads_init_mods(ctx))) {
1114 /* fields of primary importance */
1116 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1118 status = ads_gen_mod(ads_s, new_dn, mods);
1121 ads_msgfree(ads_s, res);
1126 /*******************************************************************
1127 Set a machines dNSHostName and servicePrincipalName attributes
1128 ********************************************************************/
1130 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1131 const char *os_name, const char *os_version )
1133 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1136 LDAPMessage *res = NULL;
1137 char *dn_string = NULL;
1138 const char *machine_name = global_myname();
1142 if ( !os_name || !os_version ) {
1143 return ADS_ERROR(LDAP_NO_MEMORY);
1148 status = ads_find_machine_acct(ads_s, &res, machine_name);
1149 if (!ADS_ERR_OK(status))
1152 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1153 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1154 return ADS_ERROR(LDAP_NO_MEMORY);
1157 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1158 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1162 new_dn = talloc_strdup(ctx, dn_string);
1163 ads_memfree(ads_s, dn_string);
1165 return ADS_ERROR(LDAP_NO_MEMORY);
1168 /* now do the mods */
1170 if (!(mods = ads_init_mods(ctx))) {
1174 os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1176 /* fields of primary importance */
1178 ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1179 ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1181 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1183 status = ads_gen_mod(ads_s, new_dn, mods);
1186 ads_msgfree(ads_s, res);
1187 TALLOC_FREE( os_sp );
1192 /*******************************************************************
1193 join a domain using ADS (LDAP mods)
1194 ********************************************************************/
1196 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1198 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1199 char *ou_str = NULL;
1201 LDAPMessage *res = NULL;
1204 ou_str = ads_ou_string(ads, ou);
1205 if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1206 rc = ADS_ERROR(LDAP_NO_MEMORY);
1210 rc = ads_search_dn(ads, &res, dn, NULL);
1211 if (!ADS_ERR_OK(rc)) {
1212 d_fprintf(stderr, "The specified OU does not exist.\n");
1216 /* Attempt to create the machine account and bail if this fails.
1217 Assume that the admin wants exactly what they requested */
1219 rc = ads_create_machine_acct( ads, global_myname(), dn );
1220 if (ADS_ERR_OK(rc)) {
1221 DEBUG(1, ("machine account created\n"));
1224 if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1225 DEBUG(1, ("machine account creation failed\n"));
1229 rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1230 if (!ADS_ERR_OK(rc)) {
1231 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1236 d_printf("The machine account was moved into the specified OU.\n");
1238 d_printf("The machine account already exists in the specified OU.\n");
1242 ads_msgfree(ads, res);
1243 SAFE_FREE( ou_str );
1249 /************************************************************************
1250 ************************************************************************/
1252 static bool net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1258 const char *machine_name = global_myname();
1260 status = ads_domain_func_level( ads, &domain_func );
1261 if ( !ADS_ERR_OK(status) ) {
1262 DEBUG(2,("Failed to determine domain functional level!\n"));
1266 /* go ahead and setup the default salt */
1268 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1269 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1273 fstrcpy( salt, std_salt );
1274 SAFE_FREE( std_salt );
1276 /* if it's a Windows functional domain, we have to look for the UPN */
1278 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1281 upn = ads_get_upn(ads, ctx, machine_name);
1283 fstrcpy( salt, upn );
1287 return kerberos_secrets_store_des_salt( salt );
1290 /*******************************************************************
1291 Send a DNS update request
1292 *******************************************************************/
1294 #if defined(WITH_DNS_UPDATES)
1296 DNS_ERROR DoDNSUpdate(char *pszServerName,
1297 const char *pszDomainName,
1298 const char *pszHostName,
1299 const struct in_addr *iplist, int num_addrs );
1302 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1303 const char *machine_name,
1304 const struct in_addr *addrs,
1307 struct dns_rr_ns *nameservers = NULL;
1309 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1312 const char *dnsdomain = NULL;
1313 char *root_domain = NULL;
1315 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1316 d_printf("No DNS domain configured for %s. "
1317 "Unable to perform DNS Update.\n", machine_name);
1318 status = NT_STATUS_INVALID_PARAMETER;
1323 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1324 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1325 /* Child domains often do not have NS records. Look
1326 for the NS record for the forest root domain
1327 (rootDomainNamingContext in therootDSE) */
1329 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1330 LDAPMessage *msg = NULL;
1332 ADS_STATUS ads_status;
1334 if ( !ads->ldap.ld ) {
1335 ads_status = ads_connect( ads );
1336 if ( !ADS_ERR_OK(ads_status) ) {
1337 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1342 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1343 "(objectclass=*)", rootname_attrs, &msg);
1344 if (!ADS_ERR_OK(ads_status)) {
1348 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1350 ads_msgfree( ads, msg );
1354 root_domain = ads_build_domain( root_dn );
1357 ads_msgfree( ads, msg );
1359 /* try again for NS servers */
1361 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1363 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1364 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1365 "realm\n", ads->config.realm));
1369 dnsdomain = root_domain;
1373 /* Now perform the dns update - we'll try non-secure and if we fail,
1374 we'll follow it up with a secure update */
1376 fstrcpy( dns_server, nameservers[0].hostname );
1378 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1379 if (!ERR_DNS_IS_OK(dns_err)) {
1380 status = NT_STATUS_UNSUCCESSFUL;
1385 SAFE_FREE( root_domain );
1390 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1393 struct in_addr *iplist = NULL;
1394 fstring machine_name;
1397 name_to_fqdn( machine_name, global_myname() );
1398 strlower_m( machine_name );
1400 /* Get our ip address (not the 127.0.0.x address but a real ip
1403 num_addrs = get_my_ip_address( &iplist );
1404 if ( num_addrs <= 0 ) {
1405 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1407 return NT_STATUS_INVALID_PARAMETER;
1410 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1412 SAFE_FREE( iplist );
1418 /*******************************************************************
1419 ********************************************************************/
1421 static int net_ads_join_usage(int argc, const char **argv)
1423 d_printf("net ads join [options]\n");
1424 d_printf("Valid options:\n");
1425 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1426 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1427 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1428 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1429 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1430 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1431 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1432 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1433 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1434 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1435 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1436 d_printf(" the two other attributes.\n");
1441 /*******************************************************************
1442 ********************************************************************/
1444 int net_ads_join(int argc, const char **argv)
1446 ADS_STRUCT *ads = NULL;
1449 char *short_domain_name = NULL;
1450 char *tmp_password, *password;
1451 TALLOC_CTX *ctx = NULL;
1452 DOM_SID *domain_sid = NULL;
1453 bool createupn = False;
1454 const char *machineupn = NULL;
1455 const char *create_in_ou = NULL;
1458 struct sockaddr_storage dcss;
1459 const char *os_name = NULL;
1460 const char *os_version = NULL;
1462 nt_status = check_ads_config();
1463 if (!NT_STATUS_IS_OK(nt_status)) {
1464 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1468 /* find a DC to initialize the server affinity cache */
1470 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcss );
1472 status = ads_startup(True, &ads);
1473 if (!ADS_ERR_OK(status)) {
1474 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1475 nt_status = ads_ntstatus(status);
1479 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1480 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1481 "(%s) DO NOT match. Aborting join\n",
1482 ads->config.realm, get_dyn_CONFIGFILE(), lp_realm());
1483 nt_status = NT_STATUS_INVALID_PARAMETER;
1487 if (!(ctx = talloc_init("net_ads_join"))) {
1488 d_fprintf(stderr, "Could not initialise talloc context.\n");
1489 nt_status = NT_STATUS_NO_MEMORY;
1493 /* process additional command line args */
1495 for ( i=0; i<argc; i++ ) {
1496 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1498 machineupn = get_string_param(argv[i]);
1500 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1501 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1502 d_fprintf(stderr, "Please supply a valid OU path.\n");
1503 nt_status = NT_STATUS_INVALID_PARAMETER;
1507 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1508 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1509 d_fprintf(stderr, "Please supply a operating system name.\n");
1510 nt_status = NT_STATUS_INVALID_PARAMETER;
1514 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1515 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1516 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1517 nt_status = NT_STATUS_INVALID_PARAMETER;
1522 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1523 nt_status = NT_STATUS_INVALID_PARAMETER;
1528 /* If we were given an OU, try to create the machine in
1529 the OU account first and then do the normal RPC join */
1531 if ( create_in_ou ) {
1532 status = net_precreate_machine_acct( ads, create_in_ou );
1533 if ( !ADS_ERR_OK(status) ) {
1534 d_fprintf( stderr, "Failed to pre-create the machine object "
1535 "in OU %s.\n", create_in_ou);
1536 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1537 ads_errstr(status)));
1538 nt_status = ads_ntstatus(status);
1543 /* Do the domain join here */
1545 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1546 password = talloc_strdup(ctx, tmp_password);
1548 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1549 &ads->ldap.ss, &short_domain_name, &domain_sid, password);
1550 if ( !NT_STATUS_IS_OK(nt_status) ) {
1551 DEBUG(1, ("call of net_join_domain failed: %s\n",
1552 get_friendly_nt_error_msg(nt_status)));
1556 /* Check the short name of the domain */
1558 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1559 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1560 d_printf("domain name obtained from the server.\n");
1561 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1562 d_printf("You should set \"workgroup = %s\" in %s.\n",
1563 short_domain_name, get_dyn_CONFIGFILE());
1566 d_printf("Using short domain name -- %s\n", short_domain_name);
1568 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1569 value from smb.conf and the string returned from the server. The former is
1570 neede to bootstrap winbindd's first connection to the DC to get the real
1571 short domain name --jerry */
1573 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1574 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1576 /* issue an internal error here for now.
1577 * everything else would mean changing tdb routines. */
1578 nt_status = NT_STATUS_INTERNAL_ERROR;
1582 /* Verify that everything is ok */
1584 nt_status = net_rpc_join_ok(short_domain_name,
1585 ads->config.ldap_server_name, &ads->ldap.ss);
1586 if (!NT_STATUS_IS_OK(nt_status)) {
1588 "Failed to verify membership in domain: %s!\n",
1589 nt_errstr(nt_status));
1593 /* create the dNSHostName & servicePrincipalName values */
1595 status = net_set_machine_spn( ctx, ads );
1596 if ( !ADS_ERR_OK(status) ) {
1598 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1599 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1600 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1602 /* Disable the machine account in AD. Better to fail than to leave
1603 a confused admin. */
1605 if ( net_ads_leave( 0, NULL ) != 0 ) {
1606 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1609 /* clear out the machine password */
1611 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1612 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1614 nt_status = ads_ntstatus(status);
1618 if ( !net_derive_salting_principal( ctx, ads ) ) {
1619 DEBUG(1,("Failed to determine salting principal\n"));
1626 /* default to using the short UPN name */
1628 upn = talloc_asprintf(ctx,
1629 "host/%s@%s", global_myname(),
1630 ads->config.realm );
1632 nt_status = NT_STATUS_NO_MEMORY;
1638 status = net_set_machine_upn( ctx, ads, machineupn );
1639 if ( !ADS_ERR_OK(status) ) {
1640 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1644 /* Try to set the operatingSystem attributes if asked */
1646 if ( os_name && os_version ) {
1647 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1648 if ( !ADS_ERR_OK(status) ) {
1649 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1650 "Are you a Domain Admin?\n");
1654 /* Now build the keytab, using the same ADS connection */
1656 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1657 DEBUG(1,("Error creating host keytab!\n"));
1660 #if defined(WITH_DNS_UPDATES)
1661 /* We enter this block with user creds */
1662 ads_kdestroy( NULL );
1666 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1667 /* kinit with the machine password */
1669 use_in_memory_ccache();
1670 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1671 ads->auth.password = secrets_fetch_machine_password(
1672 lp_workgroup(), NULL, NULL );
1673 ads->auth.realm = SMB_STRDUP( lp_realm() );
1674 ads_kinit_password( ads );
1677 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1678 d_fprintf( stderr, "DNS update failed!\n" );
1681 /* exit from this block using machine creds */
1684 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1692 /* issue an overall failure message at the end. */
1693 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1702 /*******************************************************************
1703 ********************************************************************/
1705 static int net_ads_dns_usage(int argc, const char **argv)
1707 #if defined(WITH_DNS_UPDATES)
1708 d_printf("net ads dns <command>\n");
1709 d_printf("Valid commands:\n");
1710 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1714 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1719 /*******************************************************************
1720 ********************************************************************/
1722 static int net_ads_dns_register(int argc, const char **argv)
1724 #if defined(WITH_DNS_UPDATES)
1730 talloc_enable_leak_report();
1734 d_fprintf(stderr, "net ads dns register\n");
1738 if (!(ctx = talloc_init("net_ads_dns"))) {
1739 d_fprintf(stderr, "Could not initialise talloc context\n");
1743 status = ads_startup(True, &ads);
1744 if ( !ADS_ERR_OK(status) ) {
1745 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1750 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1751 d_fprintf( stderr, "DNS update failed!\n" );
1752 ads_destroy( &ads );
1757 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1764 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1769 #if defined(WITH_DNS_UPDATES)
1770 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1773 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1775 #if defined(WITH_DNS_UPDATES)
1779 talloc_enable_leak_report();
1783 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1788 err = do_gethostbyname(argv[0], argv[1]);
1790 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1795 static int net_ads_dns(int argc, const char *argv[])
1797 struct functable func[] = {
1798 {"REGISTER", net_ads_dns_register},
1799 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1803 return net_run_function(argc, argv, func, net_ads_dns_usage);
1806 /*******************************************************************
1807 ********************************************************************/
1809 int net_ads_printer_usage(int argc, const char **argv)
1812 "\nnet ads printer search <printer>"
1813 "\n\tsearch for a printer in the directory\n"
1814 "\nnet ads printer info <printer> <server>"
1815 "\n\tlookup info in directory for printer on server"
1816 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1817 "\nnet ads printer publish <printername>"
1818 "\n\tpublish printer in directory"
1819 "\n\t(note: printer name is required)\n"
1820 "\nnet ads printer remove <printername>"
1821 "\n\tremove printer from directory"
1822 "\n\t(note: printer name is required)\n");
1826 /*******************************************************************
1827 ********************************************************************/
1829 static int net_ads_printer_search(int argc, const char **argv)
1833 LDAPMessage *res = NULL;
1835 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1839 rc = ads_find_printers(ads, &res);
1841 if (!ADS_ERR_OK(rc)) {
1842 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1843 ads_msgfree(ads, res);
1848 if (ads_count_replies(ads, res) == 0) {
1849 d_fprintf(stderr, "No results found\n");
1850 ads_msgfree(ads, res);
1856 ads_msgfree(ads, res);
1861 static int net_ads_printer_info(int argc, const char **argv)
1865 const char *servername, *printername;
1866 LDAPMessage *res = NULL;
1868 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1873 printername = argv[0];
1879 servername = argv[1];
1881 servername = global_myname();
1884 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1886 if (!ADS_ERR_OK(rc)) {
1887 d_fprintf(stderr, "Server '%s' not found: %s\n",
1888 servername, ads_errstr(rc));
1889 ads_msgfree(ads, res);
1894 if (ads_count_replies(ads, res) == 0) {
1895 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1896 ads_msgfree(ads, res);
1902 ads_msgfree(ads, res);
1908 static int net_ads_printer_publish(int argc, const char **argv)
1912 const char *servername, *printername;
1913 struct cli_state *cli;
1914 struct rpc_pipe_client *pipe_hnd;
1915 struct sockaddr_storage server_ss;
1917 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1918 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1919 char *prt_dn, *srv_dn, **srv_cn;
1920 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1921 LDAPMessage *res = NULL;
1923 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1924 talloc_destroy(mem_ctx);
1929 talloc_destroy(mem_ctx);
1930 return net_ads_printer_usage(argc, argv);
1933 printername = argv[0];
1936 servername = argv[1];
1938 servername = global_myname();
1941 /* Get printer data from SPOOLSS */
1943 resolve_name(servername, &server_ss, 0x20);
1945 nt_status = cli_full_connection(&cli, global_myname(), servername,
1948 opt_user_name, opt_workgroup,
1949 opt_password ? opt_password : "",
1950 CLI_FULL_CONNECTION_USE_KERBEROS,
1953 if (NT_STATUS_IS_ERR(nt_status)) {
1954 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1955 "for %s\n", servername, printername);
1957 talloc_destroy(mem_ctx);
1961 /* Publish on AD server */
1963 ads_find_machine_acct(ads, &res, servername);
1965 if (ads_count_replies(ads, res) == 0) {
1966 d_fprintf(stderr, "Could not find machine account for server %s\n",
1969 talloc_destroy(mem_ctx);
1973 srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1974 srv_cn = ldap_explode_dn(srv_dn, 1);
1976 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1977 printername_escaped = escape_rdn_val_string_alloc(printername);
1978 if (!srv_cn_escaped || !printername_escaped) {
1979 SAFE_FREE(srv_cn_escaped);
1980 SAFE_FREE(printername_escaped);
1981 d_fprintf(stderr, "Internal error, out of memory!");
1983 talloc_destroy(mem_ctx);
1987 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1989 SAFE_FREE(srv_cn_escaped);
1990 SAFE_FREE(printername_escaped);
1992 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1994 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1998 talloc_destroy(mem_ctx);
2002 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
2006 talloc_destroy(mem_ctx);
2010 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
2011 if (!ADS_ERR_OK(rc)) {
2012 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2015 talloc_destroy(mem_ctx);
2019 d_printf("published printer\n");
2022 talloc_destroy(mem_ctx);
2027 static int net_ads_printer_remove(int argc, const char **argv)
2031 const char *servername;
2033 LDAPMessage *res = NULL;
2035 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2040 return net_ads_printer_usage(argc, argv);
2044 servername = argv[1];
2046 servername = global_myname();
2049 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2051 if (!ADS_ERR_OK(rc)) {
2052 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2053 ads_msgfree(ads, res);
2058 if (ads_count_replies(ads, res) == 0) {
2059 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2060 ads_msgfree(ads, res);
2065 prt_dn = ads_get_dn(ads, res);
2066 ads_msgfree(ads, res);
2067 rc = ads_del_dn(ads, prt_dn);
2068 ads_memfree(ads, prt_dn);
2070 if (!ADS_ERR_OK(rc)) {
2071 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2080 static int net_ads_printer(int argc, const char **argv)
2082 struct functable func[] = {
2083 {"SEARCH", net_ads_printer_search},
2084 {"INFO", net_ads_printer_info},
2085 {"PUBLISH", net_ads_printer_publish},
2086 {"REMOVE", net_ads_printer_remove},
2090 return net_run_function(argc, argv, func, net_ads_printer_usage);
2094 static int net_ads_password(int argc, const char **argv)
2097 const char *auth_principal = opt_user_name;
2098 const char *auth_password = opt_password;
2100 char *new_password = NULL;
2105 if (opt_user_name == NULL || opt_password == NULL) {
2106 d_fprintf(stderr, "You must supply an administrator username/password\n");
2111 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2116 if (!strchr_m(user, '@')) {
2117 asprintf(&c, "%s@%s", argv[0], lp_realm());
2121 use_in_memory_ccache();
2122 c = strchr_m(auth_principal, '@');
2129 /* use the realm so we can eventually change passwords for users
2130 in realms other than default */
2131 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2135 /* we don't actually need a full connect, but it's the easy way to
2136 fill in the KDC's addresss */
2139 if (!ads || !ads->config.realm) {
2140 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2145 new_password = (char *)argv[1];
2147 asprintf(&prompt, "Enter new password for %s:", user);
2148 new_password = getpass(prompt);
2152 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2153 auth_password, user, new_password, ads->auth.time_offset);
2154 if (!ADS_ERR_OK(ret)) {
2155 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2160 d_printf("Password change for %s completed.\n", user);
2166 int net_ads_changetrustpw(int argc, const char **argv)
2169 char *host_principal;
2173 if (!secrets_init()) {
2174 DEBUG(1,("Failed to initialise secrets database\n"));
2178 net_use_krb_machine_account();
2180 use_in_memory_ccache();
2182 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2186 fstrcpy(my_name, global_myname());
2187 strlower_m(my_name);
2188 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2189 d_printf("Changing password for principal: %s\n", host_principal);
2191 ret = ads_change_trust_account_password(ads, host_principal);
2193 if (!ADS_ERR_OK(ret)) {
2194 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2196 SAFE_FREE(host_principal);
2200 d_printf("Password change for principal %s succeeded.\n", host_principal);
2202 if (lp_use_kerberos_keytab()) {
2203 d_printf("Attempting to update system keytab with new password.\n");
2204 if (ads_keytab_create_default(ads)) {
2205 d_printf("Failed to update system keytab.\n");
2210 SAFE_FREE(host_principal);
2216 help for net ads search
2218 static int net_ads_search_usage(int argc, const char **argv)
2221 "\nnet ads search <expression> <attributes...>\n"\
2222 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2223 "The expression is a standard LDAP search expression, and the\n"\
2224 "attributes are a list of LDAP fields to show in the results\n\n"\
2225 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2227 net_common_flags_usage(argc, argv);
2233 general ADS search function. Useful in diagnosing problems in ADS
2235 static int net_ads_search(int argc, const char **argv)
2239 const char *ldap_exp;
2241 LDAPMessage *res = NULL;
2244 return net_ads_search_usage(argc, argv);
2247 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2254 rc = ads_do_search_all(ads, ads->config.bind_path,
2256 ldap_exp, attrs, &res);
2257 if (!ADS_ERR_OK(rc)) {
2258 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2263 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2265 /* dump the results */
2268 ads_msgfree(ads, res);
2276 help for net ads search
2278 static int net_ads_dn_usage(int argc, const char **argv)
2281 "\nnet ads dn <dn> <attributes...>\n"\
2282 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2283 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2284 "to show in the results\n\n"\
2285 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2286 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2288 net_common_flags_usage(argc, argv);
2294 general ADS search function. Useful in diagnosing problems in ADS
2296 static int net_ads_dn(int argc, const char **argv)
2302 LDAPMessage *res = NULL;
2305 return net_ads_dn_usage(argc, argv);
2308 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2315 rc = ads_do_search_all(ads, dn,
2317 "(objectclass=*)", attrs, &res);
2318 if (!ADS_ERR_OK(rc)) {
2319 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2324 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2326 /* dump the results */
2329 ads_msgfree(ads, res);
2336 help for net ads sid search
2338 static int net_ads_sid_usage(int argc, const char **argv)
2341 "\nnet ads sid <sid> <attributes...>\n"\
2342 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2343 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2344 "to show in the results\n\n"\
2345 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2347 net_common_flags_usage(argc, argv);
2353 general ADS search function. Useful in diagnosing problems in ADS
2355 static int net_ads_sid(int argc, const char **argv)
2359 const char *sid_string;
2361 LDAPMessage *res = NULL;
2365 return net_ads_sid_usage(argc, argv);
2368 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2372 sid_string = argv[0];
2375 if (!string_to_sid(&sid, sid_string)) {
2376 d_fprintf(stderr, "could not convert sid\n");
2381 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2382 if (!ADS_ERR_OK(rc)) {
2383 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2388 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2390 /* dump the results */
2393 ads_msgfree(ads, res);
2400 static int net_ads_keytab_usage(int argc, const char **argv)
2403 "net ads keytab <COMMAND>\n"\
2404 "<COMMAND> can be either:\n"\
2405 " ADD Adds new service principal\n"\
2406 " CREATE Creates a fresh keytab\n"\
2407 " FLUSH Flushes out all keytab entries\n"\
2408 " HELP Prints this help message\n"\
2409 " LIST List the keytab\n"\
2410 "The ADD and LIST command will take arguments, the other commands\n"\
2411 "will not take any arguments. The arguments given to ADD\n"\
2412 "should be a list of principals to add. For example, \n"\
2413 " net ads keytab add srv1 srv2\n"\
2414 "will add principals for the services srv1 and srv2 to the\n"\
2415 "system's keytab.\n"\
2416 "The LIST command takes a keytabname.\n"\
2422 static int net_ads_keytab_flush(int argc, const char **argv)
2427 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2430 ret = ads_keytab_flush(ads);
2435 static int net_ads_keytab_add(int argc, const char **argv)
2441 d_printf("Processing principals to add...\n");
2442 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2445 for (i = 0; i < argc; i++) {
2446 ret |= ads_keytab_add_entry(ads, argv[i]);
2452 static int net_ads_keytab_create(int argc, const char **argv)
2457 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2460 ret = ads_keytab_create_default(ads);
2465 static int net_ads_keytab_list(int argc, const char **argv)
2467 const char *keytab = NULL;
2473 return ads_keytab_list(keytab);
2477 int net_ads_keytab(int argc, const char **argv)
2479 struct functable func[] = {
2480 {"ADD", net_ads_keytab_add},
2481 {"CREATE", net_ads_keytab_create},
2482 {"FLUSH", net_ads_keytab_flush},
2483 {"HELP", net_ads_keytab_usage},
2484 {"LIST", net_ads_keytab_list},
2488 if (!lp_use_kerberos_keytab()) {
2489 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2490 use keytab functions.\n");
2493 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2496 static int net_ads_kerberos_usage(int argc, const char **argv)
2499 "net ads kerberos <COMMAND>\n"\
2500 "<COMMAND> can be either:\n"\
2501 " RENEW Renew TGT from existing credential cache\n"\
2502 " PAC Dumps the Kerberos PAC\n"\
2503 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2510 static int net_ads_kerberos_renew(int argc, const char **argv)
2512 int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2514 d_printf("failed to renew kerberos ticket: %s\n",
2515 error_message(ret));
2520 static int net_ads_kerberos_pac(int argc, const char **argv)
2522 PAC_DATA *pac = NULL;
2523 PAC_LOGON_INFO *info = NULL;
2524 TALLOC_CTX *mem_ctx = NULL;
2528 mem_ctx = talloc_init("net_ads_kerberos_pac");
2533 opt_password = net_prompt_pass(opt_user_name);
2535 status = kerberos_return_pac(mem_ctx,
2544 2592000, /* one month */
2546 if (!NT_STATUS_IS_OK(status)) {
2547 d_printf("failed to query kerberos PAC: %s\n",
2552 info = get_logon_info_from_pac(pac);
2554 dump_pac_logon_info(0, info);
2559 TALLOC_FREE(mem_ctx);
2563 static int net_ads_kerberos_kinit(int argc, const char **argv)
2565 TALLOC_CTX *mem_ctx = NULL;
2569 mem_ctx = talloc_init("net_ads_kerberos_kinit");
2574 opt_password = net_prompt_pass(opt_user_name);
2576 ret = kerberos_kinit_password_ext(opt_user_name,
2584 2592000, /* one month */
2587 d_printf("failed to kinit password: %s\n",
2594 int net_ads_kerberos(int argc, const char **argv)
2596 struct functable func[] = {
2597 {"KINIT", net_ads_kerberos_kinit},
2598 {"RENEW", net_ads_kerberos_renew},
2599 {"PAC", net_ads_kerberos_pac},
2600 {"HELP", net_ads_kerberos_usage},
2604 return net_run_function(argc, argv, func, net_ads_kerberos_usage);
2608 int net_ads_help(int argc, const char **argv)
2610 struct functable func[] = {
2611 {"USER", net_ads_user_usage},
2612 {"GROUP", net_ads_group_usage},
2613 {"PRINTER", net_ads_printer_usage},
2614 {"SEARCH", net_ads_search_usage},
2615 {"INFO", net_ads_info},
2616 {"JOIN", net_ads_join_usage},
2617 {"DNS", net_ads_dns_usage},
2618 {"LEAVE", net_ads_leave},
2619 {"STATUS", net_ads_status},
2620 {"PASSWORD", net_ads_password},
2621 {"CHANGETRUSTPW", net_ads_changetrustpw},
2625 return net_run_function(argc, argv, func, net_ads_usage);
2628 int net_ads(int argc, const char **argv)
2630 struct functable func[] = {
2631 {"INFO", net_ads_info},
2632 {"JOIN", net_ads_join},
2633 {"TESTJOIN", net_ads_testjoin},
2634 {"LEAVE", net_ads_leave},
2635 {"STATUS", net_ads_status},
2636 {"USER", net_ads_user},
2637 {"GROUP", net_ads_group},
2638 {"DNS", net_ads_dns},
2639 {"PASSWORD", net_ads_password},
2640 {"CHANGETRUSTPW", net_ads_changetrustpw},
2641 {"PRINTER", net_ads_printer},
2642 {"SEARCH", net_ads_search},
2644 {"SID", net_ads_sid},
2645 {"WORKGROUP", net_ads_workgroup},
2646 {"LOOKUP", net_ads_lookup},
2647 {"KEYTAB", net_ads_keytab},
2648 {"GPO", net_ads_gpo},
2649 {"KERBEROS", net_ads_kerberos},
2650 {"HELP", net_ads_help},
2654 return net_run_function(argc, argv, func, net_ads_usage);
2659 static int net_ads_noads(void)
2661 d_fprintf(stderr, "ADS support not compiled in\n");
2665 int net_ads_keytab(int argc, const char **argv)
2667 return net_ads_noads();
2670 int net_ads_kerberos(int argc, const char **argv)
2672 return net_ads_noads();
2675 int net_ads_usage(int argc, const char **argv)
2677 return net_ads_noads();
2680 int net_ads_help(int argc, const char **argv)
2682 return net_ads_noads();
2685 int net_ads_changetrustpw(int argc, const char **argv)
2687 return net_ads_noads();
2690 int net_ads_join(int argc, const char **argv)
2692 return net_ads_noads();
2695 int net_ads_user(int argc, const char **argv)
2697 return net_ads_noads();
2700 int net_ads_group(int argc, const char **argv)
2702 return net_ads_noads();
2705 /* this one shouldn't display a message */
2706 int net_ads_check(void)
2711 int net_ads_check_our_domain(void)
2716 int net_ads(int argc, const char **argv)
2718 return net_ads_usage(argc, argv);
2721 #endif /* WITH_ADS */