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 struct cldap_netlogon_reply reply;
83 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap.ip), ads->server.realm, &reply ) ) {
84 d_fprintf(stderr, "CLDAP query failed!\n");
88 d_printf("Information for Domain Controller: %s\n\n",
89 inet_ntoa(ads->ldap.ip));
91 d_printf("Response Type: ");
93 case SAMLOGON_AD_UNK_R:
94 d_printf("SAMLOGON\n");
97 d_printf("SAMLOGON_USER\n");
100 d_printf("0x%x\n", reply.type);
103 d_printf("GUID: %s\n",
104 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
107 "\tIs a GC of the forest: %s\n"
108 "\tIs an LDAP server: %s\n"
109 "\tSupports DS: %s\n"
110 "\tIs running a KDC: %s\n"
111 "\tIs running time services: %s\n"
112 "\tIs the closest DC: %s\n"
113 "\tIs writable: %s\n"
114 "\tHas a hardware clock: %s\n"
115 "\tIs a non-domain NC serviced by LDAP server: %s\n",
116 (reply.flags & ADS_PDC) ? "yes" : "no",
117 (reply.flags & ADS_GC) ? "yes" : "no",
118 (reply.flags & ADS_LDAP) ? "yes" : "no",
119 (reply.flags & ADS_DS) ? "yes" : "no",
120 (reply.flags & ADS_KDC) ? "yes" : "no",
121 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
122 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
123 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
124 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
125 (reply.flags & ADS_NDNC) ? "yes" : "no");
127 printf("Forest:\t\t\t%s\n", reply.forest);
128 printf("Domain:\t\t\t%s\n", reply.domain);
129 printf("Domain Controller:\t%s\n", reply.hostname);
131 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
132 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
134 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
135 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
137 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
138 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
140 d_printf("NT Version: %d\n", reply.version);
141 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
142 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
149 this implements the CLDAP based netlogon lookup requests
150 for finding the domain controller of a ADS domain
152 static int net_ads_lookup(int argc, const char **argv)
156 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
157 d_fprintf(stderr, "Didn't find the cldap server!\n");
161 if (!ads->config.realm) {
162 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
163 ads->ldap.port = 389;
166 return net_ads_cldap_netlogon(ads);
171 static int net_ads_info(int argc, const char **argv)
175 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
176 d_fprintf(stderr, "Didn't find the ldap server!\n");
180 if (!ads || !ads->config.realm) {
181 d_fprintf(stderr, "Didn't find the ldap server!\n");
185 /* Try to set the server's current time since we didn't do a full
186 TCP LDAP session initially */
188 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
189 d_fprintf( stderr, "Failed to get server's current time!\n");
192 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap.ip));
193 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
194 d_printf("Realm: %s\n", ads->config.realm);
195 d_printf("Bind Path: %s\n", ads->config.bind_path);
196 d_printf("LDAP port: %d\n", ads->ldap.port);
197 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
199 d_printf("KDC server: %s\n", ads->auth.kdc_server );
200 d_printf("Server time offset: %d\n", ads->auth.time_offset );
205 static void use_in_memory_ccache(void) {
206 /* Use in-memory credentials cache so we do not interfere with
207 * existing credentials */
208 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
211 static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
213 ADS_STRUCT *ads = NULL;
215 bool need_password = False;
216 bool second_time = False;
218 const char *realm = NULL;
219 bool tried_closest_dc = False;
221 /* lp_realm() should be handled by a command line param,
222 However, the join requires that realm be set in smb.conf
223 and compares our realm with the remote server's so this is
224 ok until someone needs more flexibility */
229 if (only_own_domain) {
232 realm = assume_own_realm();
235 ads = ads_init(realm, opt_target_workgroup, opt_host);
237 if (!opt_user_name) {
238 opt_user_name = "administrator";
241 if (opt_user_specified) {
242 need_password = True;
246 if (!opt_password && need_password && !opt_machine_pass) {
247 opt_password = net_prompt_pass(opt_user_name);
250 return ADS_ERROR(LDAP_NO_MEMORY);
255 use_in_memory_ccache();
256 SAFE_FREE(ads->auth.password);
257 ads->auth.password = smb_xstrdup(opt_password);
260 ads->auth.flags |= auth_flags;
261 SAFE_FREE(ads->auth.user_name);
262 ads->auth.user_name = smb_xstrdup(opt_user_name);
265 * If the username is of the form "name@realm",
266 * extract the realm and convert to upper case.
267 * This is only used to establish the connection.
269 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
271 SAFE_FREE(ads->auth.realm);
272 ads->auth.realm = smb_xstrdup(cp);
273 strupper_m(ads->auth.realm);
276 status = ads_connect(ads);
278 if (!ADS_ERR_OK(status)) {
280 if (NT_STATUS_EQUAL(ads_ntstatus(status),
281 NT_STATUS_NO_LOGON_SERVERS)) {
282 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
287 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
288 need_password = True;
297 /* when contacting our own domain, make sure we use the closest DC.
298 * This is done by reconnecting to ADS because only the first call to
299 * ads_connect will give us our own sitename */
301 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
303 tried_closest_dc = True; /* avoid loop */
305 if (!ads->config.tried_closest_dc) {
307 namecache_delete(ads->server.realm, 0x1C);
308 namecache_delete(ads->server.workgroup, 0x1C);
321 ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
323 return ads_startup_int(only_own_domain, 0, ads);
326 ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
328 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
332 Check to see if connection can be made via ads.
333 ads_startup() stores the password in opt_password if it needs to so
334 that rpc or rap can use it without re-prompting.
336 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
341 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
345 ads->auth.flags |= ADS_AUTH_NO_BIND;
347 status = ads_connect(ads);
348 if ( !ADS_ERR_OK(status) ) {
356 int net_ads_check_our_domain(void)
358 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
361 int net_ads_check(void)
363 return net_ads_check_int(NULL, opt_workgroup, opt_host);
367 determine the netbios workgroup name for a domain
369 static int net_ads_workgroup(int argc, const char **argv)
372 struct cldap_netlogon_reply reply;
374 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
375 d_fprintf(stderr, "Didn't find the cldap server!\n");
379 if (!ads->config.realm) {
380 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
381 ads->ldap.port = 389;
384 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap.ip), ads->server.realm, &reply ) ) {
385 d_fprintf(stderr, "CLDAP query failed!\n");
389 d_printf("Workgroup: %s\n", reply.netbios_domain);
398 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
400 char **disp_fields = (char **) data_area;
402 if (!field) { /* must be end of record */
403 if (disp_fields[0]) {
404 if (!strchr_m(disp_fields[0], '$')) {
406 d_printf("%-21.21s %s\n",
407 disp_fields[0], disp_fields[1]);
409 d_printf("%s\n", disp_fields[0]);
412 SAFE_FREE(disp_fields[0]);
413 SAFE_FREE(disp_fields[1]);
416 if (!values) /* must be new field, indicate string field */
418 if (StrCaseCmp(field, "sAMAccountName") == 0) {
419 disp_fields[0] = SMB_STRDUP((char *) values[0]);
421 if (StrCaseCmp(field, "description") == 0)
422 disp_fields[1] = SMB_STRDUP((char *) values[0]);
426 static int net_ads_user_usage(int argc, const char **argv)
428 return net_help_user(argc, argv);
431 static int ads_user_add(int argc, const char **argv)
436 LDAPMessage *res=NULL;
440 if (argc < 1) return net_ads_user_usage(argc, argv);
442 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
446 status = ads_find_user_acct(ads, &res, argv[0]);
448 if (!ADS_ERR_OK(status)) {
449 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
453 if (ads_count_replies(ads, res)) {
454 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
459 ou_str = SMB_STRDUP(opt_container);
461 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
464 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
466 if (!ADS_ERR_OK(status)) {
467 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
472 /* if no password is to be set, we're done */
474 d_printf("User %s added\n", argv[0]);
479 /* try setting the password */
480 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
481 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
482 ads->auth.time_offset);
484 if (ADS_ERR_OK(status)) {
485 d_printf("User %s added\n", argv[0]);
490 /* password didn't set, delete account */
491 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
492 argv[0], ads_errstr(status));
493 ads_msgfree(ads, res);
494 status=ads_find_user_acct(ads, &res, argv[0]);
495 if (ADS_ERR_OK(status)) {
496 userdn = ads_get_dn(ads, res);
497 ads_del_dn(ads, userdn);
498 ads_memfree(ads, userdn);
503 ads_msgfree(ads, res);
509 static int ads_user_info(int argc, const char **argv)
514 const char *attrs[] = {"memberOf", NULL};
515 char *searchstring=NULL;
520 return net_ads_user_usage(argc, argv);
523 escaped_user = escape_ldap_string_alloc(argv[0]);
526 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
530 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
531 SAFE_FREE(escaped_user);
535 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
536 rc = ads_search(ads, &res, searchstring, attrs);
537 safe_free(searchstring);
539 if (!ADS_ERR_OK(rc)) {
540 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
542 SAFE_FREE(escaped_user);
546 grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
547 (LDAPMessage *)res, "memberOf");
552 for (i=0;grouplist[i];i++) {
553 groupname = ldap_explode_dn(grouplist[i], 1);
554 d_printf("%s\n", groupname[0]);
555 ldap_value_free(groupname);
557 ldap_value_free(grouplist);
560 ads_msgfree(ads, res);
562 SAFE_FREE(escaped_user);
566 static int ads_user_delete(int argc, const char **argv)
570 LDAPMessage *res = NULL;
574 return net_ads_user_usage(argc, argv);
577 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
581 rc = ads_find_user_acct(ads, &res, argv[0]);
582 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
583 d_printf("User %s does not exist.\n", argv[0]);
584 ads_msgfree(ads, res);
588 userdn = ads_get_dn(ads, res);
589 ads_msgfree(ads, res);
590 rc = ads_del_dn(ads, userdn);
591 ads_memfree(ads, userdn);
592 if (ADS_ERR_OK(rc)) {
593 d_printf("User %s deleted\n", argv[0]);
597 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
603 int net_ads_user(int argc, const char **argv)
605 struct functable func[] = {
606 {"ADD", ads_user_add},
607 {"INFO", ads_user_info},
608 {"DELETE", ads_user_delete},
613 const char *shortattrs[] = {"sAMAccountName", NULL};
614 const char *longattrs[] = {"sAMAccountName", "description", NULL};
615 char *disp_fields[2] = {NULL, NULL};
618 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
622 if (opt_long_list_entries)
623 d_printf("\nUser name Comment"\
624 "\n-----------------------------\n");
626 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
628 "(objectCategory=user)",
629 opt_long_list_entries ? longattrs :
630 shortattrs, usergrp_display,
633 return ADS_ERR_OK(rc) ? 0 : -1;
636 return net_run_function(argc, argv, func, net_ads_user_usage);
639 static int net_ads_group_usage(int argc, const char **argv)
641 return net_help_group(argc, argv);
644 static int ads_group_add(int argc, const char **argv)
648 LDAPMessage *res=NULL;
653 return net_ads_group_usage(argc, argv);
656 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
660 status = ads_find_user_acct(ads, &res, argv[0]);
662 if (!ADS_ERR_OK(status)) {
663 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
667 if (ads_count_replies(ads, res)) {
668 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
673 ou_str = SMB_STRDUP(opt_container);
675 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
678 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
680 if (ADS_ERR_OK(status)) {
681 d_printf("Group %s added\n", argv[0]);
684 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
690 ads_msgfree(ads, res);
696 static int ads_group_delete(int argc, const char **argv)
700 LDAPMessage *res = NULL;
704 return net_ads_group_usage(argc, argv);
707 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
711 rc = ads_find_user_acct(ads, &res, argv[0]);
712 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
713 d_printf("Group %s does not exist.\n", argv[0]);
714 ads_msgfree(ads, res);
718 groupdn = ads_get_dn(ads, res);
719 ads_msgfree(ads, res);
720 rc = ads_del_dn(ads, groupdn);
721 ads_memfree(ads, groupdn);
722 if (ADS_ERR_OK(rc)) {
723 d_printf("Group %s deleted\n", argv[0]);
727 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
733 int net_ads_group(int argc, const char **argv)
735 struct functable func[] = {
736 {"ADD", ads_group_add},
737 {"DELETE", ads_group_delete},
742 const char *shortattrs[] = {"sAMAccountName", NULL};
743 const char *longattrs[] = {"sAMAccountName", "description", NULL};
744 char *disp_fields[2] = {NULL, NULL};
747 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
751 if (opt_long_list_entries)
752 d_printf("\nGroup name Comment"\
753 "\n-----------------------------\n");
754 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
756 "(objectCategory=group)",
757 opt_long_list_entries ? longattrs :
758 shortattrs, usergrp_display,
762 return ADS_ERR_OK(rc) ? 0 : -1;
764 return net_run_function(argc, argv, func, net_ads_group_usage);
767 static int net_ads_status(int argc, const char **argv)
773 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
777 rc = ads_find_machine_acct(ads, &res, global_myname());
778 if (!ADS_ERR_OK(rc)) {
779 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
784 if (ads_count_replies(ads, res) == 0) {
785 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
795 /*******************************************************************
796 Leave an AD domain. Windows XP disables the machine account.
797 We'll try the same. The old code would do an LDAP delete.
798 That only worked using the machine creds because added the machine
799 with full control to the computer object's ACL.
800 *******************************************************************/
802 static int net_ads_leave(int argc, const char **argv)
804 ADS_STRUCT *ads = NULL;
808 struct cli_state *cli = NULL;
810 DOM_SID *dom_sid = NULL;
811 char *short_domain_name = NULL;
813 if (!secrets_init()) {
814 DEBUG(1,("Failed to initialise secrets database\n"));
818 if (!(ctx = talloc_init("net_ads_leave"))) {
819 d_fprintf(stderr, "Could not initialise talloc context.\n");
823 /* The finds a DC and takes care of getting the
824 user creds if necessary */
826 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
830 /* make RPC calls here */
832 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap.ip,
833 ads->config.ldap_server_name)) )
838 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
842 saf_delete( short_domain_name );
844 status = netdom_leave_domain(ctx, cli, dom_sid);
846 /* Try and delete it via LDAP - the old way we used to. */
848 adsret = ads_leave_realm(ads, global_myname());
849 if (ADS_ERR_OK(adsret)) {
850 d_printf("Deleted account for '%s' in realm '%s'\n",
851 global_myname(), ads->config.realm);
854 /* We couldn't delete it - see if the disable succeeded. */
855 if (NT_STATUS_IS_OK(status)) {
856 d_printf("Disabled account for '%s' in realm '%s'\n",
857 global_myname(), ads->config.realm);
860 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
861 global_myname(), ads->config.realm);
876 static NTSTATUS net_ads_join_ok(void)
878 ADS_STRUCT *ads = NULL;
881 if (!secrets_init()) {
882 DEBUG(1,("Failed to initialise secrets database\n"));
883 return NT_STATUS_ACCESS_DENIED;
886 net_use_krb_machine_account();
888 status = ads_startup(True, &ads);
889 if (!ADS_ERR_OK(status)) {
890 return ads_ntstatus(status);
898 check that an existing join is OK
900 int net_ads_testjoin(int argc, const char **argv)
903 use_in_memory_ccache();
905 /* Display success or failure */
906 status = net_ads_join_ok();
907 if (!NT_STATUS_IS_OK(status)) {
908 fprintf(stderr,"Join to domain is not valid: %s\n",
909 get_friendly_nt_error_msg(status));
913 printf("Join is OK\n");
917 /*******************************************************************
918 Simple configu checks before beginning the join
919 ********************************************************************/
921 static NTSTATUS check_ads_config( void )
923 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
924 d_printf("Host is not configured as a member server.\n");
925 return NT_STATUS_INVALID_DOMAIN_ROLE;
928 if (strlen(global_myname()) > 15) {
929 d_printf("Our netbios name can be at most 15 chars long, "
930 "\"%s\" is %u chars long\n", global_myname(),
931 (unsigned int)strlen(global_myname()));
932 return NT_STATUS_NAME_TOO_LONG;
935 if ( lp_security() == SEC_ADS && !*lp_realm()) {
936 d_fprintf(stderr, "realm must be set in in %s for ADS "
937 "join to succeed.\n", dyn_CONFIGFILE);
938 return NT_STATUS_INVALID_PARAMETER;
941 if (!secrets_init()) {
942 DEBUG(1,("Failed to initialise secrets database\n"));
943 /* This is a good bet for failure of secrets_init ... */
944 return NT_STATUS_ACCESS_DENIED;
950 /*******************************************************************
952 ********************************************************************/
954 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
955 struct in_addr *ip, char **domain,
957 const char *password)
959 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
960 struct cli_state *cli = NULL;
962 ret = connect_to_ipc_krb5(&cli, ip, 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 LDAPMessage *res = NULL;
1250 const char *machine_name = global_myname();
1252 status = ads_domain_func_level( ads, &domain_func );
1253 if ( !ADS_ERR_OK(status) ) {
1254 DEBUG(2,("Failed to determine domain functional level!\n"));
1258 /* go ahead and setup the default salt */
1260 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1261 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1265 fstrcpy( salt, std_salt );
1266 SAFE_FREE( std_salt );
1268 /* if it's a Windows functional domain, we have to look for the UPN */
1270 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1274 status = ads_find_machine_acct(ads, &res, machine_name);
1275 if (!ADS_ERR_OK(status)) {
1279 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1280 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1284 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1286 fstrcpy( salt, upn );
1289 ads_msgfree(ads, res);
1292 return kerberos_secrets_store_des_salt( salt );
1295 /*******************************************************************
1296 Send a DNS update request
1297 *******************************************************************/
1299 #if defined(WITH_DNS_UPDATES)
1301 DNS_ERROR DoDNSUpdate(char *pszServerName,
1302 const char *pszDomainName,
1303 const char *pszHostName,
1304 const struct in_addr *iplist, int num_addrs );
1307 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1308 const char *machine_name,
1309 const struct in_addr *addrs,
1312 struct dns_rr_ns *nameservers = NULL;
1314 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1317 const char *dnsdomain = NULL;
1318 char *root_domain = NULL;
1320 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1321 d_printf("No DNS domain configured for %s. "
1322 "Unable to perform DNS Update.\n", machine_name);
1323 status = NT_STATUS_INVALID_PARAMETER;
1328 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1329 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1330 /* Child domains often do not have NS records. Look
1331 for the NS record for the forest root domain
1332 (rootDomainNamingContext in therootDSE) */
1334 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1335 LDAPMessage *msg = NULL;
1337 ADS_STATUS ads_status;
1339 if ( !ads->ldap.ld ) {
1340 ads_status = ads_connect( ads );
1341 if ( !ADS_ERR_OK(ads_status) ) {
1342 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1347 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1348 "(objectclass=*)", rootname_attrs, &msg);
1349 if (!ADS_ERR_OK(ads_status)) {
1353 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1355 ads_msgfree( ads, msg );
1359 root_domain = ads_build_domain( root_dn );
1362 ads_msgfree( ads, msg );
1364 /* try again for NS servers */
1366 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1368 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1369 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1370 "realm\n", ads->config.realm));
1374 dnsdomain = root_domain;
1378 /* Now perform the dns update - we'll try non-secure and if we fail,
1379 we'll follow it up with a secure update */
1381 fstrcpy( dns_server, nameservers[0].hostname );
1383 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1384 if (!ERR_DNS_IS_OK(dns_err)) {
1385 status = NT_STATUS_UNSUCCESSFUL;
1390 SAFE_FREE( root_domain );
1395 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1398 struct in_addr *iplist = NULL;
1399 fstring machine_name;
1402 name_to_fqdn( machine_name, global_myname() );
1403 strlower_m( machine_name );
1405 /* Get our ip address (not the 127.0.0.x address but a real ip
1408 num_addrs = get_my_ip_address( &iplist );
1409 if ( num_addrs <= 0 ) {
1410 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1412 return NT_STATUS_INVALID_PARAMETER;
1415 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1417 SAFE_FREE( iplist );
1423 /*******************************************************************
1424 utility function to parse an integer parameter from
1426 **********************************************************/
1427 static char* get_string_param( const char* param )
1431 if ( (p = strchr( param, '=' )) == NULL )
1437 /*******************************************************************
1438 ********************************************************************/
1440 static int net_ads_join_usage(int argc, const char **argv)
1442 d_printf("net ads join [options]\n");
1443 d_printf("Valid options:\n");
1444 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1445 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1446 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1447 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1448 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1449 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1450 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1451 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1452 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1453 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1454 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1455 d_printf(" the two other attributes.\n");
1460 /*******************************************************************
1461 ********************************************************************/
1463 int net_ads_join(int argc, const char **argv)
1465 ADS_STRUCT *ads = NULL;
1468 char *machine_account = NULL;
1469 char *short_domain_name = NULL;
1470 char *tmp_password, *password;
1471 TALLOC_CTX *ctx = NULL;
1472 DOM_SID *domain_sid = NULL;
1473 bool createupn = False;
1474 const char *machineupn = NULL;
1475 const char *create_in_ou = NULL;
1478 struct in_addr dcip;
1479 const char *os_name = NULL;
1480 const char *os_version = NULL;
1482 nt_status = check_ads_config();
1483 if (!NT_STATUS_IS_OK(nt_status)) {
1484 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1488 /* find a DC to initialize the server affinity cache */
1490 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
1492 status = ads_startup(True, &ads);
1493 if (!ADS_ERR_OK(status)) {
1494 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1495 nt_status = ads_ntstatus(status);
1499 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1500 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1501 "(%s) DO NOT match. Aborting join\n",
1502 ads->config.realm, dyn_CONFIGFILE, lp_realm());
1503 nt_status = NT_STATUS_INVALID_PARAMETER;
1507 if (!(ctx = talloc_init("net_ads_join"))) {
1508 d_fprintf(stderr, "Could not initialise talloc context.\n");
1509 nt_status = NT_STATUS_NO_MEMORY;
1513 /* process additional command line args */
1515 for ( i=0; i<argc; i++ ) {
1516 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1518 machineupn = get_string_param(argv[i]);
1520 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1521 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1522 d_fprintf(stderr, "Please supply a valid OU path.\n");
1523 nt_status = NT_STATUS_INVALID_PARAMETER;
1527 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1528 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1529 d_fprintf(stderr, "Please supply a operating system name.\n");
1530 nt_status = NT_STATUS_INVALID_PARAMETER;
1534 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1535 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1536 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1537 nt_status = NT_STATUS_INVALID_PARAMETER;
1542 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1543 nt_status = NT_STATUS_INVALID_PARAMETER;
1548 /* If we were given an OU, try to create the machine in
1549 the OU account first and then do the normal RPC join */
1551 if ( create_in_ou ) {
1552 status = net_precreate_machine_acct( ads, create_in_ou );
1553 if ( !ADS_ERR_OK(status) ) {
1554 d_fprintf( stderr, "Failed to pre-create the machine object "
1555 "in OU %s.\n", create_in_ou);
1556 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1557 ads_errstr(status)));
1558 nt_status = ads_ntstatus(status);
1563 /* Do the domain join here */
1565 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1566 password = talloc_strdup(ctx, tmp_password);
1568 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1569 &ads->ldap.ip, &short_domain_name, &domain_sid, password);
1570 if ( !NT_STATUS_IS_OK(nt_status) ) {
1571 DEBUG(1, ("call of net_join_domain failed: %s\n",
1572 get_friendly_nt_error_msg(nt_status)));
1576 /* Check the short name of the domain */
1578 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1579 d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
1580 d_printf("domain name obtained from the server.\n");
1581 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1582 d_printf("You should set \"workgroup = %s\" in %s.\n",
1583 short_domain_name, dyn_CONFIGFILE);
1586 d_printf("Using short domain name -- %s\n", short_domain_name);
1588 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1589 value from smb.conf and the string returned from the server. The former is
1590 neede to bootstrap winbindd's first connection to the DC to get the real
1591 short domain name --jerry */
1593 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1594 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1596 /* issue an internal error here for now.
1597 * everything else would mean changing tdb routines. */
1598 nt_status = NT_STATUS_INTERNAL_ERROR;
1602 /* Verify that everything is ok */
1604 nt_status = net_rpc_join_ok(short_domain_name,
1605 ads->config.ldap_server_name, &ads->ldap.ip);
1606 if (!NT_STATUS_IS_OK(nt_status)) {
1608 "Failed to verify membership in domain: %s!\n",
1609 nt_errstr(nt_status));
1613 /* create the dNSHostName & servicePrincipalName values */
1615 status = net_set_machine_spn( ctx, ads );
1616 if ( !ADS_ERR_OK(status) ) {
1618 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1619 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1620 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1622 /* Disable the machine account in AD. Better to fail than to leave
1623 a confused admin. */
1625 if ( net_ads_leave( 0, NULL ) != 0 ) {
1626 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1629 /* clear out the machine password */
1631 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1632 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1634 nt_status = ads_ntstatus(status);
1638 if ( !net_derive_salting_principal( ctx, ads ) ) {
1639 DEBUG(1,("Failed to determine salting principal\n"));
1646 /* default to using the short UPN name */
1647 if ( !machineupn ) {
1648 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1649 ads->config.realm );
1653 status = net_set_machine_upn( ctx, ads, machineupn );
1654 if ( !ADS_ERR_OK(status) ) {
1655 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1659 /* Try to set the operatingSystem attributes if asked */
1661 if ( os_name && os_version ) {
1662 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1663 if ( !ADS_ERR_OK(status) ) {
1664 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1665 "Are you a Domain Admin?\n");
1669 /* Now build the keytab, using the same ADS connection */
1671 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1672 DEBUG(1,("Error creating host keytab!\n"));
1675 #if defined(WITH_DNS_UPDATES)
1676 /* We enter this block with user creds */
1677 ads_kdestroy( NULL );
1681 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1682 /* kinit with the machine password */
1684 use_in_memory_ccache();
1685 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1686 ads->auth.password = secrets_fetch_machine_password(
1687 lp_workgroup(), NULL, NULL );
1688 ads->auth.realm = SMB_STRDUP( lp_realm() );
1689 ads_kinit_password( ads );
1692 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1693 d_fprintf( stderr, "DNS update failed!\n" );
1696 /* exit from this block using machine creds */
1699 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1701 SAFE_FREE(machine_account);
1708 /* issue an overall failure message at the end. */
1709 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1711 SAFE_FREE(machine_account);
1719 /*******************************************************************
1720 ********************************************************************/
1722 static int net_ads_dns_usage(int argc, const char **argv)
1724 #if defined(WITH_DNS_UPDATES)
1725 d_printf("net ads dns <command>\n");
1726 d_printf("Valid commands:\n");
1727 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1731 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1736 /*******************************************************************
1737 ********************************************************************/
1739 static int net_ads_dns_register(int argc, const char **argv)
1741 #if defined(WITH_DNS_UPDATES)
1747 talloc_enable_leak_report();
1751 d_fprintf(stderr, "net ads dns register\n");
1755 if (!(ctx = talloc_init("net_ads_dns"))) {
1756 d_fprintf(stderr, "Could not initialise talloc context\n");
1760 status = ads_startup(True, &ads);
1761 if ( !ADS_ERR_OK(status) ) {
1762 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1767 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1768 d_fprintf( stderr, "DNS update failed!\n" );
1769 ads_destroy( &ads );
1774 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1781 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1786 #if defined(WITH_DNS_UPDATES)
1787 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1790 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1792 #if defined(WITH_DNS_UPDATES)
1796 talloc_enable_leak_report();
1800 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1805 err = do_gethostbyname(argv[0], argv[1]);
1807 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1812 static int net_ads_dns(int argc, const char *argv[])
1814 struct functable func[] = {
1815 {"REGISTER", net_ads_dns_register},
1816 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1820 return net_run_function(argc, argv, func, net_ads_dns_usage);
1823 /*******************************************************************
1824 ********************************************************************/
1826 int net_ads_printer_usage(int argc, const char **argv)
1829 "\nnet ads printer search <printer>"
1830 "\n\tsearch for a printer in the directory\n"
1831 "\nnet ads printer info <printer> <server>"
1832 "\n\tlookup info in directory for printer on server"
1833 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1834 "\nnet ads printer publish <printername>"
1835 "\n\tpublish printer in directory"
1836 "\n\t(note: printer name is required)\n"
1837 "\nnet ads printer remove <printername>"
1838 "\n\tremove printer from directory"
1839 "\n\t(note: printer name is required)\n");
1843 /*******************************************************************
1844 ********************************************************************/
1846 static int net_ads_printer_search(int argc, const char **argv)
1850 LDAPMessage *res = NULL;
1852 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1856 rc = ads_find_printers(ads, &res);
1858 if (!ADS_ERR_OK(rc)) {
1859 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1860 ads_msgfree(ads, res);
1865 if (ads_count_replies(ads, res) == 0) {
1866 d_fprintf(stderr, "No results found\n");
1867 ads_msgfree(ads, res);
1873 ads_msgfree(ads, res);
1878 static int net_ads_printer_info(int argc, const char **argv)
1882 const char *servername, *printername;
1883 LDAPMessage *res = NULL;
1885 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1890 printername = argv[0];
1896 servername = argv[1];
1898 servername = global_myname();
1901 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1903 if (!ADS_ERR_OK(rc)) {
1904 d_fprintf(stderr, "Server '%s' not found: %s\n",
1905 servername, ads_errstr(rc));
1906 ads_msgfree(ads, res);
1911 if (ads_count_replies(ads, res) == 0) {
1912 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1913 ads_msgfree(ads, res);
1919 ads_msgfree(ads, res);
1925 static int net_ads_printer_publish(int argc, const char **argv)
1929 const char *servername, *printername;
1930 struct cli_state *cli;
1931 struct rpc_pipe_client *pipe_hnd;
1932 struct in_addr server_ip;
1934 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1935 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1936 char *prt_dn, *srv_dn, **srv_cn;
1937 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1938 LDAPMessage *res = NULL;
1940 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1941 talloc_destroy(mem_ctx);
1946 talloc_destroy(mem_ctx);
1947 return net_ads_printer_usage(argc, argv);
1950 printername = argv[0];
1953 servername = argv[1];
1955 servername = global_myname();
1958 /* Get printer data from SPOOLSS */
1960 resolve_name(servername, &server_ip, 0x20);
1962 nt_status = cli_full_connection(&cli, global_myname(), servername,
1965 opt_user_name, opt_workgroup,
1966 opt_password ? opt_password : "",
1967 CLI_FULL_CONNECTION_USE_KERBEROS,
1970 if (NT_STATUS_IS_ERR(nt_status)) {
1971 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1972 "for %s\n", servername, printername);
1974 talloc_destroy(mem_ctx);
1978 /* Publish on AD server */
1980 ads_find_machine_acct(ads, &res, servername);
1982 if (ads_count_replies(ads, res) == 0) {
1983 d_fprintf(stderr, "Could not find machine account for server %s\n",
1986 talloc_destroy(mem_ctx);
1990 srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1991 srv_cn = ldap_explode_dn(srv_dn, 1);
1993 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1994 printername_escaped = escape_rdn_val_string_alloc(printername);
1995 if (!srv_cn_escaped || !printername_escaped) {
1996 SAFE_FREE(srv_cn_escaped);
1997 SAFE_FREE(printername_escaped);
1998 d_fprintf(stderr, "Internal error, out of memory!");
2000 talloc_destroy(mem_ctx);
2004 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
2006 SAFE_FREE(srv_cn_escaped);
2007 SAFE_FREE(printername_escaped);
2009 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
2011 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
2015 talloc_destroy(mem_ctx);
2019 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
2023 talloc_destroy(mem_ctx);
2027 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
2028 if (!ADS_ERR_OK(rc)) {
2029 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2032 talloc_destroy(mem_ctx);
2036 d_printf("published printer\n");
2039 talloc_destroy(mem_ctx);
2044 static int net_ads_printer_remove(int argc, const char **argv)
2048 const char *servername;
2050 LDAPMessage *res = NULL;
2052 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2057 return net_ads_printer_usage(argc, argv);
2061 servername = argv[1];
2063 servername = global_myname();
2066 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2068 if (!ADS_ERR_OK(rc)) {
2069 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2070 ads_msgfree(ads, res);
2075 if (ads_count_replies(ads, res) == 0) {
2076 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2077 ads_msgfree(ads, res);
2082 prt_dn = ads_get_dn(ads, res);
2083 ads_msgfree(ads, res);
2084 rc = ads_del_dn(ads, prt_dn);
2085 ads_memfree(ads, prt_dn);
2087 if (!ADS_ERR_OK(rc)) {
2088 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2097 static int net_ads_printer(int argc, const char **argv)
2099 struct functable func[] = {
2100 {"SEARCH", net_ads_printer_search},
2101 {"INFO", net_ads_printer_info},
2102 {"PUBLISH", net_ads_printer_publish},
2103 {"REMOVE", net_ads_printer_remove},
2107 return net_run_function(argc, argv, func, net_ads_printer_usage);
2111 static int net_ads_password(int argc, const char **argv)
2114 const char *auth_principal = opt_user_name;
2115 const char *auth_password = opt_password;
2117 char *new_password = NULL;
2122 if (opt_user_name == NULL || opt_password == NULL) {
2123 d_fprintf(stderr, "You must supply an administrator username/password\n");
2128 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2133 if (!strchr_m(user, '@')) {
2134 asprintf(&c, "%s@%s", argv[0], lp_realm());
2138 use_in_memory_ccache();
2139 c = strchr_m(auth_principal, '@');
2146 /* use the realm so we can eventually change passwords for users
2147 in realms other than default */
2148 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2152 /* we don't actually need a full connect, but it's the easy way to
2153 fill in the KDC's addresss */
2156 if (!ads || !ads->config.realm) {
2157 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2162 new_password = (char *)argv[1];
2164 asprintf(&prompt, "Enter new password for %s:", user);
2165 new_password = getpass(prompt);
2169 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2170 auth_password, user, new_password, ads->auth.time_offset);
2171 if (!ADS_ERR_OK(ret)) {
2172 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2177 d_printf("Password change for %s completed.\n", user);
2183 int net_ads_changetrustpw(int argc, const char **argv)
2186 char *host_principal;
2190 if (!secrets_init()) {
2191 DEBUG(1,("Failed to initialise secrets database\n"));
2195 net_use_krb_machine_account();
2197 use_in_memory_ccache();
2199 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2203 fstrcpy(my_name, global_myname());
2204 strlower_m(my_name);
2205 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2206 d_printf("Changing password for principal: %s\n", host_principal);
2208 ret = ads_change_trust_account_password(ads, host_principal);
2210 if (!ADS_ERR_OK(ret)) {
2211 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2213 SAFE_FREE(host_principal);
2217 d_printf("Password change for principal %s succeeded.\n", host_principal);
2219 if (lp_use_kerberos_keytab()) {
2220 d_printf("Attempting to update system keytab with new password.\n");
2221 if (ads_keytab_create_default(ads)) {
2222 d_printf("Failed to update system keytab.\n");
2227 SAFE_FREE(host_principal);
2233 help for net ads search
2235 static int net_ads_search_usage(int argc, const char **argv)
2238 "\nnet ads search <expression> <attributes...>\n"\
2239 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2240 "The expression is a standard LDAP search expression, and the\n"\
2241 "attributes are a list of LDAP fields to show in the results\n\n"\
2242 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2244 net_common_flags_usage(argc, argv);
2250 general ADS search function. Useful in diagnosing problems in ADS
2252 static int net_ads_search(int argc, const char **argv)
2256 const char *ldap_exp;
2258 LDAPMessage *res = NULL;
2261 return net_ads_search_usage(argc, argv);
2264 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2271 rc = ads_do_search_all(ads, ads->config.bind_path,
2273 ldap_exp, attrs, &res);
2274 if (!ADS_ERR_OK(rc)) {
2275 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2280 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2282 /* dump the results */
2285 ads_msgfree(ads, res);
2293 help for net ads search
2295 static int net_ads_dn_usage(int argc, const char **argv)
2298 "\nnet ads dn <dn> <attributes...>\n"\
2299 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2300 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2301 "to show in the results\n\n"\
2302 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2303 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2305 net_common_flags_usage(argc, argv);
2311 general ADS search function. Useful in diagnosing problems in ADS
2313 static int net_ads_dn(int argc, const char **argv)
2319 LDAPMessage *res = NULL;
2322 return net_ads_dn_usage(argc, argv);
2325 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2332 rc = ads_do_search_all(ads, dn,
2334 "(objectclass=*)", attrs, &res);
2335 if (!ADS_ERR_OK(rc)) {
2336 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2341 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2343 /* dump the results */
2346 ads_msgfree(ads, res);
2353 help for net ads sid search
2355 static int net_ads_sid_usage(int argc, const char **argv)
2358 "\nnet ads sid <sid> <attributes...>\n"\
2359 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2360 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2361 "to show in the results\n\n"\
2362 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2364 net_common_flags_usage(argc, argv);
2370 general ADS search function. Useful in diagnosing problems in ADS
2372 static int net_ads_sid(int argc, const char **argv)
2376 const char *sid_string;
2378 LDAPMessage *res = NULL;
2382 return net_ads_sid_usage(argc, argv);
2385 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2389 sid_string = argv[0];
2392 if (!string_to_sid(&sid, sid_string)) {
2393 d_fprintf(stderr, "could not convert sid\n");
2398 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2399 if (!ADS_ERR_OK(rc)) {
2400 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2405 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2407 /* dump the results */
2410 ads_msgfree(ads, res);
2417 static int net_ads_keytab_usage(int argc, const char **argv)
2420 "net ads keytab <COMMAND>\n"\
2421 "<COMMAND> can be either:\n"\
2422 " ADD Adds new service principal\n"\
2423 " CREATE Creates a fresh keytab\n"\
2424 " FLUSH Flushes out all keytab entries\n"\
2425 " HELP Prints this help message\n"\
2426 " LIST List the keytab\n"\
2427 "The ADD and LIST command will take arguments, the other commands\n"\
2428 "will not take any arguments. The arguments given to ADD\n"\
2429 "should be a list of principals to add. For example, \n"\
2430 " net ads keytab add srv1 srv2\n"\
2431 "will add principals for the services srv1 and srv2 to the\n"\
2432 "system's keytab.\n"\
2433 "The LIST command takes a keytabname.\n"\
2439 static int net_ads_keytab_flush(int argc, const char **argv)
2444 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2447 ret = ads_keytab_flush(ads);
2452 static int net_ads_keytab_add(int argc, const char **argv)
2458 d_printf("Processing principals to add...\n");
2459 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2462 for (i = 0; i < argc; i++) {
2463 ret |= ads_keytab_add_entry(ads, argv[i]);
2469 static int net_ads_keytab_create(int argc, const char **argv)
2474 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2477 ret = ads_keytab_create_default(ads);
2482 static int net_ads_keytab_list(int argc, const char **argv)
2484 const char *keytab = NULL;
2490 return ads_keytab_list(keytab);
2494 int net_ads_keytab(int argc, const char **argv)
2496 struct functable func[] = {
2497 {"ADD", net_ads_keytab_add},
2498 {"CREATE", net_ads_keytab_create},
2499 {"FLUSH", net_ads_keytab_flush},
2500 {"HELP", net_ads_keytab_usage},
2501 {"LIST", net_ads_keytab_list},
2505 if (!lp_use_kerberos_keytab()) {
2506 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2507 use keytab functions.\n");
2510 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2513 static int net_ads_kerberos_usage(int argc, const char **argv)
2516 "net ads kerberos <COMMAND>\n"\
2517 "<COMMAND> can be either:\n"\
2518 " RENEW Renew TGT from existing credential cache\n"\
2519 " PAC Dumps the Kerberos PAC\n"\
2520 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2527 static int net_ads_kerberos_renew(int argc, const char **argv)
2529 int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2531 d_printf("failed to renew kerberos ticket: %s\n",
2532 error_message(ret));
2537 static int net_ads_kerberos_pac(int argc, const char **argv)
2539 PAC_DATA *pac = NULL;
2540 PAC_LOGON_INFO *info = NULL;
2541 TALLOC_CTX *mem_ctx = NULL;
2545 mem_ctx = talloc_init("net_ads_kerberos_pac");
2550 opt_password = net_prompt_pass(opt_user_name);
2552 status = kerberos_return_pac(mem_ctx,
2561 2592000, /* one month */
2563 if (!NT_STATUS_IS_OK(status)) {
2564 d_printf("failed to query kerberos PAC: %s\n",
2569 info = get_logon_info_from_pac(pac);
2571 dump_pac_logon_info(0, info);
2576 TALLOC_FREE(mem_ctx);
2580 static int net_ads_kerberos_kinit(int argc, const char **argv)
2582 TALLOC_CTX *mem_ctx = NULL;
2586 mem_ctx = talloc_init("net_ads_kerberos_kinit");
2591 opt_password = net_prompt_pass(opt_user_name);
2593 ret = kerberos_kinit_password_ext(opt_user_name,
2601 2592000, /* one month */
2604 d_printf("failed to kinit password: %s\n",
2611 int net_ads_kerberos(int argc, const char **argv)
2613 struct functable func[] = {
2614 {"KINIT", net_ads_kerberos_kinit},
2615 {"RENEW", net_ads_kerberos_renew},
2616 {"PAC", net_ads_kerberos_pac},
2617 {"HELP", net_ads_kerberos_usage},
2621 return net_run_function(argc, argv, func, net_ads_kerberos_usage);
2625 int net_ads_help(int argc, const char **argv)
2627 struct functable func[] = {
2628 {"USER", net_ads_user_usage},
2629 {"GROUP", net_ads_group_usage},
2630 {"PRINTER", net_ads_printer_usage},
2631 {"SEARCH", net_ads_search_usage},
2632 {"INFO", net_ads_info},
2633 {"JOIN", net_ads_join_usage},
2634 {"DNS", net_ads_dns_usage},
2635 {"LEAVE", net_ads_leave},
2636 {"STATUS", net_ads_status},
2637 {"PASSWORD", net_ads_password},
2638 {"CHANGETRUSTPW", net_ads_changetrustpw},
2642 return net_run_function(argc, argv, func, net_ads_usage);
2645 int net_ads(int argc, const char **argv)
2647 struct functable func[] = {
2648 {"INFO", net_ads_info},
2649 {"JOIN", net_ads_join},
2650 {"TESTJOIN", net_ads_testjoin},
2651 {"LEAVE", net_ads_leave},
2652 {"STATUS", net_ads_status},
2653 {"USER", net_ads_user},
2654 {"GROUP", net_ads_group},
2655 {"DNS", net_ads_dns},
2656 {"PASSWORD", net_ads_password},
2657 {"CHANGETRUSTPW", net_ads_changetrustpw},
2658 {"PRINTER", net_ads_printer},
2659 {"SEARCH", net_ads_search},
2661 {"SID", net_ads_sid},
2662 {"WORKGROUP", net_ads_workgroup},
2663 {"LOOKUP", net_ads_lookup},
2664 {"KEYTAB", net_ads_keytab},
2665 {"GPO", net_ads_gpo},
2666 {"KERBEROS", net_ads_kerberos},
2667 {"HELP", net_ads_help},
2671 return net_run_function(argc, argv, func, net_ads_usage);
2676 static int net_ads_noads(void)
2678 d_fprintf(stderr, "ADS support not compiled in\n");
2682 int net_ads_keytab(int argc, const char **argv)
2684 return net_ads_noads();
2687 int net_ads_kerberos(int argc, const char **argv)
2689 return net_ads_noads();
2692 int net_ads_usage(int argc, const char **argv)
2694 return net_ads_noads();
2697 int net_ads_help(int argc, const char **argv)
2699 return net_ads_noads();
2702 int net_ads_changetrustpw(int argc, const char **argv)
2704 return net_ads_noads();
2707 int net_ads_join(int argc, const char **argv)
2709 return net_ads_noads();
2712 int net_ads_user(int argc, const char **argv)
2714 return net_ads_noads();
2717 int net_ads_group(int argc, const char **argv)
2719 return net_ads_noads();
2722 /* this one shouldn't display a message */
2723 int net_ads_check(void)
2728 int net_ads_check_our_domain(void)
2733 int net_ads(int argc, const char **argv)
2735 return net_ads_usage(argc, argv);
2738 #endif /* WITH_ADS */