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"
26 #include "libnet/libnet.h"
30 int net_ads_usage(int argc, const char **argv)
32 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
33 d_printf(" Join the local machine to a ADS realm\n");
35 d_printf(" Remove the local machine from a ADS realm\n");
36 d_printf("testjoin\n");
37 d_printf(" Validates the machine account in the domain\n");
39 d_printf(" List, add, or delete users in the realm\n");
41 d_printf(" List, add, or delete groups in the realm\n");
43 d_printf(" Displays details regarding a specific AD server\n");
45 d_printf(" Display details regarding the machine's account in AD\n");
47 d_printf(" Performs CLDAP query of AD domain controllers\n");
48 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
49 d_printf(" Change a user's password using an admin account\n");
50 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
51 d_printf("changetrustpw\n");
52 d_printf(" Change the trust account password of this machine in the AD tree\n");
53 d_printf("printer [info | publish | remove] <printername> <servername>\n");
54 d_printf(" Lookup, add, or remove directory entry for a printer\n");
55 d_printf("{search,dn,sid}\n");
56 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
58 d_printf(" Manage a local keytab file based on the machine account in AD\n");
60 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
61 d_printf(" (using the machine credentials)\n");
66 /* when we do not have sufficient input parameters to contact a remote domain
67 * we always fall back to our own realm - Guenther*/
69 static const char *assume_own_realm(void)
71 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
79 do a cldap netlogon query
81 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
83 char addr[INET6_ADDRSTRLEN];
84 struct cldap_netlogon_reply reply;
87 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
88 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
89 d_fprintf(stderr, "CLDAP query failed!\n");
93 d_printf("Information for Domain Controller: %s\n\n",
96 d_printf("Response Type: ");
98 case SAMLOGON_AD_UNK_R:
99 d_printf("SAMLOGON\n");
102 d_printf("SAMLOGON_USER\n");
105 d_printf("0x%x\n", reply.type);
109 smb_uuid_unpack(reply.guid, &tmp_guid);
110 d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid));
114 "\tIs a GC of the forest: %s\n"
115 "\tIs an LDAP server: %s\n"
116 "\tSupports DS: %s\n"
117 "\tIs running a KDC: %s\n"
118 "\tIs running time services: %s\n"
119 "\tIs the closest DC: %s\n"
120 "\tIs writable: %s\n"
121 "\tHas a hardware clock: %s\n"
122 "\tIs a non-domain NC serviced by LDAP server: %s\n",
123 (reply.flags & ADS_PDC) ? "yes" : "no",
124 (reply.flags & ADS_GC) ? "yes" : "no",
125 (reply.flags & ADS_LDAP) ? "yes" : "no",
126 (reply.flags & ADS_DS) ? "yes" : "no",
127 (reply.flags & ADS_KDC) ? "yes" : "no",
128 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
129 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
130 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
131 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
132 (reply.flags & ADS_NDNC) ? "yes" : "no");
134 printf("Forest:\t\t\t%s\n", reply.forest);
135 printf("Domain:\t\t\t%s\n", reply.domain);
136 printf("Domain Controller:\t%s\n", reply.hostname);
138 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
139 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
141 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
142 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
144 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
145 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
147 d_printf("NT Version: %d\n", reply.version);
148 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
149 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
155 this implements the CLDAP based netlogon lookup requests
156 for finding the domain controller of a ADS domain
158 static int net_ads_lookup(int argc, const char **argv)
162 if (!ADS_ERR_OK(ads_startup_nobind(False, &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 char addr[INET6_ADDRSTRLEN];
182 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
183 d_fprintf(stderr, "Didn't find the ldap server!\n");
187 if (!ads || !ads->config.realm) {
188 d_fprintf(stderr, "Didn't find the ldap server!\n");
192 /* Try to set the server's current time since we didn't do a full
193 TCP LDAP session initially */
195 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
196 d_fprintf( stderr, "Failed to get server's current time!\n");
199 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
201 d_printf("LDAP server: %s\n", addr);
202 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
203 d_printf("Realm: %s\n", ads->config.realm);
204 d_printf("Bind Path: %s\n", ads->config.bind_path);
205 d_printf("LDAP port: %d\n", ads->ldap.port);
206 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
208 d_printf("KDC server: %s\n", ads->auth.kdc_server );
209 d_printf("Server time offset: %d\n", ads->auth.time_offset );
214 static void use_in_memory_ccache(void) {
215 /* Use in-memory credentials cache so we do not interfere with
216 * existing credentials */
217 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
220 static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
222 ADS_STRUCT *ads = NULL;
224 bool need_password = False;
225 bool second_time = False;
227 const char *realm = NULL;
228 bool tried_closest_dc = False;
230 /* lp_realm() should be handled by a command line param,
231 However, the join requires that realm be set in smb.conf
232 and compares our realm with the remote server's so this is
233 ok until someone needs more flexibility */
238 if (only_own_domain) {
241 realm = assume_own_realm();
244 ads = ads_init(realm, opt_target_workgroup, opt_host);
246 if (!opt_user_name) {
247 opt_user_name = "administrator";
250 if (opt_user_specified) {
251 need_password = True;
255 if (!opt_password && need_password && !opt_machine_pass) {
256 opt_password = net_prompt_pass(opt_user_name);
259 return ADS_ERROR(LDAP_NO_MEMORY);
264 use_in_memory_ccache();
265 SAFE_FREE(ads->auth.password);
266 ads->auth.password = smb_xstrdup(opt_password);
269 ads->auth.flags |= auth_flags;
270 SAFE_FREE(ads->auth.user_name);
271 ads->auth.user_name = smb_xstrdup(opt_user_name);
274 * If the username is of the form "name@realm",
275 * extract the realm and convert to upper case.
276 * This is only used to establish the connection.
278 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
280 SAFE_FREE(ads->auth.realm);
281 ads->auth.realm = smb_xstrdup(cp);
282 strupper_m(ads->auth.realm);
285 status = ads_connect(ads);
287 if (!ADS_ERR_OK(status)) {
289 if (NT_STATUS_EQUAL(ads_ntstatus(status),
290 NT_STATUS_NO_LOGON_SERVERS)) {
291 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
296 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
297 need_password = True;
306 /* when contacting our own domain, make sure we use the closest DC.
307 * This is done by reconnecting to ADS because only the first call to
308 * ads_connect will give us our own sitename */
310 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
312 tried_closest_dc = True; /* avoid loop */
314 if (!ads->config.tried_closest_dc) {
316 namecache_delete(ads->server.realm, 0x1C);
317 namecache_delete(ads->server.workgroup, 0x1C);
330 ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
332 return ads_startup_int(only_own_domain, 0, ads);
335 ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
337 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
341 Check to see if connection can be made via ads.
342 ads_startup() stores the password in opt_password if it needs to so
343 that rpc or rap can use it without re-prompting.
345 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
350 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
354 ads->auth.flags |= ADS_AUTH_NO_BIND;
356 status = ads_connect(ads);
357 if ( !ADS_ERR_OK(status) ) {
365 int net_ads_check_our_domain(void)
367 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
370 int net_ads_check(void)
372 return net_ads_check_int(NULL, opt_workgroup, opt_host);
376 determine the netbios workgroup name for a domain
378 static int net_ads_workgroup(int argc, const char **argv)
381 char addr[INET6_ADDRSTRLEN];
382 struct cldap_netlogon_reply reply;
384 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
385 d_fprintf(stderr, "Didn't find the cldap server!\n");
389 if (!ads->config.realm) {
390 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
391 ads->ldap.port = 389;
394 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
395 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
396 d_fprintf(stderr, "CLDAP query failed!\n");
400 d_printf("Workgroup: %s\n", reply.netbios_domain);
409 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
411 char **disp_fields = (char **) data_area;
413 if (!field) { /* must be end of record */
414 if (disp_fields[0]) {
415 if (!strchr_m(disp_fields[0], '$')) {
417 d_printf("%-21.21s %s\n",
418 disp_fields[0], disp_fields[1]);
420 d_printf("%s\n", disp_fields[0]);
423 SAFE_FREE(disp_fields[0]);
424 SAFE_FREE(disp_fields[1]);
427 if (!values) /* must be new field, indicate string field */
429 if (StrCaseCmp(field, "sAMAccountName") == 0) {
430 disp_fields[0] = SMB_STRDUP((char *) values[0]);
432 if (StrCaseCmp(field, "description") == 0)
433 disp_fields[1] = SMB_STRDUP((char *) values[0]);
437 static int net_ads_user_usage(int argc, const char **argv)
439 return net_help_user(argc, argv);
442 static int ads_user_add(int argc, const char **argv)
447 LDAPMessage *res=NULL;
451 if (argc < 1) return net_ads_user_usage(argc, argv);
453 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
457 status = ads_find_user_acct(ads, &res, argv[0]);
459 if (!ADS_ERR_OK(status)) {
460 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
464 if (ads_count_replies(ads, res)) {
465 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
470 ou_str = SMB_STRDUP(opt_container);
472 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
475 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
477 if (!ADS_ERR_OK(status)) {
478 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
483 /* if no password is to be set, we're done */
485 d_printf("User %s added\n", argv[0]);
490 /* try setting the password */
491 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
492 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
493 ads->auth.time_offset);
495 if (ADS_ERR_OK(status)) {
496 d_printf("User %s added\n", argv[0]);
501 /* password didn't set, delete account */
502 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
503 argv[0], ads_errstr(status));
504 ads_msgfree(ads, res);
505 status=ads_find_user_acct(ads, &res, argv[0]);
506 if (ADS_ERR_OK(status)) {
507 userdn = ads_get_dn(ads, res);
508 ads_del_dn(ads, userdn);
509 ads_memfree(ads, userdn);
514 ads_msgfree(ads, res);
520 static int ads_user_info(int argc, const char **argv)
525 const char *attrs[] = {"memberOf", NULL};
526 char *searchstring=NULL;
531 return net_ads_user_usage(argc, argv);
534 escaped_user = escape_ldap_string_alloc(argv[0]);
537 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
541 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
542 SAFE_FREE(escaped_user);
546 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
547 rc = ads_search(ads, &res, searchstring, attrs);
548 safe_free(searchstring);
550 if (!ADS_ERR_OK(rc)) {
551 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
553 SAFE_FREE(escaped_user);
557 grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
558 (LDAPMessage *)res, "memberOf");
563 for (i=0;grouplist[i];i++) {
564 groupname = ldap_explode_dn(grouplist[i], 1);
565 d_printf("%s\n", groupname[0]);
566 ldap_value_free(groupname);
568 ldap_value_free(grouplist);
571 ads_msgfree(ads, res);
573 SAFE_FREE(escaped_user);
577 static int ads_user_delete(int argc, const char **argv)
581 LDAPMessage *res = NULL;
585 return net_ads_user_usage(argc, argv);
588 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
592 rc = ads_find_user_acct(ads, &res, argv[0]);
593 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
594 d_printf("User %s does not exist.\n", argv[0]);
595 ads_msgfree(ads, res);
599 userdn = ads_get_dn(ads, res);
600 ads_msgfree(ads, res);
601 rc = ads_del_dn(ads, userdn);
602 ads_memfree(ads, userdn);
603 if (ADS_ERR_OK(rc)) {
604 d_printf("User %s deleted\n", argv[0]);
608 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
614 int net_ads_user(int argc, const char **argv)
616 struct functable func[] = {
617 {"ADD", ads_user_add},
618 {"INFO", ads_user_info},
619 {"DELETE", ads_user_delete},
624 const char *shortattrs[] = {"sAMAccountName", NULL};
625 const char *longattrs[] = {"sAMAccountName", "description", NULL};
626 char *disp_fields[2] = {NULL, NULL};
629 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
633 if (opt_long_list_entries)
634 d_printf("\nUser name Comment"\
635 "\n-----------------------------\n");
637 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
639 "(objectCategory=user)",
640 opt_long_list_entries ? longattrs :
641 shortattrs, usergrp_display,
644 return ADS_ERR_OK(rc) ? 0 : -1;
647 return net_run_function(argc, argv, func, net_ads_user_usage);
650 static int net_ads_group_usage(int argc, const char **argv)
652 return net_help_group(argc, argv);
655 static int ads_group_add(int argc, const char **argv)
659 LDAPMessage *res=NULL;
664 return net_ads_group_usage(argc, argv);
667 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
671 status = ads_find_user_acct(ads, &res, argv[0]);
673 if (!ADS_ERR_OK(status)) {
674 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
678 if (ads_count_replies(ads, res)) {
679 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
684 ou_str = SMB_STRDUP(opt_container);
686 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
689 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
691 if (ADS_ERR_OK(status)) {
692 d_printf("Group %s added\n", argv[0]);
695 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
701 ads_msgfree(ads, res);
707 static int ads_group_delete(int argc, const char **argv)
711 LDAPMessage *res = NULL;
715 return net_ads_group_usage(argc, argv);
718 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
722 rc = ads_find_user_acct(ads, &res, argv[0]);
723 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
724 d_printf("Group %s does not exist.\n", argv[0]);
725 ads_msgfree(ads, res);
729 groupdn = ads_get_dn(ads, res);
730 ads_msgfree(ads, res);
731 rc = ads_del_dn(ads, groupdn);
732 ads_memfree(ads, groupdn);
733 if (ADS_ERR_OK(rc)) {
734 d_printf("Group %s deleted\n", argv[0]);
738 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
744 int net_ads_group(int argc, const char **argv)
746 struct functable func[] = {
747 {"ADD", ads_group_add},
748 {"DELETE", ads_group_delete},
753 const char *shortattrs[] = {"sAMAccountName", NULL};
754 const char *longattrs[] = {"sAMAccountName", "description", NULL};
755 char *disp_fields[2] = {NULL, NULL};
758 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
762 if (opt_long_list_entries)
763 d_printf("\nGroup name Comment"\
764 "\n-----------------------------\n");
765 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
767 "(objectCategory=group)",
768 opt_long_list_entries ? longattrs :
769 shortattrs, usergrp_display,
773 return ADS_ERR_OK(rc) ? 0 : -1;
775 return net_run_function(argc, argv, func, net_ads_group_usage);
778 static int net_ads_status(int argc, const char **argv)
784 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
788 rc = ads_find_machine_acct(ads, &res, global_myname());
789 if (!ADS_ERR_OK(rc)) {
790 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
795 if (ads_count_replies(ads, res) == 0) {
796 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
806 /*******************************************************************
807 Leave an AD domain. Windows XP disables the machine account.
808 We'll try the same. The old code would do an LDAP delete.
809 That only worked using the machine creds because added the machine
810 with full control to the computer object's ACL.
811 *******************************************************************/
813 static int net_ads_leave(int argc, const char **argv)
816 struct libnet_UnjoinCtx *r = NULL;
819 if (!(ctx = talloc_init("net_ads_leave"))) {
820 d_fprintf(stderr, "Could not initialise talloc context.\n");
824 use_in_memory_ccache();
826 werr = libnet_init_UnjoinCtx(ctx, &r);
827 if (!W_ERROR_IS_OK(werr)) {
828 d_fprintf(stderr, "Could not initialise unjoin context.\n");
832 r->in.debug = opt_verbose;
833 r->in.dc_name = opt_host;
834 r->in.domain_name = lp_realm();
835 r->in.admin_account = opt_user_name;
836 r->in.admin_password = net_prompt_pass(opt_user_name);
837 r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
838 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
840 werr = libnet_Unjoin(ctx, r);
841 if (!W_ERROR_IS_OK(werr)) {
842 d_printf("%s: %s\n", get_friendly_werror_msg(werr),
843 r->out.error_string ? r->out.error_string : "");
847 if (W_ERROR_IS_OK(werr)) {
848 d_printf("Deleted account for '%s' in realm '%s'\n",
849 r->in.machine_name, r->out.dns_domain_name);
853 /* We couldn't delete it - see if the disable succeeded. */
854 if (r->out.disabled_machine_account) {
855 d_printf("Disabled account for '%s' in realm '%s'\n",
856 r->in.machine_name, r->out.dns_domain_name);
861 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
862 r->in.machine_name, r->out.dns_domain_name);
868 if (W_ERROR_IS_OK(werr)) {
875 static NTSTATUS net_ads_join_ok(void)
877 ADS_STRUCT *ads = NULL;
880 if (!secrets_init()) {
881 DEBUG(1,("Failed to initialise secrets database\n"));
882 return NT_STATUS_ACCESS_DENIED;
885 net_use_krb_machine_account();
887 status = ads_startup(True, &ads);
888 if (!ADS_ERR_OK(status)) {
889 return ads_ntstatus(status);
897 check that an existing join is OK
899 int net_ads_testjoin(int argc, const char **argv)
902 use_in_memory_ccache();
904 /* Display success or failure */
905 status = net_ads_join_ok();
906 if (!NT_STATUS_IS_OK(status)) {
907 fprintf(stderr,"Join to domain is not valid: %s\n",
908 get_friendly_nt_error_msg(status));
912 printf("Join is OK\n");
916 /*******************************************************************
917 Simple configu checks before beginning the join
918 ********************************************************************/
920 static NTSTATUS check_ads_config( void )
922 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
923 d_printf("Host is not configured as a member server.\n");
924 return NT_STATUS_INVALID_DOMAIN_ROLE;
927 if (strlen(global_myname()) > 15) {
928 d_printf("Our netbios name can be at most 15 chars long, "
929 "\"%s\" is %u chars long\n", global_myname(),
930 (unsigned int)strlen(global_myname()));
931 return NT_STATUS_NAME_TOO_LONG;
934 if ( lp_security() == SEC_ADS && !*lp_realm()) {
935 d_fprintf(stderr, "realm must be set in in %s for ADS "
936 "join to succeed.\n", get_dyn_CONFIGFILE());
937 return NT_STATUS_INVALID_PARAMETER;
940 if (!secrets_init()) {
941 DEBUG(1,("Failed to initialise secrets database\n"));
942 /* This is a good bet for failure of secrets_init ... */
943 return NT_STATUS_ACCESS_DENIED;
949 /*******************************************************************
951 ********************************************************************/
953 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
954 struct sockaddr_storage *pss,
957 const char *password)
959 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
960 struct cli_state *cli = NULL;
962 ret = connect_to_ipc_krb5(&cli, pss, servername);
963 if ( !NT_STATUS_IS_OK(ret) ) {
967 ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
968 if ( !NT_STATUS_IS_OK(ret) ) {
972 /* cli->server_domain is not filled in when using krb5
975 saf_store( *domain, cli->desthost );
977 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
986 /*******************************************************************
987 Set a machines dNSHostName and servicePrincipalName attributes
988 ********************************************************************/
990 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
992 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
995 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
998 LDAPMessage *res = NULL;
999 char *dn_string = NULL;
1000 const char *machine_name = global_myname();
1003 if ( !machine_name ) {
1004 return ADS_ERROR(LDAP_NO_MEMORY);
1009 status = ads_find_machine_acct(ads_s, &res, machine_name);
1010 if (!ADS_ERR_OK(status))
1013 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1014 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1015 return ADS_ERROR(LDAP_NO_MEMORY);
1018 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1019 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1023 new_dn = talloc_strdup(ctx, dn_string);
1024 ads_memfree(ads_s, dn_string);
1026 return ADS_ERROR(LDAP_NO_MEMORY);
1029 /* Windows only creates HOST/shortname & HOST/fqdn. */
1031 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1034 servicePrincipalName[0] = psp;
1036 name_to_fqdn(my_fqdn, machine_name);
1037 strlower_m(my_fqdn);
1038 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1040 servicePrincipalName[1] = psp;
1042 if (!(mods = ads_init_mods(ctx))) {
1046 /* fields of primary importance */
1048 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1049 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1051 status = ads_gen_mod(ads_s, new_dn, mods);
1054 ads_msgfree(ads_s, res);
1059 /*******************************************************************
1060 Set a machines dNSHostName and servicePrincipalName attributes
1061 ********************************************************************/
1063 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1065 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1068 LDAPMessage *res = NULL;
1069 char *dn_string = NULL;
1070 const char *machine_name = global_myname();
1073 if ( !machine_name ) {
1074 return ADS_ERROR(LDAP_NO_MEMORY);
1079 status = ads_find_machine_acct(ads_s, &res, machine_name);
1080 if (!ADS_ERR_OK(status))
1083 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1084 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1085 return ADS_ERROR(LDAP_NO_MEMORY);
1088 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1089 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1093 new_dn = talloc_strdup(ctx, dn_string);
1094 ads_memfree(ads_s, dn_string);
1096 return ADS_ERROR(LDAP_NO_MEMORY);
1099 /* now do the mods */
1101 if (!(mods = ads_init_mods(ctx))) {
1105 /* fields of primary importance */
1107 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1109 status = ads_gen_mod(ads_s, new_dn, mods);
1112 ads_msgfree(ads_s, res);
1117 /*******************************************************************
1118 Set a machines dNSHostName and servicePrincipalName attributes
1119 ********************************************************************/
1121 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1122 const char *os_name, const char *os_version )
1124 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1127 LDAPMessage *res = NULL;
1128 char *dn_string = NULL;
1129 const char *machine_name = global_myname();
1133 if ( !os_name || !os_version ) {
1134 return ADS_ERROR(LDAP_NO_MEMORY);
1139 status = ads_find_machine_acct(ads_s, &res, machine_name);
1140 if (!ADS_ERR_OK(status))
1143 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1144 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1145 return ADS_ERROR(LDAP_NO_MEMORY);
1148 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1149 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1153 new_dn = talloc_strdup(ctx, dn_string);
1154 ads_memfree(ads_s, dn_string);
1156 return ADS_ERROR(LDAP_NO_MEMORY);
1159 /* now do the mods */
1161 if (!(mods = ads_init_mods(ctx))) {
1165 os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1167 /* fields of primary importance */
1169 ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1170 ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1172 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1174 status = ads_gen_mod(ads_s, new_dn, mods);
1177 ads_msgfree(ads_s, res);
1178 TALLOC_FREE( os_sp );
1183 /*******************************************************************
1184 join a domain using ADS (LDAP mods)
1185 ********************************************************************/
1187 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1189 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1190 char *ou_str = NULL;
1192 LDAPMessage *res = NULL;
1195 ou_str = ads_ou_string(ads, ou);
1196 if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1197 rc = ADS_ERROR(LDAP_NO_MEMORY);
1201 rc = ads_search_dn(ads, &res, dn, NULL);
1202 if (!ADS_ERR_OK(rc)) {
1203 d_fprintf(stderr, "The specified OU does not exist.\n");
1207 /* Attempt to create the machine account and bail if this fails.
1208 Assume that the admin wants exactly what they requested */
1210 rc = ads_create_machine_acct( ads, global_myname(), dn );
1211 if (ADS_ERR_OK(rc)) {
1212 DEBUG(1, ("machine account created\n"));
1215 if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1216 DEBUG(1, ("machine account creation failed\n"));
1220 rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1221 if (!ADS_ERR_OK(rc)) {
1222 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1227 d_printf("The machine account was moved into the specified OU.\n");
1229 d_printf("The machine account already exists in the specified OU.\n");
1233 ads_msgfree(ads, res);
1234 SAFE_FREE( ou_str );
1240 /************************************************************************
1241 ************************************************************************/
1243 static bool net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1249 const char *machine_name = global_myname();
1251 status = ads_domain_func_level( ads, &domain_func );
1252 if ( !ADS_ERR_OK(status) ) {
1253 DEBUG(2,("Failed to determine domain functional level!\n"));
1257 /* go ahead and setup the default salt */
1259 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1260 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1264 fstrcpy( salt, std_salt );
1265 SAFE_FREE( std_salt );
1267 /* if it's a Windows functional domain, we have to look for the UPN */
1269 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1270 char *upn = ads_get_upn(ads, ctx, machine_name);
1272 fstrcpy( salt, upn );
1276 return kerberos_secrets_store_des_salt( salt );
1279 /*******************************************************************
1280 Send a DNS update request
1281 *******************************************************************/
1283 #if defined(WITH_DNS_UPDATES)
1285 DNS_ERROR DoDNSUpdate(char *pszServerName,
1286 const char *pszDomainName, const char *pszHostName,
1287 const struct sockaddr_storage *sslist,
1290 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1291 const char *machine_name,
1292 const struct sockaddr_storage *addrs,
1295 struct dns_rr_ns *nameservers = NULL;
1297 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1300 const char *dnsdomain = NULL;
1301 char *root_domain = NULL;
1303 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1304 d_printf("No DNS domain configured for %s. "
1305 "Unable to perform DNS Update.\n", machine_name);
1306 status = NT_STATUS_INVALID_PARAMETER;
1311 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1312 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1313 /* Child domains often do not have NS records. Look
1314 for the NS record for the forest root domain
1315 (rootDomainNamingContext in therootDSE) */
1317 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1318 LDAPMessage *msg = NULL;
1320 ADS_STATUS ads_status;
1322 if ( !ads->ldap.ld ) {
1323 ads_status = ads_connect( ads );
1324 if ( !ADS_ERR_OK(ads_status) ) {
1325 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1330 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1331 "(objectclass=*)", rootname_attrs, &msg);
1332 if (!ADS_ERR_OK(ads_status)) {
1336 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1338 ads_msgfree( ads, msg );
1342 root_domain = ads_build_domain( root_dn );
1345 ads_msgfree( ads, msg );
1347 /* try again for NS servers */
1349 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1351 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1352 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1353 "realm\n", ads->config.realm));
1357 dnsdomain = root_domain;
1361 /* Now perform the dns update - we'll try non-secure and if we fail,
1362 we'll follow it up with a secure update */
1364 fstrcpy( dns_server, nameservers[0].hostname );
1366 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1367 if (!ERR_DNS_IS_OK(dns_err)) {
1368 status = NT_STATUS_UNSUCCESSFUL;
1373 SAFE_FREE( root_domain );
1378 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1381 struct sockaddr_storage *iplist = NULL;
1382 fstring machine_name;
1385 name_to_fqdn( machine_name, global_myname() );
1386 strlower_m( machine_name );
1388 /* Get our ip address (not the 127.0.0.x address but a real ip
1391 num_addrs = get_my_ip_address( &iplist );
1392 if ( num_addrs <= 0 ) {
1393 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1395 return NT_STATUS_INVALID_PARAMETER;
1398 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1400 SAFE_FREE( iplist );
1406 /*******************************************************************
1407 ********************************************************************/
1409 static int net_ads_join_usage(int argc, const char **argv)
1411 d_printf("net ads join [options]\n");
1412 d_printf("Valid options:\n");
1413 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1414 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1415 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1416 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1417 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1418 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1419 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1420 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1421 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1422 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1423 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1424 d_printf(" the two other attributes.\n");
1429 /*******************************************************************
1430 ********************************************************************/
1432 int net_ads_join(int argc, const char **argv)
1434 ADS_STRUCT *ads = NULL;
1437 const char *short_domain_name = NULL;
1438 char *tmp_password, *password;
1439 TALLOC_CTX *ctx = NULL;
1440 DOM_SID *domain_sid = NULL;
1441 bool createupn = False;
1442 const char *machineupn = NULL;
1443 const char *create_in_ou = NULL;
1446 struct sockaddr_storage dcss;
1447 const char *os_name = NULL;
1448 const char *os_version = NULL;
1450 nt_status = check_ads_config();
1451 if (!NT_STATUS_IS_OK(nt_status)) {
1452 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1456 /* find a DC to initialize the server affinity cache */
1458 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcss );
1460 status = ads_startup(True, &ads);
1461 if (!ADS_ERR_OK(status)) {
1462 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1463 nt_status = ads_ntstatus(status);
1467 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1468 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1469 "(%s) DO NOT match. Aborting join\n",
1470 ads->config.realm, get_dyn_CONFIGFILE(), lp_realm());
1471 nt_status = NT_STATUS_INVALID_PARAMETER;
1475 if (!(ctx = talloc_init("net_ads_join"))) {
1476 d_fprintf(stderr, "Could not initialise talloc context.\n");
1477 nt_status = NT_STATUS_NO_MEMORY;
1481 /* process additional command line args */
1483 for ( i=0; i<argc; i++ ) {
1484 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1486 machineupn = get_string_param(argv[i]);
1488 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1489 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1490 d_fprintf(stderr, "Please supply a valid OU path.\n");
1491 nt_status = NT_STATUS_INVALID_PARAMETER;
1495 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1496 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1497 d_fprintf(stderr, "Please supply a operating system name.\n");
1498 nt_status = NT_STATUS_INVALID_PARAMETER;
1502 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1503 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1504 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1505 nt_status = NT_STATUS_INVALID_PARAMETER;
1510 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1511 nt_status = NT_STATUS_INVALID_PARAMETER;
1516 /* If we were given an OU, try to create the machine in
1517 the OU account first and then do the normal RPC join */
1519 if ( create_in_ou ) {
1520 status = net_precreate_machine_acct( ads, create_in_ou );
1521 if ( !ADS_ERR_OK(status) ) {
1522 d_fprintf( stderr, "Failed to pre-create the machine object "
1523 "in OU %s.\n", create_in_ou);
1524 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1525 ads_errstr(status)));
1526 nt_status = ads_ntstatus(status);
1531 /* Do the domain join here */
1533 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1534 password = talloc_strdup(ctx, tmp_password);
1536 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1537 &ads->ldap.ss, &short_domain_name, &domain_sid, password);
1538 if ( !NT_STATUS_IS_OK(nt_status) ) {
1539 DEBUG(1, ("call of net_join_domain failed: %s\n",
1540 get_friendly_nt_error_msg(nt_status)));
1544 /* Check the short name of the domain */
1546 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1547 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1548 d_printf("domain name obtained from the server.\n");
1549 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1550 d_printf("You should set \"workgroup = %s\" in %s.\n",
1551 short_domain_name, get_dyn_CONFIGFILE());
1554 d_printf("Using short domain name -- %s\n", short_domain_name);
1556 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1557 value from smb.conf and the string returned from the server. The former is
1558 neede to bootstrap winbindd's first connection to the DC to get the real
1559 short domain name --jerry */
1561 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1562 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1564 /* issue an internal error here for now.
1565 * everything else would mean changing tdb routines. */
1566 nt_status = NT_STATUS_INTERNAL_ERROR;
1570 /* Verify that everything is ok */
1572 nt_status = net_rpc_join_ok(short_domain_name,
1573 ads->config.ldap_server_name, &ads->ldap.ss);
1574 if (!NT_STATUS_IS_OK(nt_status)) {
1576 "Failed to verify membership in domain: %s!\n",
1577 nt_errstr(nt_status));
1581 /* create the dNSHostName & servicePrincipalName values */
1583 status = net_set_machine_spn( ctx, ads );
1584 if ( !ADS_ERR_OK(status) ) {
1586 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1587 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1588 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1590 /* Disable the machine account in AD. Better to fail than to leave
1591 a confused admin. */
1593 if ( net_ads_leave( 0, NULL ) != 0 ) {
1594 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1597 /* clear out the machine password */
1599 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1600 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1602 nt_status = ads_ntstatus(status);
1606 if ( !net_derive_salting_principal( ctx, ads ) ) {
1607 DEBUG(1,("Failed to determine salting principal\n"));
1614 /* default to using the short UPN name */
1616 upn = talloc_asprintf(ctx,
1617 "host/%s@%s", global_myname(),
1618 ads->config.realm );
1620 nt_status = NT_STATUS_NO_MEMORY;
1626 status = net_set_machine_upn( ctx, ads, machineupn );
1627 if ( !ADS_ERR_OK(status) ) {
1628 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1632 /* Try to set the operatingSystem attributes if asked */
1634 if ( os_name && os_version ) {
1635 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1636 if ( !ADS_ERR_OK(status) ) {
1637 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1638 "Are you a Domain Admin?\n");
1642 /* Now build the keytab, using the same ADS connection */
1644 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1645 DEBUG(1,("Error creating host keytab!\n"));
1648 #if defined(WITH_DNS_UPDATES)
1649 /* We enter this block with user creds */
1650 ads_kdestroy( NULL );
1654 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1655 /* kinit with the machine password */
1657 use_in_memory_ccache();
1658 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1659 ads->auth.password = secrets_fetch_machine_password(
1660 lp_workgroup(), NULL, NULL );
1661 ads->auth.realm = SMB_STRDUP( lp_realm() );
1662 ads_kinit_password( ads );
1665 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1666 d_fprintf( stderr, "DNS update failed!\n" );
1669 /* exit from this block using machine creds */
1672 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1680 /* issue an overall failure message at the end. */
1681 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1690 /*******************************************************************
1691 ********************************************************************/
1693 static int net_ads_dns_usage(int argc, const char **argv)
1695 #if defined(WITH_DNS_UPDATES)
1696 d_printf("net ads dns <command>\n");
1697 d_printf("Valid commands:\n");
1698 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1702 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1707 /*******************************************************************
1708 ********************************************************************/
1710 static int net_ads_dns_register(int argc, const char **argv)
1712 #if defined(WITH_DNS_UPDATES)
1718 talloc_enable_leak_report();
1722 d_fprintf(stderr, "net ads dns register\n");
1726 if (!(ctx = talloc_init("net_ads_dns"))) {
1727 d_fprintf(stderr, "Could not initialise talloc context\n");
1731 status = ads_startup(True, &ads);
1732 if ( !ADS_ERR_OK(status) ) {
1733 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1738 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1739 d_fprintf( stderr, "DNS update failed!\n" );
1740 ads_destroy( &ads );
1745 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1752 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1757 #if defined(WITH_DNS_UPDATES)
1758 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1761 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1763 #if defined(WITH_DNS_UPDATES)
1767 talloc_enable_leak_report();
1771 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1776 err = do_gethostbyname(argv[0], argv[1]);
1778 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1783 static int net_ads_dns(int argc, const char *argv[])
1785 struct functable func[] = {
1786 {"REGISTER", net_ads_dns_register},
1787 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1791 return net_run_function(argc, argv, func, net_ads_dns_usage);
1794 /*******************************************************************
1795 ********************************************************************/
1797 int net_ads_printer_usage(int argc, const char **argv)
1800 "\nnet ads printer search <printer>"
1801 "\n\tsearch for a printer in the directory\n"
1802 "\nnet ads printer info <printer> <server>"
1803 "\n\tlookup info in directory for printer on server"
1804 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1805 "\nnet ads printer publish <printername>"
1806 "\n\tpublish printer in directory"
1807 "\n\t(note: printer name is required)\n"
1808 "\nnet ads printer remove <printername>"
1809 "\n\tremove printer from directory"
1810 "\n\t(note: printer name is required)\n");
1814 /*******************************************************************
1815 ********************************************************************/
1817 static int net_ads_printer_search(int argc, const char **argv)
1821 LDAPMessage *res = NULL;
1823 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1827 rc = ads_find_printers(ads, &res);
1829 if (!ADS_ERR_OK(rc)) {
1830 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1831 ads_msgfree(ads, res);
1836 if (ads_count_replies(ads, res) == 0) {
1837 d_fprintf(stderr, "No results found\n");
1838 ads_msgfree(ads, res);
1844 ads_msgfree(ads, res);
1849 static int net_ads_printer_info(int argc, const char **argv)
1853 const char *servername, *printername;
1854 LDAPMessage *res = NULL;
1856 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1861 printername = argv[0];
1867 servername = argv[1];
1869 servername = global_myname();
1872 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1874 if (!ADS_ERR_OK(rc)) {
1875 d_fprintf(stderr, "Server '%s' not found: %s\n",
1876 servername, ads_errstr(rc));
1877 ads_msgfree(ads, res);
1882 if (ads_count_replies(ads, res) == 0) {
1883 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1884 ads_msgfree(ads, res);
1890 ads_msgfree(ads, res);
1896 static int net_ads_printer_publish(int argc, const char **argv)
1900 const char *servername, *printername;
1901 struct cli_state *cli;
1902 struct rpc_pipe_client *pipe_hnd;
1903 struct sockaddr_storage server_ss;
1905 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1906 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1907 char *prt_dn, *srv_dn, **srv_cn;
1908 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1909 LDAPMessage *res = NULL;
1911 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1912 talloc_destroy(mem_ctx);
1917 talloc_destroy(mem_ctx);
1918 return net_ads_printer_usage(argc, argv);
1921 printername = argv[0];
1924 servername = argv[1];
1926 servername = global_myname();
1929 /* Get printer data from SPOOLSS */
1931 resolve_name(servername, &server_ss, 0x20);
1933 nt_status = cli_full_connection(&cli, global_myname(), servername,
1936 opt_user_name, opt_workgroup,
1937 opt_password ? opt_password : "",
1938 CLI_FULL_CONNECTION_USE_KERBEROS,
1941 if (NT_STATUS_IS_ERR(nt_status)) {
1942 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1943 "for %s\n", servername, printername);
1945 talloc_destroy(mem_ctx);
1949 /* Publish on AD server */
1951 ads_find_machine_acct(ads, &res, servername);
1953 if (ads_count_replies(ads, res) == 0) {
1954 d_fprintf(stderr, "Could not find machine account for server %s\n",
1957 talloc_destroy(mem_ctx);
1961 srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1962 srv_cn = ldap_explode_dn(srv_dn, 1);
1964 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1965 printername_escaped = escape_rdn_val_string_alloc(printername);
1966 if (!srv_cn_escaped || !printername_escaped) {
1967 SAFE_FREE(srv_cn_escaped);
1968 SAFE_FREE(printername_escaped);
1969 d_fprintf(stderr, "Internal error, out of memory!");
1971 talloc_destroy(mem_ctx);
1975 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1977 SAFE_FREE(srv_cn_escaped);
1978 SAFE_FREE(printername_escaped);
1980 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1982 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1986 talloc_destroy(mem_ctx);
1990 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1994 talloc_destroy(mem_ctx);
1998 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1999 if (!ADS_ERR_OK(rc)) {
2000 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2003 talloc_destroy(mem_ctx);
2007 d_printf("published printer\n");
2010 talloc_destroy(mem_ctx);
2015 static int net_ads_printer_remove(int argc, const char **argv)
2019 const char *servername;
2021 LDAPMessage *res = NULL;
2023 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2028 return net_ads_printer_usage(argc, argv);
2032 servername = argv[1];
2034 servername = global_myname();
2037 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2039 if (!ADS_ERR_OK(rc)) {
2040 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2041 ads_msgfree(ads, res);
2046 if (ads_count_replies(ads, res) == 0) {
2047 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2048 ads_msgfree(ads, res);
2053 prt_dn = ads_get_dn(ads, res);
2054 ads_msgfree(ads, res);
2055 rc = ads_del_dn(ads, prt_dn);
2056 ads_memfree(ads, prt_dn);
2058 if (!ADS_ERR_OK(rc)) {
2059 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2068 static int net_ads_printer(int argc, const char **argv)
2070 struct functable func[] = {
2071 {"SEARCH", net_ads_printer_search},
2072 {"INFO", net_ads_printer_info},
2073 {"PUBLISH", net_ads_printer_publish},
2074 {"REMOVE", net_ads_printer_remove},
2078 return net_run_function(argc, argv, func, net_ads_printer_usage);
2082 static int net_ads_password(int argc, const char **argv)
2085 const char *auth_principal = opt_user_name;
2086 const char *auth_password = opt_password;
2088 char *new_password = NULL;
2093 if (opt_user_name == NULL || opt_password == NULL) {
2094 d_fprintf(stderr, "You must supply an administrator username/password\n");
2099 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2104 if (!strchr_m(user, '@')) {
2105 asprintf(&c, "%s@%s", argv[0], lp_realm());
2109 use_in_memory_ccache();
2110 c = strchr_m(auth_principal, '@');
2117 /* use the realm so we can eventually change passwords for users
2118 in realms other than default */
2119 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2123 /* we don't actually need a full connect, but it's the easy way to
2124 fill in the KDC's addresss */
2127 if (!ads || !ads->config.realm) {
2128 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2133 new_password = (char *)argv[1];
2135 asprintf(&prompt, "Enter new password for %s:", user);
2136 new_password = getpass(prompt);
2140 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2141 auth_password, user, new_password, ads->auth.time_offset);
2142 if (!ADS_ERR_OK(ret)) {
2143 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2148 d_printf("Password change for %s completed.\n", user);
2154 int net_ads_changetrustpw(int argc, const char **argv)
2157 char *host_principal;
2161 if (!secrets_init()) {
2162 DEBUG(1,("Failed to initialise secrets database\n"));
2166 net_use_krb_machine_account();
2168 use_in_memory_ccache();
2170 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2174 fstrcpy(my_name, global_myname());
2175 strlower_m(my_name);
2176 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2177 d_printf("Changing password for principal: %s\n", host_principal);
2179 ret = ads_change_trust_account_password(ads, host_principal);
2181 if (!ADS_ERR_OK(ret)) {
2182 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2184 SAFE_FREE(host_principal);
2188 d_printf("Password change for principal %s succeeded.\n", host_principal);
2190 if (lp_use_kerberos_keytab()) {
2191 d_printf("Attempting to update system keytab with new password.\n");
2192 if (ads_keytab_create_default(ads)) {
2193 d_printf("Failed to update system keytab.\n");
2198 SAFE_FREE(host_principal);
2204 help for net ads search
2206 static int net_ads_search_usage(int argc, const char **argv)
2209 "\nnet ads search <expression> <attributes...>\n"\
2210 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2211 "The expression is a standard LDAP search expression, and the\n"\
2212 "attributes are a list of LDAP fields to show in the results\n\n"\
2213 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2215 net_common_flags_usage(argc, argv);
2221 general ADS search function. Useful in diagnosing problems in ADS
2223 static int net_ads_search(int argc, const char **argv)
2227 const char *ldap_exp;
2229 LDAPMessage *res = NULL;
2232 return net_ads_search_usage(argc, argv);
2235 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2242 rc = ads_do_search_all(ads, ads->config.bind_path,
2244 ldap_exp, attrs, &res);
2245 if (!ADS_ERR_OK(rc)) {
2246 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2251 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2253 /* dump the results */
2256 ads_msgfree(ads, res);
2264 help for net ads search
2266 static int net_ads_dn_usage(int argc, const char **argv)
2269 "\nnet ads dn <dn> <attributes...>\n"\
2270 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2271 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2272 "to show in the results\n\n"\
2273 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2274 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2276 net_common_flags_usage(argc, argv);
2282 general ADS search function. Useful in diagnosing problems in ADS
2284 static int net_ads_dn(int argc, const char **argv)
2290 LDAPMessage *res = NULL;
2293 return net_ads_dn_usage(argc, argv);
2296 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2303 rc = ads_do_search_all(ads, dn,
2305 "(objectclass=*)", attrs, &res);
2306 if (!ADS_ERR_OK(rc)) {
2307 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2312 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2314 /* dump the results */
2317 ads_msgfree(ads, res);
2324 help for net ads sid search
2326 static int net_ads_sid_usage(int argc, const char **argv)
2329 "\nnet ads sid <sid> <attributes...>\n"\
2330 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2331 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2332 "to show in the results\n\n"\
2333 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2335 net_common_flags_usage(argc, argv);
2341 general ADS search function. Useful in diagnosing problems in ADS
2343 static int net_ads_sid(int argc, const char **argv)
2347 const char *sid_string;
2349 LDAPMessage *res = NULL;
2353 return net_ads_sid_usage(argc, argv);
2356 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2360 sid_string = argv[0];
2363 if (!string_to_sid(&sid, sid_string)) {
2364 d_fprintf(stderr, "could not convert sid\n");
2369 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2370 if (!ADS_ERR_OK(rc)) {
2371 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2376 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2378 /* dump the results */
2381 ads_msgfree(ads, res);
2388 static int net_ads_keytab_usage(int argc, const char **argv)
2391 "net ads keytab <COMMAND>\n"\
2392 "<COMMAND> can be either:\n"\
2393 " ADD Adds new service principal\n"\
2394 " CREATE Creates a fresh keytab\n"\
2395 " FLUSH Flushes out all keytab entries\n"\
2396 " HELP Prints this help message\n"\
2397 " LIST List the keytab\n"\
2398 "The ADD and LIST command will take arguments, the other commands\n"\
2399 "will not take any arguments. The arguments given to ADD\n"\
2400 "should be a list of principals to add. For example, \n"\
2401 " net ads keytab add srv1 srv2\n"\
2402 "will add principals for the services srv1 and srv2 to the\n"\
2403 "system's keytab.\n"\
2404 "The LIST command takes a keytabname.\n"\
2410 static int net_ads_keytab_flush(int argc, const char **argv)
2415 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2418 ret = ads_keytab_flush(ads);
2423 static int net_ads_keytab_add(int argc, const char **argv)
2429 d_printf("Processing principals to add...\n");
2430 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2433 for (i = 0; i < argc; i++) {
2434 ret |= ads_keytab_add_entry(ads, argv[i]);
2440 static int net_ads_keytab_create(int argc, const char **argv)
2445 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2448 ret = ads_keytab_create_default(ads);
2453 static int net_ads_keytab_list(int argc, const char **argv)
2455 const char *keytab = NULL;
2461 return ads_keytab_list(keytab);
2465 int net_ads_keytab(int argc, const char **argv)
2467 struct functable func[] = {
2468 {"ADD", net_ads_keytab_add},
2469 {"CREATE", net_ads_keytab_create},
2470 {"FLUSH", net_ads_keytab_flush},
2471 {"HELP", net_ads_keytab_usage},
2472 {"LIST", net_ads_keytab_list},
2476 if (!lp_use_kerberos_keytab()) {
2477 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2478 use keytab functions.\n");
2481 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2484 static int net_ads_kerberos_usage(int argc, const char **argv)
2487 "net ads kerberos <COMMAND>\n"\
2488 "<COMMAND> can be either:\n"\
2489 " RENEW Renew TGT from existing credential cache\n"\
2490 " PAC Dumps the Kerberos PAC\n"\
2491 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2498 static int net_ads_kerberos_renew(int argc, const char **argv)
2500 int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2502 d_printf("failed to renew kerberos ticket: %s\n",
2503 error_message(ret));
2508 static int net_ads_kerberos_pac(int argc, const char **argv)
2510 struct PAC_DATA *pac = NULL;
2511 struct PAC_LOGON_INFO *info = NULL;
2512 TALLOC_CTX *mem_ctx = NULL;
2516 mem_ctx = talloc_init("net_ads_kerberos_pac");
2521 opt_password = net_prompt_pass(opt_user_name);
2523 status = kerberos_return_pac(mem_ctx,
2532 2592000, /* one month */
2534 if (!NT_STATUS_IS_OK(status)) {
2535 d_printf("failed to query kerberos PAC: %s\n",
2540 info = get_logon_info_from_pac(pac);
2543 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
2544 d_printf("The Pac: %s\n", s);
2549 TALLOC_FREE(mem_ctx);
2553 static int net_ads_kerberos_kinit(int argc, const char **argv)
2555 TALLOC_CTX *mem_ctx = NULL;
2559 mem_ctx = talloc_init("net_ads_kerberos_kinit");
2564 opt_password = net_prompt_pass(opt_user_name);
2566 ret = kerberos_kinit_password_ext(opt_user_name,
2574 2592000, /* one month */
2577 d_printf("failed to kinit password: %s\n",
2584 int net_ads_kerberos(int argc, const char **argv)
2586 struct functable func[] = {
2587 {"KINIT", net_ads_kerberos_kinit},
2588 {"RENEW", net_ads_kerberos_renew},
2589 {"PAC", net_ads_kerberos_pac},
2590 {"HELP", net_ads_kerberos_usage},
2594 return net_run_function(argc, argv, func, net_ads_kerberos_usage);
2598 int net_ads_help(int argc, const char **argv)
2600 struct functable func[] = {
2601 {"USER", net_ads_user_usage},
2602 {"GROUP", net_ads_group_usage},
2603 {"PRINTER", net_ads_printer_usage},
2604 {"SEARCH", net_ads_search_usage},
2605 {"INFO", net_ads_info},
2606 {"JOIN", net_ads_join_usage},
2607 {"DNS", net_ads_dns_usage},
2608 {"LEAVE", net_ads_leave},
2609 {"STATUS", net_ads_status},
2610 {"PASSWORD", net_ads_password},
2611 {"CHANGETRUSTPW", net_ads_changetrustpw},
2615 return net_run_function(argc, argv, func, net_ads_usage);
2618 int net_ads(int argc, const char **argv)
2620 struct functable func[] = {
2621 {"INFO", net_ads_info},
2622 {"JOIN", net_ads_join},
2623 {"TESTJOIN", net_ads_testjoin},
2624 {"LEAVE", net_ads_leave},
2625 {"STATUS", net_ads_status},
2626 {"USER", net_ads_user},
2627 {"GROUP", net_ads_group},
2628 {"DNS", net_ads_dns},
2629 {"PASSWORD", net_ads_password},
2630 {"CHANGETRUSTPW", net_ads_changetrustpw},
2631 {"PRINTER", net_ads_printer},
2632 {"SEARCH", net_ads_search},
2634 {"SID", net_ads_sid},
2635 {"WORKGROUP", net_ads_workgroup},
2636 {"LOOKUP", net_ads_lookup},
2637 {"KEYTAB", net_ads_keytab},
2638 {"GPO", net_ads_gpo},
2639 {"KERBEROS", net_ads_kerberos},
2640 {"HELP", net_ads_help},
2644 return net_run_function(argc, argv, func, net_ads_usage);
2649 static int net_ads_noads(void)
2651 d_fprintf(stderr, "ADS support not compiled in\n");
2655 int net_ads_keytab(int argc, const char **argv)
2657 return net_ads_noads();
2660 int net_ads_kerberos(int argc, const char **argv)
2662 return net_ads_noads();
2665 int net_ads_usage(int argc, const char **argv)
2667 return net_ads_noads();
2670 int net_ads_help(int argc, const char **argv)
2672 return net_ads_noads();
2675 int net_ads_changetrustpw(int argc, const char **argv)
2677 return net_ads_noads();
2680 int net_ads_join(int argc, const char **argv)
2682 return net_ads_noads();
2685 int net_ads_user(int argc, const char **argv)
2687 return net_ads_noads();
2690 int net_ads_group(int argc, const char **argv)
2692 return net_ads_noads();
2695 /* this one shouldn't display a message */
2696 int net_ads_check(void)
2701 int net_ads_check_our_domain(void)
2706 int net_ads(int argc, const char **argv)
2708 return net_ads_usage(argc, argv);
2711 #endif /* WITH_ADS */