2 Samba Unix/Linux SMB client library
4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5 Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6 Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
7 Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "utils/net.h"
29 int net_ads_usage(int argc, const char **argv)
31 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
32 d_printf(" Join the local machine to a ADS realm\n");
34 d_printf(" Remove the local machine from a ADS realm\n");
35 d_printf("testjoin\n");
36 d_printf(" Validates the machine account in the domain\n");
38 d_printf(" List, add, or delete users in the realm\n");
40 d_printf(" List, add, or delete groups in the realm\n");
42 d_printf(" Displays details regarding a specific AD server\n");
44 d_printf(" Display details regarding the machine's account in AD\n");
46 d_printf(" Performs CLDAP query of AD domain controllers\n");
47 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
48 d_printf(" Change a user's password using an admin account\n");
49 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
50 d_printf("changetrustpw\n");
51 d_printf(" Change the trust account password of this machine in the AD tree\n");
52 d_printf("printer [info | publish | remove] <printername> <servername>\n");
53 d_printf(" Lookup, add, or remove directory entry for a printer\n");
54 d_printf("{search,dn,sid}\n");
55 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
57 d_printf(" Manage a local keytab file based on the machine account in AD\n");
59 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
60 d_printf(" (using the machine credentials)\n");
65 /* when we do not have sufficient input parameters to contact a remote domain
66 * we always fall back to our own realm - Guenther*/
68 static const char *assume_own_realm(void)
70 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
78 do a cldap netlogon query
80 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
82 struct cldap_netlogon_reply reply;
84 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
85 d_fprintf(stderr, "CLDAP query failed!\n");
89 d_printf("Information for Domain Controller: %s\n\n",
90 inet_ntoa(ads->ldap_ip));
92 d_printf("Response Type: ");
94 case SAMLOGON_AD_UNK_R:
95 d_printf("SAMLOGON\n");
98 d_printf("SAMLOGON_USER\n");
101 d_printf("0x%x\n", reply.type);
104 d_printf("GUID: %s\n",
105 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
108 "\tIs a GC of the forest: %s\n"
109 "\tIs an LDAP server: %s\n"
110 "\tSupports DS: %s\n"
111 "\tIs running a KDC: %s\n"
112 "\tIs running time services: %s\n"
113 "\tIs the closest DC: %s\n"
114 "\tIs writable: %s\n"
115 "\tHas a hardware clock: %s\n"
116 "\tIs a non-domain NC serviced by LDAP server: %s\n",
117 (reply.flags & ADS_PDC) ? "yes" : "no",
118 (reply.flags & ADS_GC) ? "yes" : "no",
119 (reply.flags & ADS_LDAP) ? "yes" : "no",
120 (reply.flags & ADS_DS) ? "yes" : "no",
121 (reply.flags & ADS_KDC) ? "yes" : "no",
122 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
123 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
124 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
125 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
126 (reply.flags & ADS_NDNC) ? "yes" : "no");
128 printf("Forest:\t\t\t%s\n", reply.forest);
129 printf("Domain:\t\t\t%s\n", reply.domain);
130 printf("Domain Controller:\t%s\n", reply.hostname);
132 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
133 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
135 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
136 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
138 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
139 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
141 d_printf("NT Version: %d\n", reply.version);
142 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
143 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
150 this implements the CLDAP based netlogon lookup requests
151 for finding the domain controller of a ADS domain
153 static int net_ads_lookup(int argc, const char **argv)
157 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
158 d_fprintf(stderr, "Didn't find the cldap server!\n");
162 if (!ads->config.realm) {
163 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
164 ads->ldap_port = 389;
167 return net_ads_cldap_netlogon(ads);
172 static int net_ads_info(int argc, const char **argv)
176 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
177 d_fprintf(stderr, "Didn't find the ldap server!\n");
181 if (!ads || !ads->config.realm) {
182 d_fprintf(stderr, "Didn't find the ldap server!\n");
186 /* Try to set the server's current time since we didn't do a full
187 TCP LDAP session initially */
189 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
190 d_fprintf( stderr, "Failed to get server's current time!\n");
193 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
194 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
195 d_printf("Realm: %s\n", ads->config.realm);
196 d_printf("Bind Path: %s\n", ads->config.bind_path);
197 d_printf("LDAP port: %d\n", ads->ldap_port);
198 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
200 d_printf("KDC server: %s\n", ads->auth.kdc_server );
201 d_printf("Server time offset: %d\n", ads->auth.time_offset );
206 static void use_in_memory_ccache(void) {
207 /* Use in-memory credentials cache so we do not interfere with
208 * existing credentials */
209 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
212 static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
214 ADS_STRUCT *ads = NULL;
216 BOOL need_password = False;
217 BOOL second_time = False;
219 const char *realm = NULL;
220 BOOL tried_closest_dc = False;
222 /* lp_realm() should be handled by a command line param,
223 However, the join requires that realm be set in smb.conf
224 and compares our realm with the remote server's so this is
225 ok until someone needs more flexibility */
230 if (only_own_domain) {
233 realm = assume_own_realm();
236 ads = ads_init(realm, opt_target_workgroup, opt_host);
238 if (!opt_user_name) {
239 opt_user_name = "administrator";
242 if (opt_user_specified) {
243 need_password = True;
247 if (!opt_password && need_password && !opt_machine_pass) {
249 asprintf(&prompt,"%s's password: ", opt_user_name);
252 return ADS_ERROR(LDAP_NO_MEMORY);
254 opt_password = getpass(prompt);
259 use_in_memory_ccache();
260 SAFE_FREE(ads->auth.password);
261 ads->auth.password = smb_xstrdup(opt_password);
264 ads->auth.flags |= auth_flags;
265 SAFE_FREE(ads->auth.user_name);
266 ads->auth.user_name = smb_xstrdup(opt_user_name);
269 * If the username is of the form "name@realm",
270 * extract the realm and convert to upper case.
271 * This is only used to establish the connection.
273 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
275 SAFE_FREE(ads->auth.realm);
276 ads->auth.realm = smb_xstrdup(cp);
277 strupper_m(ads->auth.realm);
280 status = ads_connect(ads);
282 if (!ADS_ERR_OK(status)) {
284 if (NT_STATUS_EQUAL(ads_ntstatus(status),
285 NT_STATUS_NO_LOGON_SERVERS)) {
286 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
291 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
292 need_password = True;
301 /* when contacting our own domain, make sure we use the closest DC.
302 * This is done by reconnecting to ADS because only the first call to
303 * ads_connect will give us our own sitename */
305 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
307 tried_closest_dc = True; /* avoid loop */
309 if (!ads->config.tried_closest_dc) {
311 namecache_delete(ads->server.realm, 0x1C);
312 namecache_delete(ads->server.workgroup, 0x1C);
325 ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
327 return ads_startup_int(only_own_domain, 0, ads);
330 ADS_STATUS ads_startup_nobind(BOOL only_own_domain, ADS_STRUCT **ads)
332 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
336 Check to see if connection can be made via ads.
337 ads_startup() stores the password in opt_password if it needs to so
338 that rpc or rap can use it without re-prompting.
340 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
345 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
349 ads->auth.flags |= ADS_AUTH_NO_BIND;
351 status = ads_connect(ads);
352 if ( !ADS_ERR_OK(status) ) {
360 int net_ads_check_our_domain(void)
362 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
365 int net_ads_check(void)
367 return net_ads_check_int(NULL, opt_workgroup, opt_host);
370 determine the netbios workgroup name for a domain
372 static int net_ads_workgroup(int argc, const char **argv)
375 struct cldap_netlogon_reply reply;
377 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
378 d_fprintf(stderr, "Didn't find the cldap server!\n");
382 if (!ads->config.realm) {
383 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
384 ads->ldap_port = 389;
387 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
388 d_fprintf(stderr, "CLDAP query failed!\n");
392 d_printf("Workgroup: %s\n", reply.netbios_domain);
401 static BOOL usergrp_display(char *field, void **values, void *data_area)
403 char **disp_fields = (char **) data_area;
405 if (!field) { /* must be end of record */
406 if (disp_fields[0]) {
407 if (!strchr_m(disp_fields[0], '$')) {
409 d_printf("%-21.21s %s\n",
410 disp_fields[0], disp_fields[1]);
412 d_printf("%s\n", disp_fields[0]);
415 SAFE_FREE(disp_fields[0]);
416 SAFE_FREE(disp_fields[1]);
419 if (!values) /* must be new field, indicate string field */
421 if (StrCaseCmp(field, "sAMAccountName") == 0) {
422 disp_fields[0] = SMB_STRDUP((char *) values[0]);
424 if (StrCaseCmp(field, "description") == 0)
425 disp_fields[1] = SMB_STRDUP((char *) values[0]);
429 static int net_ads_user_usage(int argc, const char **argv)
431 return net_help_user(argc, argv);
434 static int ads_user_add(int argc, const char **argv)
439 LDAPMessage *res=NULL;
443 if (argc < 1) return net_ads_user_usage(argc, argv);
445 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
449 status = ads_find_user_acct(ads, &res, argv[0]);
451 if (!ADS_ERR_OK(status)) {
452 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
456 if (ads_count_replies(ads, res)) {
457 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
462 ou_str = SMB_STRDUP(opt_container);
464 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
467 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
469 if (!ADS_ERR_OK(status)) {
470 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
475 /* if no password is to be set, we're done */
477 d_printf("User %s added\n", argv[0]);
482 /* try setting the password */
483 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
484 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
485 ads->auth.time_offset);
487 if (ADS_ERR_OK(status)) {
488 d_printf("User %s added\n", argv[0]);
493 /* password didn't set, delete account */
494 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
495 argv[0], ads_errstr(status));
496 ads_msgfree(ads, res);
497 status=ads_find_user_acct(ads, &res, argv[0]);
498 if (ADS_ERR_OK(status)) {
499 userdn = ads_get_dn(ads, res);
500 ads_del_dn(ads, userdn);
501 ads_memfree(ads, userdn);
506 ads_msgfree(ads, res);
512 static int ads_user_info(int argc, const char **argv)
517 const char *attrs[] = {"memberOf", NULL};
518 char *searchstring=NULL;
523 return net_ads_user_usage(argc, argv);
526 escaped_user = escape_ldap_string_alloc(argv[0]);
529 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
533 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
534 SAFE_FREE(escaped_user);
538 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
539 rc = ads_search(ads, &res, searchstring, attrs);
540 safe_free(searchstring);
542 if (!ADS_ERR_OK(rc)) {
543 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
545 SAFE_FREE(escaped_user);
549 grouplist = ldap_get_values((LDAP *)ads->ld,
550 (LDAPMessage *)res, "memberOf");
555 for (i=0;grouplist[i];i++) {
556 groupname = ldap_explode_dn(grouplist[i], 1);
557 d_printf("%s\n", groupname[0]);
558 ldap_value_free(groupname);
560 ldap_value_free(grouplist);
563 ads_msgfree(ads, res);
565 SAFE_FREE(escaped_user);
569 static int ads_user_delete(int argc, const char **argv)
573 LDAPMessage *res = NULL;
577 return net_ads_user_usage(argc, argv);
580 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
584 rc = ads_find_user_acct(ads, &res, argv[0]);
585 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
586 d_printf("User %s does not exist.\n", argv[0]);
587 ads_msgfree(ads, res);
591 userdn = ads_get_dn(ads, res);
592 ads_msgfree(ads, res);
593 rc = ads_del_dn(ads, userdn);
594 ads_memfree(ads, userdn);
595 if (ADS_ERR_OK(rc)) {
596 d_printf("User %s deleted\n", argv[0]);
600 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
606 int net_ads_user(int argc, const char **argv)
608 struct functable func[] = {
609 {"ADD", ads_user_add},
610 {"INFO", ads_user_info},
611 {"DELETE", ads_user_delete},
616 const char *shortattrs[] = {"sAMAccountName", NULL};
617 const char *longattrs[] = {"sAMAccountName", "description", NULL};
618 char *disp_fields[2] = {NULL, NULL};
621 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
625 if (opt_long_list_entries)
626 d_printf("\nUser name Comment"\
627 "\n-----------------------------\n");
629 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
631 "(objectCategory=user)",
632 opt_long_list_entries ? longattrs :
633 shortattrs, usergrp_display,
636 return ADS_ERR_OK(rc) ? 0 : -1;
639 return net_run_function(argc, argv, func, net_ads_user_usage);
642 static int net_ads_group_usage(int argc, const char **argv)
644 return net_help_group(argc, argv);
647 static int ads_group_add(int argc, const char **argv)
651 LDAPMessage *res=NULL;
656 return net_ads_group_usage(argc, argv);
659 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
663 status = ads_find_user_acct(ads, &res, argv[0]);
665 if (!ADS_ERR_OK(status)) {
666 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
670 if (ads_count_replies(ads, res)) {
671 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
676 ou_str = SMB_STRDUP(opt_container);
678 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
681 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
683 if (ADS_ERR_OK(status)) {
684 d_printf("Group %s added\n", argv[0]);
687 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
693 ads_msgfree(ads, res);
699 static int ads_group_delete(int argc, const char **argv)
703 LDAPMessage *res = NULL;
707 return net_ads_group_usage(argc, argv);
710 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
714 rc = ads_find_user_acct(ads, &res, argv[0]);
715 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
716 d_printf("Group %s does not exist.\n", argv[0]);
717 ads_msgfree(ads, res);
721 groupdn = ads_get_dn(ads, res);
722 ads_msgfree(ads, res);
723 rc = ads_del_dn(ads, groupdn);
724 ads_memfree(ads, groupdn);
725 if (ADS_ERR_OK(rc)) {
726 d_printf("Group %s deleted\n", argv[0]);
730 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
736 int net_ads_group(int argc, const char **argv)
738 struct functable func[] = {
739 {"ADD", ads_group_add},
740 {"DELETE", ads_group_delete},
745 const char *shortattrs[] = {"sAMAccountName", NULL};
746 const char *longattrs[] = {"sAMAccountName", "description", NULL};
747 char *disp_fields[2] = {NULL, NULL};
750 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
754 if (opt_long_list_entries)
755 d_printf("\nGroup name Comment"\
756 "\n-----------------------------\n");
757 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
759 "(objectCategory=group)",
760 opt_long_list_entries ? longattrs :
761 shortattrs, usergrp_display,
765 return ADS_ERR_OK(rc) ? 0 : -1;
767 return net_run_function(argc, argv, func, net_ads_group_usage);
770 static int net_ads_status(int argc, const char **argv)
776 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
780 rc = ads_find_machine_acct(ads, &res, global_myname());
781 if (!ADS_ERR_OK(rc)) {
782 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
787 if (ads_count_replies(ads, res) == 0) {
788 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
798 /*******************************************************************
799 Leave an AD domain. Windows XP disables the machine account.
800 We'll try the same. The old code would do an LDAP delete.
801 That only worked using the machine creds because added the machine
802 with full control to the computer object's ACL.
803 *******************************************************************/
805 static int net_ads_leave(int argc, const char **argv)
807 ADS_STRUCT *ads = NULL;
811 struct cli_state *cli = NULL;
813 DOM_SID *dom_sid = NULL;
814 char *short_domain_name = NULL;
816 if (!secrets_init()) {
817 DEBUG(1,("Failed to initialise secrets database\n"));
821 if (!(ctx = talloc_init("net_ads_leave"))) {
822 d_fprintf(stderr, "Could not initialise talloc context.\n");
826 /* The finds a DC and takes care of getting the
827 user creds if necessary */
829 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
833 /* make RPC calls here */
835 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
836 ads->config.ldap_server_name)) )
841 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
845 saf_delete( short_domain_name );
847 status = netdom_leave_domain(ctx, cli, dom_sid);
849 /* Try and delete it via LDAP - the old way we used to. */
851 adsret = ads_leave_realm(ads, global_myname());
852 if (ADS_ERR_OK(adsret)) {
853 d_printf("Deleted account for '%s' in realm '%s'\n",
854 global_myname(), ads->config.realm);
857 /* We couldn't delete it - see if the disable succeeded. */
858 if (NT_STATUS_IS_OK(status)) {
859 d_printf("Disabled account for '%s' in realm '%s'\n",
860 global_myname(), ads->config.realm);
863 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
864 global_myname(), ads->config.realm);
879 static NTSTATUS net_ads_join_ok(void)
881 ADS_STRUCT *ads = NULL;
884 if (!secrets_init()) {
885 DEBUG(1,("Failed to initialise secrets database\n"));
886 return NT_STATUS_ACCESS_DENIED;
889 net_use_machine_password();
891 status = ads_startup(True, &ads);
892 if (!ADS_ERR_OK(status)) {
893 return ads_ntstatus(status);
901 check that an existing join is OK
903 int net_ads_testjoin(int argc, const char **argv)
906 use_in_memory_ccache();
908 /* Display success or failure */
909 status = net_ads_join_ok();
910 if (!NT_STATUS_IS_OK(status)) {
911 fprintf(stderr,"Join to domain is not valid: %s\n",
912 get_friendly_nt_error_msg(status));
916 printf("Join is OK\n");
920 /*******************************************************************
921 Simple configu checks before beginning the join
922 ********************************************************************/
924 static NTSTATUS check_ads_config( void )
926 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
927 d_printf("Host is not configured as a member server.\n");
928 return NT_STATUS_INVALID_DOMAIN_ROLE;
931 if (strlen(global_myname()) > 15) {
932 d_printf("Our netbios name can be at most 15 chars long, "
933 "\"%s\" is %u chars long\n", global_myname(),
934 (unsigned int)strlen(global_myname()));
935 return NT_STATUS_NAME_TOO_LONG;
938 if ( lp_security() == SEC_ADS && !*lp_realm()) {
939 d_fprintf(stderr, "realm must be set in in %s for ADS "
940 "join to succeed.\n", dyn_CONFIGFILE);
941 return NT_STATUS_INVALID_PARAMETER;
944 if (!secrets_init()) {
945 DEBUG(1,("Failed to initialise secrets database\n"));
946 /* This is a good bet for failure of secrets_init ... */
947 return NT_STATUS_ACCESS_DENIED;
953 /*******************************************************************
955 ********************************************************************/
957 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
958 struct in_addr *ip, char **domain,
960 const char *password)
962 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
963 struct cli_state *cli = NULL;
965 ret = connect_to_ipc_krb5(&cli, ip, servername);
966 if ( !NT_STATUS_IS_OK(ret) ) {
970 ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
971 if ( !NT_STATUS_IS_OK(ret) ) {
975 /* cli->server_domain is not filled in when using krb5
978 saf_store( *domain, cli->desthost );
980 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
989 /*******************************************************************
990 Set a machines dNSHostName and servicePrincipalName attributes
991 ********************************************************************/
993 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
995 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
998 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1001 LDAPMessage *res = NULL;
1002 char *dn_string = NULL;
1003 const char *machine_name = global_myname();
1006 if ( !machine_name ) {
1007 return ADS_ERROR(LDAP_NO_MEMORY);
1012 status = ads_find_machine_acct(ads_s, &res, machine_name);
1013 if (!ADS_ERR_OK(status))
1016 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1017 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1018 return ADS_ERROR(LDAP_NO_MEMORY);
1021 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1022 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1026 new_dn = talloc_strdup(ctx, dn_string);
1027 ads_memfree(ads_s, dn_string);
1029 return ADS_ERROR(LDAP_NO_MEMORY);
1032 /* Windows only creates HOST/shortname & HOST/fqdn. */
1034 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1037 servicePrincipalName[0] = psp;
1039 name_to_fqdn(my_fqdn, machine_name);
1040 strlower_m(my_fqdn);
1041 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1043 servicePrincipalName[1] = psp;
1045 if (!(mods = ads_init_mods(ctx))) {
1049 /* fields of primary importance */
1051 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1052 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1054 status = ads_gen_mod(ads_s, new_dn, mods);
1057 ads_msgfree(ads_s, res);
1062 /*******************************************************************
1063 Set a machines dNSHostName and servicePrincipalName attributes
1064 ********************************************************************/
1066 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1068 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1071 LDAPMessage *res = NULL;
1072 char *dn_string = NULL;
1073 const char *machine_name = global_myname();
1076 if ( !machine_name ) {
1077 return ADS_ERROR(LDAP_NO_MEMORY);
1082 status = ads_find_machine_acct(ads_s, &res, machine_name);
1083 if (!ADS_ERR_OK(status))
1086 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1087 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1088 return ADS_ERROR(LDAP_NO_MEMORY);
1091 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1092 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1096 new_dn = talloc_strdup(ctx, dn_string);
1097 ads_memfree(ads_s, dn_string);
1099 return ADS_ERROR(LDAP_NO_MEMORY);
1102 /* now do the mods */
1104 if (!(mods = ads_init_mods(ctx))) {
1108 /* fields of primary importance */
1110 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1112 status = ads_gen_mod(ads_s, new_dn, mods);
1115 ads_msgfree(ads_s, res);
1120 /*******************************************************************
1121 Set a machines dNSHostName and servicePrincipalName attributes
1122 ********************************************************************/
1124 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1125 const char *os_name, const char *os_version )
1127 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1130 LDAPMessage *res = NULL;
1131 char *dn_string = NULL;
1132 const char *machine_name = global_myname();
1136 if ( !os_name || !os_version ) {
1137 return ADS_ERROR(LDAP_NO_MEMORY);
1142 status = ads_find_machine_acct(ads_s, &res, machine_name);
1143 if (!ADS_ERR_OK(status))
1146 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1147 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1148 return ADS_ERROR(LDAP_NO_MEMORY);
1151 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1152 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1156 new_dn = talloc_strdup(ctx, dn_string);
1157 ads_memfree(ads_s, dn_string);
1159 return ADS_ERROR(LDAP_NO_MEMORY);
1162 /* now do the mods */
1164 if (!(mods = ads_init_mods(ctx))) {
1168 os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1170 /* fields of primary importance */
1172 ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1173 ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1175 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1177 status = ads_gen_mod(ads_s, new_dn, mods);
1180 ads_msgfree(ads_s, res);
1181 TALLOC_FREE( os_sp );
1186 /*******************************************************************
1187 join a domain using ADS (LDAP mods)
1188 ********************************************************************/
1190 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1192 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1193 char *ou_str = NULL;
1195 LDAPMessage *res = NULL;
1198 ou_str = ads_ou_string(ads, ou);
1199 if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1200 rc = ADS_ERROR(LDAP_NO_MEMORY);
1204 rc = ads_search_dn(ads, &res, dn, NULL);
1205 if (!ADS_ERR_OK(rc)) {
1206 d_fprintf(stderr, "The specified OU does not exist.\n");
1210 /* Attempt to create the machine account and bail if this fails.
1211 Assume that the admin wants exactly what they requested */
1213 rc = ads_create_machine_acct( ads, global_myname(), dn );
1214 if (ADS_ERR_OK(rc)) {
1215 DEBUG(1, ("machine account created\n"));
1218 if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1219 DEBUG(1, ("machine account creation failed\n"));
1223 rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1224 if (!ADS_ERR_OK(rc)) {
1225 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1230 d_printf("The machine account was moved into the specified OU.\n");
1232 d_printf("The machine account already exists in the specified OU.\n");
1236 ads_msgfree(ads, res);
1237 SAFE_FREE( ou_str );
1243 /************************************************************************
1244 ************************************************************************/
1246 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1252 LDAPMessage *res = NULL;
1253 const char *machine_name = global_myname();
1255 status = ads_domain_func_level( ads, &domain_func );
1256 if ( !ADS_ERR_OK(status) ) {
1257 DEBUG(2,("Failed to determine domain functional level!\n"));
1261 /* go ahead and setup the default salt */
1263 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1264 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1268 fstrcpy( salt, std_salt );
1269 SAFE_FREE( std_salt );
1271 /* if it's a Windows functional domain, we have to look for the UPN */
1273 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1277 status = ads_find_machine_acct(ads, &res, machine_name);
1278 if (!ADS_ERR_OK(status)) {
1282 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1283 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1287 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1289 fstrcpy( salt, upn );
1292 ads_msgfree(ads, res);
1295 return kerberos_secrets_store_des_salt( salt );
1298 /*******************************************************************
1299 Send a DNS update request
1300 *******************************************************************/
1302 #if defined(WITH_DNS_UPDATES)
1304 DNS_ERROR DoDNSUpdate(char *pszServerName,
1305 const char *pszDomainName,
1306 const char *pszHostName,
1307 const struct in_addr *iplist, int num_addrs );
1310 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1311 const char *machine_name,
1312 const struct in_addr *addrs,
1315 struct dns_rr_ns *nameservers = NULL;
1317 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1320 const char *dnsdomain = NULL;
1321 char *root_domain = NULL;
1323 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1324 d_printf("No DNS domain configured for %s. "
1325 "Unable to perform DNS Update.\n", machine_name);
1326 status = NT_STATUS_INVALID_PARAMETER;
1331 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1332 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1333 /* Child domains often do not have NS records. Look
1334 for the NS record for the forest root domain
1335 (rootDomainNamingContext in therootDSE) */
1337 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1338 LDAPMessage *msg = NULL;
1340 ADS_STATUS ads_status;
1343 ads_status = ads_connect( ads );
1344 if ( !ADS_ERR_OK(ads_status) ) {
1345 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1350 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1351 "(objectclass=*)", rootname_attrs, &msg);
1352 if (!ADS_ERR_OK(ads_status)) {
1356 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1358 ads_msgfree( ads, msg );
1362 root_domain = ads_build_domain( root_dn );
1365 ads_msgfree( ads, msg );
1367 /* try again for NS servers */
1369 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1371 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1372 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1373 "realm\n", ads->config.realm));
1377 dnsdomain = root_domain;
1381 /* Now perform the dns update - we'll try non-secure and if we fail,
1382 we'll follow it up with a secure update */
1384 fstrcpy( dns_server, nameservers[0].hostname );
1386 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1387 if (!ERR_DNS_IS_OK(dns_err)) {
1388 status = NT_STATUS_UNSUCCESSFUL;
1393 SAFE_FREE( root_domain );
1398 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1401 struct in_addr *iplist = NULL;
1402 fstring machine_name;
1405 name_to_fqdn( machine_name, global_myname() );
1406 strlower_m( machine_name );
1408 /* Get our ip address (not the 127.0.0.x address but a real ip
1411 num_addrs = get_my_ip_address( &iplist );
1412 if ( num_addrs <= 0 ) {
1413 DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
1415 return NT_STATUS_INVALID_PARAMETER;
1418 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1420 SAFE_FREE( iplist );
1426 /*******************************************************************
1427 utility function to parse an integer parameter from
1429 **********************************************************/
1430 static char* get_string_param( const char* param )
1434 if ( (p = strchr( param, '=' )) == NULL )
1440 /*******************************************************************
1441 ********************************************************************/
1443 static int net_ads_join_usage(int argc, const char **argv)
1445 d_printf("net ads join [options]\n");
1446 d_printf("Valid options:\n");
1447 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1448 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1449 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1450 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1451 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1452 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1453 d_printf(" need to be doubled or even quadrupled. It is not used as a separator");
1458 /*******************************************************************
1459 ********************************************************************/
1461 int net_ads_join(int argc, const char **argv)
1463 ADS_STRUCT *ads = NULL;
1466 char *machine_account = NULL;
1467 char *short_domain_name = NULL;
1468 char *tmp_password, *password;
1469 TALLOC_CTX *ctx = NULL;
1470 DOM_SID *domain_sid = NULL;
1471 BOOL createupn = False;
1472 const char *machineupn = NULL;
1473 const char *create_in_ou = NULL;
1476 struct in_addr dcip;
1477 const char *os_name = NULL;
1478 const char *os_version = NULL;
1480 nt_status = check_ads_config();
1481 if (!NT_STATUS_IS_OK(nt_status)) {
1482 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1486 /* find a DC to initialize the server affinity cache */
1488 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
1490 status = ads_startup(True, &ads);
1491 if (!ADS_ERR_OK(status)) {
1492 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1493 nt_status = ads_ntstatus(status);
1497 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1498 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1499 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1500 dyn_CONFIGFILE, lp_realm());
1501 nt_status = NT_STATUS_INVALID_PARAMETER;
1505 if (!(ctx = talloc_init("net_ads_join"))) {
1506 d_fprintf(stderr, "Could not initialise talloc context.\n");
1507 nt_status = NT_STATUS_NO_MEMORY;
1511 /* process additional command line args */
1513 for ( i=0; i<argc; i++ ) {
1514 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1516 machineupn = get_string_param(argv[i]);
1518 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1519 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1520 d_fprintf(stderr, "Please supply a valid OU path.\n");
1521 nt_status = NT_STATUS_INVALID_PARAMETER;
1525 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1526 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1527 d_fprintf(stderr, "Please supply a operating system name.\n");
1528 nt_status = NT_STATUS_INVALID_PARAMETER;
1532 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1533 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1534 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1535 nt_status = NT_STATUS_INVALID_PARAMETER;
1540 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1541 nt_status = NT_STATUS_INVALID_PARAMETER;
1546 /* If we were given an OU, try to create the machine in
1547 the OU account first and then do the normal RPC join */
1549 if ( create_in_ou ) {
1550 status = net_precreate_machine_acct( ads, create_in_ou );
1551 if ( !ADS_ERR_OK(status) ) {
1552 d_fprintf( stderr, "Failed to pre-create the machine object "
1553 "in OU %s.\n", create_in_ou);
1554 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1555 ads_errstr(status)));
1556 nt_status = ads_ntstatus(status);
1561 /* Do the domain join here */
1563 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1564 password = talloc_strdup(ctx, tmp_password);
1566 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1567 &ads->ldap_ip, &short_domain_name, &domain_sid, password);
1568 if ( !NT_STATUS_IS_OK(nt_status) ) {
1569 DEBUG(1, ("call of net_join_domain failed: %s\n",
1570 get_friendly_nt_error_msg(nt_status)));
1574 /* Check the short name of the domain */
1576 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1577 d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
1578 d_printf("domain name obtained from the server.\n");
1579 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1580 d_printf("You should set \"workgroup = %s\" in %s.\n",
1581 short_domain_name, dyn_CONFIGFILE);
1584 d_printf("Using short domain name -- %s\n", short_domain_name);
1586 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1587 value from smb.conf and the string returned from the server. The former is
1588 neede to bootstrap winbindd's first connection to the DC to get the real
1589 short domain name --jerry */
1591 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1592 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1594 /* issue an internal error here for now.
1595 * everything else would mean changing tdb routines. */
1596 nt_status = NT_STATUS_INTERNAL_ERROR;
1600 /* Verify that everything is ok */
1602 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1603 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1607 /* create the dNSHostName & servicePrincipalName values */
1609 status = net_set_machine_spn( ctx, ads );
1610 if ( !ADS_ERR_OK(status) ) {
1612 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1613 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1614 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1616 /* Disable the machine account in AD. Better to fail than to leave
1617 a confused admin. */
1619 if ( net_ads_leave( 0, NULL ) != 0 ) {
1620 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1623 /* clear out the machine password */
1625 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1626 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1628 nt_status = ads_ntstatus(status);
1632 if ( !net_derive_salting_principal( ctx, ads ) ) {
1633 DEBUG(1,("Failed to determine salting principal\n"));
1640 /* default to using the short UPN name */
1641 if ( !machineupn ) {
1642 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1643 ads->config.realm );
1647 status = net_set_machine_upn( ctx, ads, machineupn );
1648 if ( !ADS_ERR_OK(status) ) {
1649 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1653 /* Try to set the operatingSystem attributes if asked */
1655 if ( os_name && os_version ) {
1656 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1657 if ( !ADS_ERR_OK(status) ) {
1658 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1659 "Are you a Domain Admin?\n");
1663 /* Now build the keytab, using the same ADS connection */
1665 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1666 DEBUG(1,("Error creating host keytab!\n"));
1669 #if defined(WITH_DNS_UPDATES)
1670 /* We enter this block with user creds */
1671 ads_kdestroy( NULL );
1675 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1676 /* kinit with the machine password */
1678 use_in_memory_ccache();
1679 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1680 ads->auth.password = secrets_fetch_machine_password(
1681 lp_workgroup(), NULL, NULL );
1682 ads->auth.realm = SMB_STRDUP( lp_realm() );
1683 ads_kinit_password( ads );
1686 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1687 d_fprintf( stderr, "DNS update failed!\n" );
1690 /* exit from this block using machine creds */
1693 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1695 SAFE_FREE(machine_account);
1702 /* issue an overall failure message at the end. */
1703 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1705 SAFE_FREE(machine_account);
1713 /*******************************************************************
1714 ********************************************************************/
1716 static int net_ads_dns_usage(int argc, const char **argv)
1718 #if defined(WITH_DNS_UPDATES)
1719 d_printf("net ads dns <command>\n");
1720 d_printf("Valid commands:\n");
1721 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1725 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1730 /*******************************************************************
1731 ********************************************************************/
1733 static int net_ads_dns_register(int argc, const char **argv)
1735 #if defined(WITH_DNS_UPDATES)
1741 talloc_enable_leak_report();
1745 d_fprintf(stderr, "net ads dns register <name> <ip>\n");
1749 if (!(ctx = talloc_init("net_ads_dns"))) {
1750 d_fprintf(stderr, "Could not initialise talloc context\n");
1754 status = ads_startup(True, &ads);
1755 if ( !ADS_ERR_OK(status) ) {
1756 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1761 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1762 d_fprintf( stderr, "DNS update failed!\n" );
1763 ads_destroy( &ads );
1768 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1775 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1780 #if defined(WITH_DNS_UPDATES)
1781 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1784 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1786 #if defined(WITH_DNS_UPDATES)
1790 talloc_enable_leak_report();
1794 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1799 err = do_gethostbyname(argv[0], argv[1]);
1801 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1806 static int net_ads_dns(int argc, const char *argv[])
1808 struct functable func[] = {
1809 {"REGISTER", net_ads_dns_register},
1810 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1814 return net_run_function(argc, argv, func, net_ads_dns_usage);
1817 /*******************************************************************
1818 ********************************************************************/
1820 int net_ads_printer_usage(int argc, const char **argv)
1823 "\nnet ads printer search <printer>"
1824 "\n\tsearch for a printer in the directory\n"
1825 "\nnet ads printer info <printer> <server>"
1826 "\n\tlookup info in directory for printer on server"
1827 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1828 "\nnet ads printer publish <printername>"
1829 "\n\tpublish printer in directory"
1830 "\n\t(note: printer name is required)\n"
1831 "\nnet ads printer remove <printername>"
1832 "\n\tremove printer from directory"
1833 "\n\t(note: printer name is required)\n");
1837 /*******************************************************************
1838 ********************************************************************/
1840 static int net_ads_printer_search(int argc, const char **argv)
1844 LDAPMessage *res = NULL;
1846 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1850 rc = ads_find_printers(ads, &res);
1852 if (!ADS_ERR_OK(rc)) {
1853 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1854 ads_msgfree(ads, res);
1859 if (ads_count_replies(ads, res) == 0) {
1860 d_fprintf(stderr, "No results found\n");
1861 ads_msgfree(ads, res);
1867 ads_msgfree(ads, res);
1872 static int net_ads_printer_info(int argc, const char **argv)
1876 const char *servername, *printername;
1877 LDAPMessage *res = NULL;
1879 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1884 printername = argv[0];
1890 servername = argv[1];
1892 servername = global_myname();
1895 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1897 if (!ADS_ERR_OK(rc)) {
1898 d_fprintf(stderr, "Server '%s' not found: %s\n",
1899 servername, ads_errstr(rc));
1900 ads_msgfree(ads, res);
1905 if (ads_count_replies(ads, res) == 0) {
1906 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1907 ads_msgfree(ads, res);
1913 ads_msgfree(ads, res);
1919 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1920 void *buf, size_t len, void *private_data)
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->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_machine_password();
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 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"\
2438 static int net_ads_keytab_flush(int argc, const char **argv)
2443 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2446 ret = ads_keytab_flush(ads);
2451 static int net_ads_keytab_add(int argc, const char **argv)
2457 d_printf("Processing principals to add...\n");
2458 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2461 for (i = 0; i < argc; i++) {
2462 ret |= ads_keytab_add_entry(ads, argv[i]);
2468 static int net_ads_keytab_create(int argc, const char **argv)
2473 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2476 ret = ads_keytab_create_default(ads);
2481 static int net_ads_keytab_list(int argc, const char **argv)
2483 return ads_keytab_list();
2487 int net_ads_keytab(int argc, const char **argv)
2489 struct functable func[] = {
2490 {"CREATE", net_ads_keytab_create},
2491 {"ADD", net_ads_keytab_add},
2492 {"FLUSH", net_ads_keytab_flush},
2493 {"HELP", net_ads_keytab_usage},
2494 {"LIST", net_ads_keytab_list},
2498 if (!lp_use_kerberos_keytab()) {
2499 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2500 use keytab functions.\n");
2503 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2506 int net_ads_help(int argc, const char **argv)
2508 struct functable func[] = {
2509 {"USER", net_ads_user_usage},
2510 {"GROUP", net_ads_group_usage},
2511 {"PRINTER", net_ads_printer_usage},
2512 {"SEARCH", net_ads_search_usage},
2513 {"INFO", net_ads_info},
2514 {"JOIN", net_ads_join_usage},
2515 {"DNS", net_ads_dns_usage},
2516 {"LEAVE", net_ads_leave},
2517 {"STATUS", net_ads_status},
2518 {"PASSWORD", net_ads_password},
2519 {"CHANGETRUSTPW", net_ads_changetrustpw},
2523 return net_run_function(argc, argv, func, net_ads_usage);
2526 int net_ads(int argc, const char **argv)
2528 struct functable func[] = {
2529 {"INFO", net_ads_info},
2530 {"JOIN", net_ads_join},
2531 {"TESTJOIN", net_ads_testjoin},
2532 {"LEAVE", net_ads_leave},
2533 {"STATUS", net_ads_status},
2534 {"USER", net_ads_user},
2535 {"GROUP", net_ads_group},
2536 {"DNS", net_ads_dns},
2537 {"PASSWORD", net_ads_password},
2538 {"CHANGETRUSTPW", net_ads_changetrustpw},
2539 {"PRINTER", net_ads_printer},
2540 {"SEARCH", net_ads_search},
2542 {"SID", net_ads_sid},
2543 {"WORKGROUP", net_ads_workgroup},
2544 {"LOOKUP", net_ads_lookup},
2545 {"KEYTAB", net_ads_keytab},
2546 {"GPO", net_ads_gpo},
2547 {"HELP", net_ads_help},
2551 return net_run_function(argc, argv, func, net_ads_usage);
2556 static int net_ads_noads(void)
2558 d_fprintf(stderr, "ADS support not compiled in\n");
2562 int net_ads_keytab(int argc, const char **argv)
2564 return net_ads_noads();
2567 int net_ads_usage(int argc, const char **argv)
2569 return net_ads_noads();
2572 int net_ads_help(int argc, const char **argv)
2574 return net_ads_noads();
2577 int net_ads_changetrustpw(int argc, const char **argv)
2579 return net_ads_noads();
2582 int net_ads_join(int argc, const char **argv)
2584 return net_ads_noads();
2587 int net_ads_user(int argc, const char **argv)
2589 return net_ads_noads();
2592 int net_ads_group(int argc, const char **argv)
2594 return net_ads_noads();
2597 /* this one shouldn't display a message */
2598 int net_ads_check(void)
2603 int net_ads_check_our_domain(void)
2608 int net_ads(int argc, const char **argv)
2610 return net_ads_usage(argc, argv);
2613 #endif /* WITH_ADS */