2 Samba Unix/Linux SMB client library
4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5 Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6 Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
7 Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "utils/net.h"
26 #include "libnet/libnet.h"
30 int net_ads_usage(int argc
, const char **argv
)
32 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
33 d_printf(" Join the local machine to a ADS realm\n");
35 d_printf(" Remove the local machine from a ADS realm\n");
36 d_printf("testjoin\n");
37 d_printf(" Validates the machine account in the domain\n");
39 d_printf(" List, add, or delete users in the realm\n");
41 d_printf(" List, add, or delete groups in the realm\n");
43 d_printf(" Displays details regarding a specific AD server\n");
45 d_printf(" Display details regarding the machine's account in AD\n");
47 d_printf(" Performs CLDAP query of AD domain controllers\n");
48 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
49 d_printf(" Change a user's password using an admin account\n");
50 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
51 d_printf("changetrustpw\n");
52 d_printf(" Change the trust account password of this machine in the AD tree\n");
53 d_printf("printer [info | publish | remove] <printername> <servername>\n");
54 d_printf(" Lookup, add, or remove directory entry for a printer\n");
55 d_printf("{search,dn,sid}\n");
56 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
58 d_printf(" Manage a local keytab file based on the machine account in AD\n");
60 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
61 d_printf(" (using the machine credentials)\n");
66 /* when we do not have sufficient input parameters to contact a remote domain
67 * we always fall back to our own realm - Guenther*/
69 static const char *assume_own_realm(void)
71 if (!opt_host
&& strequal(lp_workgroup(), opt_target_workgroup
)) {
79 do a cldap netlogon query
81 static int net_ads_cldap_netlogon(ADS_STRUCT
*ads
)
83 char addr
[INET6_ADDRSTRLEN
];
84 struct cldap_netlogon_reply reply
;
87 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
88 if ( !ads_cldap_netlogon(addr
, ads
->server
.realm
, &reply
) ) {
89 d_fprintf(stderr
, "CLDAP query failed!\n");
93 d_printf("Information for Domain Controller: %s\n\n",
96 d_printf("Response Type: ");
98 case SAMLOGON_AD_UNK_R
:
99 d_printf("SAMLOGON\n");
102 d_printf("SAMLOGON_USER\n");
105 d_printf("0x%x\n", reply
.type
);
109 smb_uuid_unpack(reply
.guid
, &tmp_guid
);
110 d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid
));
114 "\tIs a GC of the forest: %s\n"
115 "\tIs an LDAP server: %s\n"
116 "\tSupports DS: %s\n"
117 "\tIs running a KDC: %s\n"
118 "\tIs running time services: %s\n"
119 "\tIs the closest DC: %s\n"
120 "\tIs writable: %s\n"
121 "\tHas a hardware clock: %s\n"
122 "\tIs a non-domain NC serviced by LDAP server: %s\n",
123 (reply
.flags
& ADS_PDC
) ? "yes" : "no",
124 (reply
.flags
& ADS_GC
) ? "yes" : "no",
125 (reply
.flags
& ADS_LDAP
) ? "yes" : "no",
126 (reply
.flags
& ADS_DS
) ? "yes" : "no",
127 (reply
.flags
& ADS_KDC
) ? "yes" : "no",
128 (reply
.flags
& ADS_TIMESERV
) ? "yes" : "no",
129 (reply
.flags
& ADS_CLOSEST
) ? "yes" : "no",
130 (reply
.flags
& ADS_WRITABLE
) ? "yes" : "no",
131 (reply
.flags
& ADS_GOOD_TIMESERV
) ? "yes" : "no",
132 (reply
.flags
& ADS_NDNC
) ? "yes" : "no");
134 printf("Forest:\t\t\t%s\n", reply
.forest
);
135 printf("Domain:\t\t\t%s\n", reply
.domain
);
136 printf("Domain Controller:\t%s\n", reply
.hostname
);
138 printf("Pre-Win2k Domain:\t%s\n", reply
.netbios_domain
);
139 printf("Pre-Win2k Hostname:\t%s\n", reply
.netbios_hostname
);
141 if (*reply
.unk
) printf("Unk:\t\t\t%s\n", reply
.unk
);
142 if (*reply
.user_name
) printf("User name:\t%s\n", reply
.user_name
);
144 printf("Server Site Name :\t\t%s\n", reply
.server_site_name
);
145 printf("Client Site Name :\t\t%s\n", reply
.client_site_name
);
147 d_printf("NT Version: %d\n", reply
.version
);
148 d_printf("LMNT Token: %.2x\n", reply
.lmnt_token
);
149 d_printf("LM20 Token: %.2x\n", reply
.lm20_token
);
155 this implements the CLDAP based netlogon lookup requests
156 for finding the domain controller of a ADS domain
158 static int net_ads_lookup(int argc
, const char **argv
)
162 if (!ADS_ERR_OK(ads_startup_nobind(False
, &ads
))) {
163 d_fprintf(stderr
, "Didn't find the cldap server!\n");
167 if (!ads
->config
.realm
) {
168 ads
->config
.realm
= CONST_DISCARD(char *, opt_target_workgroup
);
169 ads
->ldap
.port
= 389;
172 return net_ads_cldap_netlogon(ads
);
177 static int net_ads_info(int argc
, const char **argv
)
180 char addr
[INET6_ADDRSTRLEN
];
182 if (!ADS_ERR_OK(ads_startup_nobind(False
, &ads
))) {
183 d_fprintf(stderr
, "Didn't find the ldap server!\n");
187 if (!ads
|| !ads
->config
.realm
) {
188 d_fprintf(stderr
, "Didn't find the ldap server!\n");
192 /* Try to set the server's current time since we didn't do a full
193 TCP LDAP session initially */
195 if ( !ADS_ERR_OK(ads_current_time( ads
)) ) {
196 d_fprintf( stderr
, "Failed to get server's current time!\n");
199 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
201 d_printf("LDAP server: %s\n", addr
);
202 d_printf("LDAP server name: %s\n", ads
->config
.ldap_server_name
);
203 d_printf("Realm: %s\n", ads
->config
.realm
);
204 d_printf("Bind Path: %s\n", ads
->config
.bind_path
);
205 d_printf("LDAP port: %d\n", ads
->ldap
.port
);
206 d_printf("Server time: %s\n", http_timestring(ads
->config
.current_time
));
208 d_printf("KDC server: %s\n", ads
->auth
.kdc_server
);
209 d_printf("Server time offset: %d\n", ads
->auth
.time_offset
);
214 static void use_in_memory_ccache(void) {
215 /* Use in-memory credentials cache so we do not interfere with
216 * existing credentials */
217 setenv(KRB5_ENV_CCNAME
, "MEMORY:net_ads", 1);
220 static ADS_STATUS
ads_startup_int(bool only_own_domain
, uint32 auth_flags
, ADS_STRUCT
**ads_ret
)
222 ADS_STRUCT
*ads
= NULL
;
224 bool need_password
= False
;
225 bool second_time
= False
;
227 const char *realm
= NULL
;
228 bool tried_closest_dc
= False
;
230 /* lp_realm() should be handled by a command line param,
231 However, the join requires that realm be set in smb.conf
232 and compares our realm with the remote server's so this is
233 ok until someone needs more flexibility */
238 if (only_own_domain
) {
241 realm
= assume_own_realm();
244 ads
= ads_init(realm
, opt_target_workgroup
, opt_host
);
246 if (!opt_user_name
) {
247 opt_user_name
= "administrator";
250 if (opt_user_specified
) {
251 need_password
= True
;
255 if (!opt_password
&& need_password
&& !opt_machine_pass
) {
256 opt_password
= net_prompt_pass(opt_user_name
);
259 return ADS_ERROR(LDAP_NO_MEMORY
);
264 use_in_memory_ccache();
265 SAFE_FREE(ads
->auth
.password
);
266 ads
->auth
.password
= smb_xstrdup(opt_password
);
269 ads
->auth
.flags
|= auth_flags
;
270 SAFE_FREE(ads
->auth
.user_name
);
271 ads
->auth
.user_name
= smb_xstrdup(opt_user_name
);
274 * If the username is of the form "name@realm",
275 * extract the realm and convert to upper case.
276 * This is only used to establish the connection.
278 if ((cp
= strchr_m(ads
->auth
.user_name
, '@'))!=0) {
280 SAFE_FREE(ads
->auth
.realm
);
281 ads
->auth
.realm
= smb_xstrdup(cp
);
282 strupper_m(ads
->auth
.realm
);
285 status
= ads_connect(ads
);
287 if (!ADS_ERR_OK(status
)) {
289 if (NT_STATUS_EQUAL(ads_ntstatus(status
),
290 NT_STATUS_NO_LOGON_SERVERS
)) {
291 DEBUG(0,("ads_connect: %s\n", ads_errstr(status
)));
296 if (!need_password
&& !second_time
&& !(auth_flags
& ADS_AUTH_NO_BIND
)) {
297 need_password
= True
;
306 /* when contacting our own domain, make sure we use the closest DC.
307 * This is done by reconnecting to ADS because only the first call to
308 * ads_connect will give us our own sitename */
310 if ((only_own_domain
|| !opt_host
) && !tried_closest_dc
) {
312 tried_closest_dc
= True
; /* avoid loop */
314 if (!ads
->config
.tried_closest_dc
) {
316 namecache_delete(ads
->server
.realm
, 0x1C);
317 namecache_delete(ads
->server
.workgroup
, 0x1C);
330 ADS_STATUS
ads_startup(bool only_own_domain
, ADS_STRUCT
**ads
)
332 return ads_startup_int(only_own_domain
, 0, ads
);
335 ADS_STATUS
ads_startup_nobind(bool only_own_domain
, ADS_STRUCT
**ads
)
337 return ads_startup_int(only_own_domain
, ADS_AUTH_NO_BIND
, ads
);
341 Check to see if connection can be made via ads.
342 ads_startup() stores the password in opt_password if it needs to so
343 that rpc or rap can use it without re-prompting.
345 static int net_ads_check_int(const char *realm
, const char *workgroup
, const char *host
)
350 if ( (ads
= ads_init( realm
, workgroup
, host
)) == NULL
) {
354 ads
->auth
.flags
|= ADS_AUTH_NO_BIND
;
356 status
= ads_connect(ads
);
357 if ( !ADS_ERR_OK(status
) ) {
365 int net_ads_check_our_domain(void)
367 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL
);
370 int net_ads_check(void)
372 return net_ads_check_int(NULL
, opt_workgroup
, opt_host
);
376 determine the netbios workgroup name for a domain
378 static int net_ads_workgroup(int argc
, const char **argv
)
381 char addr
[INET6_ADDRSTRLEN
];
382 struct cldap_netlogon_reply reply
;
384 if (!ADS_ERR_OK(ads_startup_nobind(False
, &ads
))) {
385 d_fprintf(stderr
, "Didn't find the cldap server!\n");
389 if (!ads
->config
.realm
) {
390 ads
->config
.realm
= CONST_DISCARD(char *, opt_target_workgroup
);
391 ads
->ldap
.port
= 389;
394 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
395 if ( !ads_cldap_netlogon(addr
, ads
->server
.realm
, &reply
) ) {
396 d_fprintf(stderr
, "CLDAP query failed!\n");
400 d_printf("Workgroup: %s\n", reply
.netbios_domain
);
409 static bool usergrp_display(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
411 char **disp_fields
= (char **) data_area
;
413 if (!field
) { /* must be end of record */
414 if (disp_fields
[0]) {
415 if (!strchr_m(disp_fields
[0], '$')) {
417 d_printf("%-21.21s %s\n",
418 disp_fields
[0], disp_fields
[1]);
420 d_printf("%s\n", disp_fields
[0]);
423 SAFE_FREE(disp_fields
[0]);
424 SAFE_FREE(disp_fields
[1]);
427 if (!values
) /* must be new field, indicate string field */
429 if (StrCaseCmp(field
, "sAMAccountName") == 0) {
430 disp_fields
[0] = SMB_STRDUP((char *) values
[0]);
432 if (StrCaseCmp(field
, "description") == 0)
433 disp_fields
[1] = SMB_STRDUP((char *) values
[0]);
437 static int net_ads_user_usage(int argc
, const char **argv
)
439 return net_help_user(argc
, argv
);
442 static int ads_user_add(int argc
, const char **argv
)
447 LDAPMessage
*res
=NULL
;
451 if (argc
< 1) return net_ads_user_usage(argc
, argv
);
453 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
457 status
= ads_find_user_acct(ads
, &res
, argv
[0]);
459 if (!ADS_ERR_OK(status
)) {
460 d_fprintf(stderr
, "ads_user_add: %s\n", ads_errstr(status
));
464 if (ads_count_replies(ads
, res
)) {
465 d_fprintf(stderr
, "ads_user_add: User %s already exists\n", argv
[0]);
470 ou_str
= SMB_STRDUP(opt_container
);
472 ou_str
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_USERS
);
475 status
= ads_add_user_acct(ads
, argv
[0], ou_str
, opt_comment
);
477 if (!ADS_ERR_OK(status
)) {
478 d_fprintf(stderr
, "Could not add user %s: %s\n", argv
[0],
483 /* if no password is to be set, we're done */
485 d_printf("User %s added\n", argv
[0]);
490 /* try setting the password */
491 asprintf(&upn
, "%s@%s", argv
[0], ads
->config
.realm
);
492 status
= ads_krb5_set_password(ads
->auth
.kdc_server
, upn
, argv
[1],
493 ads
->auth
.time_offset
);
495 if (ADS_ERR_OK(status
)) {
496 d_printf("User %s added\n", argv
[0]);
501 /* password didn't set, delete account */
502 d_fprintf(stderr
, "Could not add user %s. Error setting password %s\n",
503 argv
[0], ads_errstr(status
));
504 ads_msgfree(ads
, res
);
505 status
=ads_find_user_acct(ads
, &res
, argv
[0]);
506 if (ADS_ERR_OK(status
)) {
507 userdn
= ads_get_dn(ads
, res
);
508 ads_del_dn(ads
, userdn
);
509 ads_memfree(ads
, userdn
);
514 ads_msgfree(ads
, res
);
520 static int ads_user_info(int argc
, const char **argv
)
525 const char *attrs
[] = {"memberOf", NULL
};
526 char *searchstring
=NULL
;
531 return net_ads_user_usage(argc
, argv
);
534 escaped_user
= escape_ldap_string_alloc(argv
[0]);
537 d_fprintf(stderr
, "ads_user_info: failed to escape user %s\n", argv
[0]);
541 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
542 SAFE_FREE(escaped_user
);
546 asprintf(&searchstring
, "(sAMAccountName=%s)", escaped_user
);
547 rc
= ads_search(ads
, &res
, searchstring
, attrs
);
548 safe_free(searchstring
);
550 if (!ADS_ERR_OK(rc
)) {
551 d_fprintf(stderr
, "ads_search: %s\n", ads_errstr(rc
));
553 SAFE_FREE(escaped_user
);
557 grouplist
= ldap_get_values((LDAP
*)ads
->ldap
.ld
,
558 (LDAPMessage
*)res
, "memberOf");
563 for (i
=0;grouplist
[i
];i
++) {
564 groupname
= ldap_explode_dn(grouplist
[i
], 1);
565 d_printf("%s\n", groupname
[0]);
566 ldap_value_free(groupname
);
568 ldap_value_free(grouplist
);
571 ads_msgfree(ads
, res
);
573 SAFE_FREE(escaped_user
);
577 static int ads_user_delete(int argc
, const char **argv
)
581 LDAPMessage
*res
= NULL
;
585 return net_ads_user_usage(argc
, argv
);
588 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
592 rc
= ads_find_user_acct(ads
, &res
, argv
[0]);
593 if (!ADS_ERR_OK(rc
) || ads_count_replies(ads
, res
) != 1) {
594 d_printf("User %s does not exist.\n", argv
[0]);
595 ads_msgfree(ads
, res
);
599 userdn
= ads_get_dn(ads
, res
);
600 ads_msgfree(ads
, res
);
601 rc
= ads_del_dn(ads
, userdn
);
602 ads_memfree(ads
, userdn
);
603 if (ADS_ERR_OK(rc
)) {
604 d_printf("User %s deleted\n", argv
[0]);
608 d_fprintf(stderr
, "Error deleting user %s: %s\n", argv
[0],
614 int net_ads_user(int argc
, const char **argv
)
616 struct functable func
[] = {
617 {"ADD", ads_user_add
},
618 {"INFO", ads_user_info
},
619 {"DELETE", ads_user_delete
},
624 const char *shortattrs
[] = {"sAMAccountName", NULL
};
625 const char *longattrs
[] = {"sAMAccountName", "description", NULL
};
626 char *disp_fields
[2] = {NULL
, NULL
};
629 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
633 if (opt_long_list_entries
)
634 d_printf("\nUser name Comment"\
635 "\n-----------------------------\n");
637 rc
= ads_do_search_all_fn(ads
, ads
->config
.bind_path
,
639 "(objectCategory=user)",
640 opt_long_list_entries
? longattrs
:
641 shortattrs
, usergrp_display
,
644 return ADS_ERR_OK(rc
) ? 0 : -1;
647 return net_run_function(argc
, argv
, func
, net_ads_user_usage
);
650 static int net_ads_group_usage(int argc
, const char **argv
)
652 return net_help_group(argc
, argv
);
655 static int ads_group_add(int argc
, const char **argv
)
659 LDAPMessage
*res
=NULL
;
664 return net_ads_group_usage(argc
, argv
);
667 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
671 status
= ads_find_user_acct(ads
, &res
, argv
[0]);
673 if (!ADS_ERR_OK(status
)) {
674 d_fprintf(stderr
, "ads_group_add: %s\n", ads_errstr(status
));
678 if (ads_count_replies(ads
, res
)) {
679 d_fprintf(stderr
, "ads_group_add: Group %s already exists\n", argv
[0]);
684 ou_str
= SMB_STRDUP(opt_container
);
686 ou_str
= ads_default_ou_string(ads
, WELL_KNOWN_GUID_USERS
);
689 status
= ads_add_group_acct(ads
, argv
[0], ou_str
, opt_comment
);
691 if (ADS_ERR_OK(status
)) {
692 d_printf("Group %s added\n", argv
[0]);
695 d_fprintf(stderr
, "Could not add group %s: %s\n", argv
[0],
701 ads_msgfree(ads
, res
);
707 static int ads_group_delete(int argc
, const char **argv
)
711 LDAPMessage
*res
= NULL
;
715 return net_ads_group_usage(argc
, argv
);
718 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
722 rc
= ads_find_user_acct(ads
, &res
, argv
[0]);
723 if (!ADS_ERR_OK(rc
) || ads_count_replies(ads
, res
) != 1) {
724 d_printf("Group %s does not exist.\n", argv
[0]);
725 ads_msgfree(ads
, res
);
729 groupdn
= ads_get_dn(ads
, res
);
730 ads_msgfree(ads
, res
);
731 rc
= ads_del_dn(ads
, groupdn
);
732 ads_memfree(ads
, groupdn
);
733 if (ADS_ERR_OK(rc
)) {
734 d_printf("Group %s deleted\n", argv
[0]);
738 d_fprintf(stderr
, "Error deleting group %s: %s\n", argv
[0],
744 int net_ads_group(int argc
, const char **argv
)
746 struct functable func
[] = {
747 {"ADD", ads_group_add
},
748 {"DELETE", ads_group_delete
},
753 const char *shortattrs
[] = {"sAMAccountName", NULL
};
754 const char *longattrs
[] = {"sAMAccountName", "description", NULL
};
755 char *disp_fields
[2] = {NULL
, NULL
};
758 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
762 if (opt_long_list_entries
)
763 d_printf("\nGroup name Comment"\
764 "\n-----------------------------\n");
765 rc
= ads_do_search_all_fn(ads
, ads
->config
.bind_path
,
767 "(objectCategory=group)",
768 opt_long_list_entries
? longattrs
:
769 shortattrs
, usergrp_display
,
773 return ADS_ERR_OK(rc
) ? 0 : -1;
775 return net_run_function(argc
, argv
, func
, net_ads_group_usage
);
778 static int net_ads_status(int argc
, const char **argv
)
784 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
788 rc
= ads_find_machine_acct(ads
, &res
, global_myname());
789 if (!ADS_ERR_OK(rc
)) {
790 d_fprintf(stderr
, "ads_find_machine_acct: %s\n", ads_errstr(rc
));
795 if (ads_count_replies(ads
, res
) == 0) {
796 d_fprintf(stderr
, "No machine account for '%s' found\n", global_myname());
806 /*******************************************************************
807 Leave an AD domain. Windows XP disables the machine account.
808 We'll try the same. The old code would do an LDAP delete.
809 That only worked using the machine creds because added the machine
810 with full control to the computer object's ACL.
811 *******************************************************************/
813 static int net_ads_leave(int argc
, const char **argv
)
816 struct libnet_UnjoinCtx
*r
= NULL
;
819 if (!(ctx
= talloc_init("net_ads_leave"))) {
820 d_fprintf(stderr
, "Could not initialise talloc context.\n");
824 use_in_memory_ccache();
826 werr
= libnet_init_UnjoinCtx(ctx
, &r
);
827 if (!W_ERROR_IS_OK(werr
)) {
828 d_fprintf(stderr
, "Could not initialise unjoin context.\n");
832 r
->in
.debug
= opt_verbose
;
833 r
->in
.dc_name
= opt_host
;
834 r
->in
.domain_name
= lp_realm();
835 r
->in
.admin_account
= opt_user_name
;
836 r
->in
.admin_password
= net_prompt_pass(opt_user_name
);
837 r
->in
.unjoin_flags
= WKSSVC_JOIN_FLAGS_JOIN_TYPE
|
838 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE
;
840 werr
= libnet_Unjoin(ctx
, r
);
841 if (!W_ERROR_IS_OK(werr
)) {
842 d_printf("Failed to leave domain: %s\n",
843 r
->out
.error_string
? r
->out
.error_string
:
844 get_friendly_werror_msg(werr
));
848 if (W_ERROR_IS_OK(werr
)) {
849 d_printf("Deleted account for '%s' in realm '%s'\n",
850 r
->in
.machine_name
, r
->out
.dns_domain_name
);
854 /* We couldn't delete it - see if the disable succeeded. */
855 if (r
->out
.disabled_machine_account
) {
856 d_printf("Disabled account for '%s' in realm '%s'\n",
857 r
->in
.machine_name
, r
->out
.dns_domain_name
);
862 d_fprintf(stderr
, "Failed to disable machine account for '%s' in realm '%s'\n",
863 r
->in
.machine_name
, r
->out
.dns_domain_name
);
869 if (W_ERROR_IS_OK(werr
)) {
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", get_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 sockaddr_storage
*pss
,
958 const char *password
)
960 NTSTATUS ret
= NT_STATUS_UNSUCCESSFUL
;
961 struct cli_state
*cli
= NULL
;
963 ret
= connect_to_ipc_krb5(&cli
, pss
, servername
);
964 if ( !NT_STATUS_IS_OK(ret
) ) {
968 ret
= netdom_get_domain_sid( ctx
, cli
, domain
, dom_sid
);
969 if ( !NT_STATUS_IS_OK(ret
) ) {
973 /* cli->server_domain is not filled in when using krb5
976 saf_store( *domain
, cli
->desthost
);
978 ret
= netdom_join_domain( ctx
, cli
, *dom_sid
, password
, ND_TYPE_AD
);
987 /*******************************************************************
988 Set a machines dNSHostName and servicePrincipalName attributes
989 ********************************************************************/
991 static ADS_STATUS
net_set_machine_spn(TALLOC_CTX
*ctx
, ADS_STRUCT
*ads_s
)
993 ADS_STATUS status
= ADS_ERROR(LDAP_SERVER_DOWN
);
996 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
999 LDAPMessage
*res
= NULL
;
1000 char *dn_string
= NULL
;
1001 const char *machine_name
= global_myname();
1004 if ( !machine_name
) {
1005 return ADS_ERROR(LDAP_NO_MEMORY
);
1010 status
= ads_find_machine_acct(ads_s
, &res
, machine_name
);
1011 if (!ADS_ERR_OK(status
))
1014 if ( (count
= ads_count_replies(ads_s
, res
)) != 1 ) {
1015 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count
));
1016 return ADS_ERROR(LDAP_NO_MEMORY
);
1019 if ( (dn_string
= ads_get_dn(ads_s
, res
)) == NULL
) {
1020 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1024 new_dn
= talloc_strdup(ctx
, dn_string
);
1025 ads_memfree(ads_s
, dn_string
);
1027 return ADS_ERROR(LDAP_NO_MEMORY
);
1030 /* Windows only creates HOST/shortname & HOST/fqdn. */
1032 if ( !(psp
= talloc_asprintf(ctx
, "HOST/%s", machine_name
)) )
1035 servicePrincipalName
[0] = psp
;
1037 name_to_fqdn(my_fqdn
, machine_name
);
1038 strlower_m(my_fqdn
);
1039 if ( !(psp
= talloc_asprintf(ctx
, "HOST/%s", my_fqdn
)) )
1041 servicePrincipalName
[1] = psp
;
1043 if (!(mods
= ads_init_mods(ctx
))) {
1047 /* fields of primary importance */
1049 ads_mod_str(ctx
, &mods
, "dNSHostName", my_fqdn
);
1050 ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1052 status
= ads_gen_mod(ads_s
, new_dn
, mods
);
1055 ads_msgfree(ads_s
, res
);
1060 /*******************************************************************
1061 Set a machines dNSHostName and servicePrincipalName attributes
1062 ********************************************************************/
1064 static ADS_STATUS
net_set_machine_upn(TALLOC_CTX
*ctx
, ADS_STRUCT
*ads_s
, const char *upn
)
1066 ADS_STATUS status
= ADS_ERROR(LDAP_SERVER_DOWN
);
1069 LDAPMessage
*res
= NULL
;
1070 char *dn_string
= NULL
;
1071 const char *machine_name
= global_myname();
1074 if ( !machine_name
) {
1075 return ADS_ERROR(LDAP_NO_MEMORY
);
1080 status
= ads_find_machine_acct(ads_s
, &res
, machine_name
);
1081 if (!ADS_ERR_OK(status
))
1084 if ( (count
= ads_count_replies(ads_s
, res
)) != 1 ) {
1085 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count
));
1086 return ADS_ERROR(LDAP_NO_MEMORY
);
1089 if ( (dn_string
= ads_get_dn(ads_s
, res
)) == NULL
) {
1090 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1094 new_dn
= talloc_strdup(ctx
, dn_string
);
1095 ads_memfree(ads_s
, dn_string
);
1097 return ADS_ERROR(LDAP_NO_MEMORY
);
1100 /* now do the mods */
1102 if (!(mods
= ads_init_mods(ctx
))) {
1106 /* fields of primary importance */
1108 ads_mod_str(ctx
, &mods
, "userPrincipalName", upn
);
1110 status
= ads_gen_mod(ads_s
, new_dn
, mods
);
1113 ads_msgfree(ads_s
, res
);
1118 /*******************************************************************
1119 Set a machines dNSHostName and servicePrincipalName attributes
1120 ********************************************************************/
1122 static ADS_STATUS
net_set_os_attributes(TALLOC_CTX
*ctx
, ADS_STRUCT
*ads_s
,
1123 const char *os_name
, const char *os_version
)
1125 ADS_STATUS status
= ADS_ERROR(LDAP_SERVER_DOWN
);
1128 LDAPMessage
*res
= NULL
;
1129 char *dn_string
= NULL
;
1130 const char *machine_name
= global_myname();
1134 if ( !os_name
|| !os_version
) {
1135 return ADS_ERROR(LDAP_NO_MEMORY
);
1140 status
= ads_find_machine_acct(ads_s
, &res
, machine_name
);
1141 if (!ADS_ERR_OK(status
))
1144 if ( (count
= ads_count_replies(ads_s
, res
)) != 1 ) {
1145 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count
));
1146 return ADS_ERROR(LDAP_NO_MEMORY
);
1149 if ( (dn_string
= ads_get_dn(ads_s
, res
)) == NULL
) {
1150 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1154 new_dn
= talloc_strdup(ctx
, dn_string
);
1155 ads_memfree(ads_s
, dn_string
);
1157 return ADS_ERROR(LDAP_NO_MEMORY
);
1160 /* now do the mods */
1162 if (!(mods
= ads_init_mods(ctx
))) {
1166 os_sp
= talloc_asprintf( ctx
, "Samba %s", SAMBA_VERSION_STRING
);
1168 /* fields of primary importance */
1170 ads_mod_str(ctx
, &mods
, "operatingSystem", os_name
);
1171 ads_mod_str(ctx
, &mods
, "operatingSystemVersion", os_version
);
1173 ads_mod_str(ctx
, &mods
, "operatingSystemServicePack", os_sp
);
1175 status
= ads_gen_mod(ads_s
, new_dn
, mods
);
1178 ads_msgfree(ads_s
, res
);
1179 TALLOC_FREE( os_sp
);
1184 /*******************************************************************
1185 join a domain using ADS (LDAP mods)
1186 ********************************************************************/
1188 static ADS_STATUS
net_precreate_machine_acct( ADS_STRUCT
*ads
, const char *ou
)
1190 ADS_STATUS rc
= ADS_ERROR(LDAP_SERVER_DOWN
);
1191 char *ou_str
= NULL
;
1193 LDAPMessage
*res
= NULL
;
1196 ou_str
= ads_ou_string(ads
, ou
);
1197 if (asprintf(&dn
, "%s,%s", ou_str
, ads
->config
.bind_path
) == -1) {
1198 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
1202 rc
= ads_search_dn(ads
, &res
, dn
, NULL
);
1203 if (!ADS_ERR_OK(rc
)) {
1204 d_fprintf(stderr
, "The specified OU does not exist.\n");
1208 /* Attempt to create the machine account and bail if this fails.
1209 Assume that the admin wants exactly what they requested */
1211 rc
= ads_create_machine_acct( ads
, global_myname(), dn
);
1212 if (ADS_ERR_OK(rc
)) {
1213 DEBUG(1, ("machine account created\n"));
1216 if ( !(rc
.error_type
== ENUM_ADS_ERROR_LDAP
&& rc
.err
.rc
== LDAP_ALREADY_EXISTS
) ) {
1217 DEBUG(1, ("machine account creation failed\n"));
1221 rc
= ads_move_machine_acct(ads
, global_myname(), dn
, &moved
);
1222 if (!ADS_ERR_OK(rc
)) {
1223 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1228 d_printf("The machine account was moved into the specified OU.\n");
1230 d_printf("The machine account already exists in the specified OU.\n");
1234 ads_msgfree(ads
, res
);
1235 SAFE_FREE( ou_str
);
1241 /************************************************************************
1242 ************************************************************************/
1244 static bool net_derive_salting_principal( TALLOC_CTX
*ctx
, ADS_STRUCT
*ads
)
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
) {
1271 char *upn
= ads_get_upn(ads
, ctx
, machine_name
);
1273 fstrcpy( salt
, upn
);
1277 return kerberos_secrets_store_des_salt( salt
);
1280 /*******************************************************************
1281 Send a DNS update request
1282 *******************************************************************/
1284 #if defined(WITH_DNS_UPDATES)
1286 DNS_ERROR
DoDNSUpdate(char *pszServerName
,
1287 const char *pszDomainName
, const char *pszHostName
,
1288 const struct sockaddr_storage
*sslist
,
1291 static NTSTATUS
net_update_dns_internal(TALLOC_CTX
*ctx
, ADS_STRUCT
*ads
,
1292 const char *machine_name
,
1293 const struct sockaddr_storage
*addrs
,
1296 struct dns_rr_ns
*nameservers
= NULL
;
1298 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
1301 const char *dnsdomain
= NULL
;
1302 char *root_domain
= NULL
;
1304 if ( (dnsdomain
= strchr_m( machine_name
, '.')) == NULL
) {
1305 d_printf("No DNS domain configured for %s. "
1306 "Unable to perform DNS Update.\n", machine_name
);
1307 status
= NT_STATUS_INVALID_PARAMETER
;
1312 status
= ads_dns_lookup_ns( ctx
, dnsdomain
, &nameservers
, &ns_count
);
1313 if ( !NT_STATUS_IS_OK(status
) || (ns_count
== 0)) {
1314 /* Child domains often do not have NS records. Look
1315 for the NS record for the forest root domain
1316 (rootDomainNamingContext in therootDSE) */
1318 const char *rootname_attrs
[] = { "rootDomainNamingContext", NULL
};
1319 LDAPMessage
*msg
= NULL
;
1321 ADS_STATUS ads_status
;
1323 if ( !ads
->ldap
.ld
) {
1324 ads_status
= ads_connect( ads
);
1325 if ( !ADS_ERR_OK(ads_status
) ) {
1326 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1331 ads_status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
1332 "(objectclass=*)", rootname_attrs
, &msg
);
1333 if (!ADS_ERR_OK(ads_status
)) {
1337 root_dn
= ads_pull_string(ads
, ctx
, msg
, "rootDomainNamingContext");
1339 ads_msgfree( ads
, msg
);
1343 root_domain
= ads_build_domain( root_dn
);
1346 ads_msgfree( ads
, msg
);
1348 /* try again for NS servers */
1350 status
= ads_dns_lookup_ns( ctx
, root_domain
, &nameservers
, &ns_count
);
1352 if ( !NT_STATUS_IS_OK(status
) || (ns_count
== 0)) {
1353 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1354 "realm\n", ads
->config
.realm
));
1358 dnsdomain
= root_domain
;
1362 /* Now perform the dns update - we'll try non-secure and if we fail,
1363 we'll follow it up with a secure update */
1365 fstrcpy( dns_server
, nameservers
[0].hostname
);
1367 dns_err
= DoDNSUpdate(dns_server
, dnsdomain
, machine_name
, addrs
, num_addrs
);
1368 if (!ERR_DNS_IS_OK(dns_err
)) {
1369 status
= NT_STATUS_UNSUCCESSFUL
;
1374 SAFE_FREE( root_domain
);
1379 static NTSTATUS
net_update_dns(TALLOC_CTX
*mem_ctx
, ADS_STRUCT
*ads
)
1382 struct sockaddr_storage
*iplist
= NULL
;
1383 fstring machine_name
;
1386 name_to_fqdn( machine_name
, global_myname() );
1387 strlower_m( machine_name
);
1389 /* Get our ip address (not the 127.0.0.x address but a real ip
1392 num_addrs
= get_my_ip_address( &iplist
);
1393 if ( num_addrs
<= 0 ) {
1394 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1396 return NT_STATUS_INVALID_PARAMETER
;
1399 status
= net_update_dns_internal(mem_ctx
, ads
, machine_name
,
1401 SAFE_FREE( iplist
);
1407 /*******************************************************************
1408 ********************************************************************/
1410 static int net_ads_join_usage(int argc
, const char **argv
)
1412 d_printf("net ads join [options]\n");
1413 d_printf("Valid options:\n");
1414 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1415 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1416 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1417 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1418 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1419 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1420 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1421 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1422 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1423 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1424 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1425 d_printf(" the two other attributes.\n");
1430 /*******************************************************************
1431 ********************************************************************/
1433 int net_ads_join(int argc
, const char **argv
)
1436 TALLOC_CTX
*ctx
= NULL
;
1437 struct libnet_JoinCtx
*r
= NULL
;
1438 const char *domain
= lp_realm();
1439 WERROR werr
= WERR_SETUP_NOT_JOINED
;
1440 bool createupn
= False
;
1441 const char *machineupn
= NULL
;
1442 const char *create_in_ou
= NULL
;
1444 const char *os_name
= NULL
;
1445 const char *os_version
= NULL
;
1447 nt_status
= check_ads_config();
1448 if (!NT_STATUS_IS_OK(nt_status
)) {
1449 d_fprintf(stderr
, "Invalid configuration. Exiting....\n");
1450 werr
= ntstatus_to_werror(nt_status
);
1454 use_in_memory_ccache();
1456 werr
= libnet_init_JoinCtx(ctx
, &r
);
1457 if (!W_ERROR_IS_OK(werr
)) {
1461 if (!(ctx
= talloc_init("net_ads_join"))) {
1462 d_fprintf(stderr
, "Could not initialise talloc context.\n");
1467 /* process additional command line args */
1469 for ( i
=0; i
<argc
; i
++ ) {
1470 if ( !StrnCaseCmp(argv
[i
], "createupn", strlen("createupn")) ) {
1472 machineupn
= get_string_param(argv
[i
]);
1474 else if ( !StrnCaseCmp(argv
[i
], "createcomputer", strlen("createcomputer")) ) {
1475 if ( (create_in_ou
= get_string_param(argv
[i
])) == NULL
) {
1476 d_fprintf(stderr
, "Please supply a valid OU path.\n");
1477 werr
= WERR_INVALID_PARAM
;
1481 else if ( !StrnCaseCmp(argv
[i
], "osName", strlen("osName")) ) {
1482 if ( (os_name
= get_string_param(argv
[i
])) == NULL
) {
1483 d_fprintf(stderr
, "Please supply a operating system name.\n");
1484 werr
= WERR_INVALID_PARAM
;
1488 else if ( !StrnCaseCmp(argv
[i
], "osVer", strlen("osVer")) ) {
1489 if ( (os_version
= get_string_param(argv
[i
])) == NULL
) {
1490 d_fprintf(stderr
, "Please supply a valid operating system version.\n");
1491 werr
= WERR_INVALID_PARAM
;
1500 /* Do the domain join here */
1502 r
->in
.domain_name
= domain
;
1503 r
->in
.create_upn
= createupn
;
1504 r
->in
.upn
= machineupn
;
1505 r
->in
.account_ou
= create_in_ou
;
1506 r
->in
.os_name
= os_name
;
1507 r
->in
.os_version
= os_version
;
1508 r
->in
.dc_name
= opt_host
;
1509 r
->in
.admin_account
= opt_user_name
;
1510 r
->in
.admin_password
= net_prompt_pass(opt_user_name
);
1511 r
->in
.debug
= opt_verbose
;
1512 r
->in
.join_flags
= WKSSVC_JOIN_FLAGS_JOIN_TYPE
|
1513 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE
|
1514 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED
;
1516 werr
= libnet_Join(ctx
, r
);
1517 if (!W_ERROR_IS_OK(werr
)) {
1521 /* Check the short name of the domain */
1523 if (!strequal(lp_workgroup(), r
->out
.netbios_domain_name
)) {
1524 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1525 d_printf("domain name obtained from the server.\n");
1526 d_printf("Using the name [%s] from the server.\n", r
->out
.netbios_domain_name
);
1527 d_printf("You should set \"workgroup = %s\" in %s.\n",
1528 r
->out
.netbios_domain_name
, get_dyn_CONFIGFILE());
1531 d_printf("Using short domain name -- %s\n", r
->out
.netbios_domain_name
);
1533 d_printf("Joined '%s' to realm '%s'\n", r
->in
.machine_name
,
1534 r
->out
.dns_domain_name
);
1536 #if defined(WITH_DNS_UPDATES)
1538 /* We enter this block with user creds */
1539 ADS_STRUCT
*ads_dns
= NULL
;
1541 if ( (ads_dns
= ads_init( lp_realm(), NULL
, NULL
)) != NULL
) {
1542 /* kinit with the machine password */
1544 use_in_memory_ccache();
1545 asprintf( &ads_dns
->auth
.user_name
, "%s$", global_myname() );
1546 ads_dns
->auth
.password
= secrets_fetch_machine_password(
1547 lp_workgroup(), NULL
, NULL
);
1548 ads_dns
->auth
.realm
= SMB_STRDUP( lp_realm() );
1549 ads_kinit_password( ads_dns
);
1552 if ( !ads_dns
|| !NT_STATUS_IS_OK(net_update_dns( ctx
, ads_dns
)) ) {
1553 d_fprintf( stderr
, "DNS update failed!\n" );
1556 /* exit from this block using machine creds */
1557 ads_destroy(&ads_dns
);
1566 /* issue an overall failure message at the end. */
1567 d_printf("Failed to join domain: %s\n",
1568 r
&& r
->out
.error_string
? r
->out
.error_string
:
1569 get_friendly_werror_msg(werr
));
1575 /*******************************************************************
1576 ********************************************************************/
1578 static int net_ads_dns_usage(int argc
, const char **argv
)
1580 #if defined(WITH_DNS_UPDATES)
1581 d_printf("net ads dns <command>\n");
1582 d_printf("Valid commands:\n");
1583 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1587 d_fprintf(stderr
, "DNS update support not enabled at compile time!\n");
1592 /*******************************************************************
1593 ********************************************************************/
1595 static int net_ads_dns_register(int argc
, const char **argv
)
1597 #if defined(WITH_DNS_UPDATES)
1603 talloc_enable_leak_report();
1607 d_fprintf(stderr
, "net ads dns register\n");
1611 if (!(ctx
= talloc_init("net_ads_dns"))) {
1612 d_fprintf(stderr
, "Could not initialise talloc context\n");
1616 status
= ads_startup(True
, &ads
);
1617 if ( !ADS_ERR_OK(status
) ) {
1618 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status
)));
1623 if ( !NT_STATUS_IS_OK(net_update_dns(ctx
, ads
)) ) {
1624 d_fprintf( stderr
, "DNS update failed!\n" );
1625 ads_destroy( &ads
);
1630 d_fprintf( stderr
, "Successfully registered hostname with DNS\n" );
1637 d_fprintf(stderr
, "DNS update support not enabled at compile time!\n");
1642 #if defined(WITH_DNS_UPDATES)
1643 DNS_ERROR
do_gethostbyname(const char *server
, const char *host
);
1646 static int net_ads_dns_gethostbyname(int argc
, const char **argv
)
1648 #if defined(WITH_DNS_UPDATES)
1652 talloc_enable_leak_report();
1656 d_fprintf(stderr
, "net ads dns gethostbyname <server> "
1661 err
= do_gethostbyname(argv
[0], argv
[1]);
1663 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err
));
1668 static int net_ads_dns(int argc
, const char *argv
[])
1670 struct functable func
[] = {
1671 {"REGISTER", net_ads_dns_register
},
1672 {"GETHOSTBYNAME", net_ads_dns_gethostbyname
},
1676 return net_run_function(argc
, argv
, func
, net_ads_dns_usage
);
1679 /*******************************************************************
1680 ********************************************************************/
1682 int net_ads_printer_usage(int argc
, const char **argv
)
1685 "\nnet ads printer search <printer>"
1686 "\n\tsearch for a printer in the directory\n"
1687 "\nnet ads printer info <printer> <server>"
1688 "\n\tlookup info in directory for printer on server"
1689 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1690 "\nnet ads printer publish <printername>"
1691 "\n\tpublish printer in directory"
1692 "\n\t(note: printer name is required)\n"
1693 "\nnet ads printer remove <printername>"
1694 "\n\tremove printer from directory"
1695 "\n\t(note: printer name is required)\n");
1699 /*******************************************************************
1700 ********************************************************************/
1702 static int net_ads_printer_search(int argc
, const char **argv
)
1706 LDAPMessage
*res
= NULL
;
1708 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
1712 rc
= ads_find_printers(ads
, &res
);
1714 if (!ADS_ERR_OK(rc
)) {
1715 d_fprintf(stderr
, "ads_find_printer: %s\n", ads_errstr(rc
));
1716 ads_msgfree(ads
, res
);
1721 if (ads_count_replies(ads
, res
) == 0) {
1722 d_fprintf(stderr
, "No results found\n");
1723 ads_msgfree(ads
, res
);
1729 ads_msgfree(ads
, res
);
1734 static int net_ads_printer_info(int argc
, const char **argv
)
1738 const char *servername
, *printername
;
1739 LDAPMessage
*res
= NULL
;
1741 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
1746 printername
= argv
[0];
1752 servername
= argv
[1];
1754 servername
= global_myname();
1757 rc
= ads_find_printer_on_server(ads
, &res
, printername
, servername
);
1759 if (!ADS_ERR_OK(rc
)) {
1760 d_fprintf(stderr
, "Server '%s' not found: %s\n",
1761 servername
, ads_errstr(rc
));
1762 ads_msgfree(ads
, res
);
1767 if (ads_count_replies(ads
, res
) == 0) {
1768 d_fprintf(stderr
, "Printer '%s' not found\n", printername
);
1769 ads_msgfree(ads
, res
);
1775 ads_msgfree(ads
, res
);
1781 static int net_ads_printer_publish(int argc
, const char **argv
)
1785 const char *servername
, *printername
;
1786 struct cli_state
*cli
;
1787 struct rpc_pipe_client
*pipe_hnd
;
1788 struct sockaddr_storage server_ss
;
1790 TALLOC_CTX
*mem_ctx
= talloc_init("net_ads_printer_publish");
1791 ADS_MODLIST mods
= ads_init_mods(mem_ctx
);
1792 char *prt_dn
, *srv_dn
, **srv_cn
;
1793 char *srv_cn_escaped
= NULL
, *printername_escaped
= NULL
;
1794 LDAPMessage
*res
= NULL
;
1796 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
1797 talloc_destroy(mem_ctx
);
1802 talloc_destroy(mem_ctx
);
1803 return net_ads_printer_usage(argc
, argv
);
1806 printername
= argv
[0];
1809 servername
= argv
[1];
1811 servername
= global_myname();
1814 /* Get printer data from SPOOLSS */
1816 resolve_name(servername
, &server_ss
, 0x20);
1818 nt_status
= cli_full_connection(&cli
, global_myname(), servername
,
1821 opt_user_name
, opt_workgroup
,
1822 opt_password
? opt_password
: "",
1823 CLI_FULL_CONNECTION_USE_KERBEROS
,
1826 if (NT_STATUS_IS_ERR(nt_status
)) {
1827 d_fprintf(stderr
, "Unable to open a connnection to %s to obtain data "
1828 "for %s\n", servername
, printername
);
1830 talloc_destroy(mem_ctx
);
1834 /* Publish on AD server */
1836 ads_find_machine_acct(ads
, &res
, servername
);
1838 if (ads_count_replies(ads
, res
) == 0) {
1839 d_fprintf(stderr
, "Could not find machine account for server %s\n",
1842 talloc_destroy(mem_ctx
);
1846 srv_dn
= ldap_get_dn((LDAP
*)ads
->ldap
.ld
, (LDAPMessage
*)res
);
1847 srv_cn
= ldap_explode_dn(srv_dn
, 1);
1849 srv_cn_escaped
= escape_rdn_val_string_alloc(srv_cn
[0]);
1850 printername_escaped
= escape_rdn_val_string_alloc(printername
);
1851 if (!srv_cn_escaped
|| !printername_escaped
) {
1852 SAFE_FREE(srv_cn_escaped
);
1853 SAFE_FREE(printername_escaped
);
1854 d_fprintf(stderr
, "Internal error, out of memory!");
1856 talloc_destroy(mem_ctx
);
1860 asprintf(&prt_dn
, "cn=%s-%s,%s", srv_cn_escaped
, printername_escaped
, srv_dn
);
1862 SAFE_FREE(srv_cn_escaped
);
1863 SAFE_FREE(printername_escaped
);
1865 pipe_hnd
= cli_rpc_pipe_open_noauth(cli
, PI_SPOOLSS
, &nt_status
);
1867 d_fprintf(stderr
, "Unable to open a connnection to the spoolss pipe on %s\n",
1871 talloc_destroy(mem_ctx
);
1875 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd
, mem_ctx
, &mods
,
1879 talloc_destroy(mem_ctx
);
1883 rc
= ads_add_printer_entry(ads
, prt_dn
, mem_ctx
, &mods
);
1884 if (!ADS_ERR_OK(rc
)) {
1885 d_fprintf(stderr
, "ads_publish_printer: %s\n", ads_errstr(rc
));
1888 talloc_destroy(mem_ctx
);
1892 d_printf("published printer\n");
1895 talloc_destroy(mem_ctx
);
1900 static int net_ads_printer_remove(int argc
, const char **argv
)
1904 const char *servername
;
1906 LDAPMessage
*res
= NULL
;
1908 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
1913 return net_ads_printer_usage(argc
, argv
);
1917 servername
= argv
[1];
1919 servername
= global_myname();
1922 rc
= ads_find_printer_on_server(ads
, &res
, argv
[0], servername
);
1924 if (!ADS_ERR_OK(rc
)) {
1925 d_fprintf(stderr
, "ads_find_printer_on_server: %s\n", ads_errstr(rc
));
1926 ads_msgfree(ads
, res
);
1931 if (ads_count_replies(ads
, res
) == 0) {
1932 d_fprintf(stderr
, "Printer '%s' not found\n", argv
[1]);
1933 ads_msgfree(ads
, res
);
1938 prt_dn
= ads_get_dn(ads
, res
);
1939 ads_msgfree(ads
, res
);
1940 rc
= ads_del_dn(ads
, prt_dn
);
1941 ads_memfree(ads
, prt_dn
);
1943 if (!ADS_ERR_OK(rc
)) {
1944 d_fprintf(stderr
, "ads_del_dn: %s\n", ads_errstr(rc
));
1953 static int net_ads_printer(int argc
, const char **argv
)
1955 struct functable func
[] = {
1956 {"SEARCH", net_ads_printer_search
},
1957 {"INFO", net_ads_printer_info
},
1958 {"PUBLISH", net_ads_printer_publish
},
1959 {"REMOVE", net_ads_printer_remove
},
1963 return net_run_function(argc
, argv
, func
, net_ads_printer_usage
);
1967 static int net_ads_password(int argc
, const char **argv
)
1970 const char *auth_principal
= opt_user_name
;
1971 const char *auth_password
= opt_password
;
1973 char *new_password
= NULL
;
1978 if (opt_user_name
== NULL
|| opt_password
== NULL
) {
1979 d_fprintf(stderr
, "You must supply an administrator username/password\n");
1984 d_fprintf(stderr
, "ERROR: You must say which username to change password for\n");
1989 if (!strchr_m(user
, '@')) {
1990 asprintf(&c
, "%s@%s", argv
[0], lp_realm());
1994 use_in_memory_ccache();
1995 c
= strchr_m(auth_principal
, '@');
2002 /* use the realm so we can eventually change passwords for users
2003 in realms other than default */
2004 if (!(ads
= ads_init(realm
, opt_workgroup
, opt_host
))) {
2008 /* we don't actually need a full connect, but it's the easy way to
2009 fill in the KDC's addresss */
2012 if (!ads
|| !ads
->config
.realm
) {
2013 d_fprintf(stderr
, "Didn't find the kerberos server!\n");
2018 new_password
= (char *)argv
[1];
2020 asprintf(&prompt
, "Enter new password for %s:", user
);
2021 new_password
= getpass(prompt
);
2025 ret
= kerberos_set_password(ads
->auth
.kdc_server
, auth_principal
,
2026 auth_password
, user
, new_password
, ads
->auth
.time_offset
);
2027 if (!ADS_ERR_OK(ret
)) {
2028 d_fprintf(stderr
, "Password change failed: %s\n", ads_errstr(ret
));
2033 d_printf("Password change for %s completed.\n", user
);
2039 int net_ads_changetrustpw(int argc
, const char **argv
)
2042 char *host_principal
;
2046 if (!secrets_init()) {
2047 DEBUG(1,("Failed to initialise secrets database\n"));
2051 net_use_krb_machine_account();
2053 use_in_memory_ccache();
2055 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
2059 fstrcpy(my_name
, global_myname());
2060 strlower_m(my_name
);
2061 asprintf(&host_principal
, "%s$@%s", my_name
, ads
->config
.realm
);
2062 d_printf("Changing password for principal: %s\n", host_principal
);
2064 ret
= ads_change_trust_account_password(ads
, host_principal
);
2066 if (!ADS_ERR_OK(ret
)) {
2067 d_fprintf(stderr
, "Password change failed: %s\n", ads_errstr(ret
));
2069 SAFE_FREE(host_principal
);
2073 d_printf("Password change for principal %s succeeded.\n", host_principal
);
2075 if (lp_use_kerberos_keytab()) {
2076 d_printf("Attempting to update system keytab with new password.\n");
2077 if (ads_keytab_create_default(ads
)) {
2078 d_printf("Failed to update system keytab.\n");
2083 SAFE_FREE(host_principal
);
2089 help for net ads search
2091 static int net_ads_search_usage(int argc
, const char **argv
)
2094 "\nnet ads search <expression> <attributes...>\n"\
2095 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2096 "The expression is a standard LDAP search expression, and the\n"\
2097 "attributes are a list of LDAP fields to show in the results\n\n"\
2098 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2100 net_common_flags_usage(argc
, argv
);
2106 general ADS search function. Useful in diagnosing problems in ADS
2108 static int net_ads_search(int argc
, const char **argv
)
2112 const char *ldap_exp
;
2114 LDAPMessage
*res
= NULL
;
2117 return net_ads_search_usage(argc
, argv
);
2120 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
2127 rc
= ads_do_search_all(ads
, ads
->config
.bind_path
,
2129 ldap_exp
, attrs
, &res
);
2130 if (!ADS_ERR_OK(rc
)) {
2131 d_fprintf(stderr
, "search failed: %s\n", ads_errstr(rc
));
2136 d_printf("Got %d replies\n\n", ads_count_replies(ads
, res
));
2138 /* dump the results */
2141 ads_msgfree(ads
, res
);
2149 help for net ads search
2151 static int net_ads_dn_usage(int argc
, const char **argv
)
2154 "\nnet ads dn <dn> <attributes...>\n"\
2155 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2156 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2157 "to show in the results\n\n"\
2158 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2159 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2161 net_common_flags_usage(argc
, argv
);
2167 general ADS search function. Useful in diagnosing problems in ADS
2169 static int net_ads_dn(int argc
, const char **argv
)
2175 LDAPMessage
*res
= NULL
;
2178 return net_ads_dn_usage(argc
, argv
);
2181 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
2188 rc
= ads_do_search_all(ads
, dn
,
2190 "(objectclass=*)", attrs
, &res
);
2191 if (!ADS_ERR_OK(rc
)) {
2192 d_fprintf(stderr
, "search failed: %s\n", ads_errstr(rc
));
2197 d_printf("Got %d replies\n\n", ads_count_replies(ads
, res
));
2199 /* dump the results */
2202 ads_msgfree(ads
, res
);
2209 help for net ads sid search
2211 static int net_ads_sid_usage(int argc
, const char **argv
)
2214 "\nnet ads sid <sid> <attributes...>\n"\
2215 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2216 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2217 "to show in the results\n\n"\
2218 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2220 net_common_flags_usage(argc
, argv
);
2226 general ADS search function. Useful in diagnosing problems in ADS
2228 static int net_ads_sid(int argc
, const char **argv
)
2232 const char *sid_string
;
2234 LDAPMessage
*res
= NULL
;
2238 return net_ads_sid_usage(argc
, argv
);
2241 if (!ADS_ERR_OK(ads_startup(False
, &ads
))) {
2245 sid_string
= argv
[0];
2248 if (!string_to_sid(&sid
, sid_string
)) {
2249 d_fprintf(stderr
, "could not convert sid\n");
2254 rc
= ads_search_retry_sid(ads
, &res
, &sid
, attrs
);
2255 if (!ADS_ERR_OK(rc
)) {
2256 d_fprintf(stderr
, "search failed: %s\n", ads_errstr(rc
));
2261 d_printf("Got %d replies\n\n", ads_count_replies(ads
, res
));
2263 /* dump the results */
2266 ads_msgfree(ads
, res
);
2273 static int net_ads_keytab_usage(int argc
, const char **argv
)
2276 "net ads keytab <COMMAND>\n"\
2277 "<COMMAND> can be either:\n"\
2278 " ADD Adds new service principal\n"\
2279 " CREATE Creates a fresh keytab\n"\
2280 " FLUSH Flushes out all keytab entries\n"\
2281 " HELP Prints this help message\n"\
2282 " LIST List the keytab\n"\
2283 "The ADD and LIST command will take arguments, the other commands\n"\
2284 "will not take any arguments. The arguments given to ADD\n"\
2285 "should be a list of principals to add. For example, \n"\
2286 " net ads keytab add srv1 srv2\n"\
2287 "will add principals for the services srv1 and srv2 to the\n"\
2288 "system's keytab.\n"\
2289 "The LIST command takes a keytabname.\n"\
2295 static int net_ads_keytab_flush(int argc
, const char **argv
)
2300 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
2303 ret
= ads_keytab_flush(ads
);
2308 static int net_ads_keytab_add(int argc
, const char **argv
)
2314 d_printf("Processing principals to add...\n");
2315 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
2318 for (i
= 0; i
< argc
; i
++) {
2319 ret
|= ads_keytab_add_entry(ads
, argv
[i
]);
2325 static int net_ads_keytab_create(int argc
, const char **argv
)
2330 if (!ADS_ERR_OK(ads_startup(True
, &ads
))) {
2333 ret
= ads_keytab_create_default(ads
);
2338 static int net_ads_keytab_list(int argc
, const char **argv
)
2340 const char *keytab
= NULL
;
2346 return ads_keytab_list(keytab
);
2350 int net_ads_keytab(int argc
, const char **argv
)
2352 struct functable func
[] = {
2353 {"ADD", net_ads_keytab_add
},
2354 {"CREATE", net_ads_keytab_create
},
2355 {"FLUSH", net_ads_keytab_flush
},
2356 {"HELP", net_ads_keytab_usage
},
2357 {"LIST", net_ads_keytab_list
},
2361 if (!lp_use_kerberos_keytab()) {
2362 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2363 use keytab functions.\n");
2366 return net_run_function(argc
, argv
, func
, net_ads_keytab_usage
);
2369 static int net_ads_kerberos_usage(int argc
, const char **argv
)
2372 "net ads kerberos <COMMAND>\n"\
2373 "<COMMAND> can be either:\n"\
2374 " RENEW Renew TGT from existing credential cache\n"\
2375 " PAC Dumps the Kerberos PAC\n"\
2376 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2383 static int net_ads_kerberos_renew(int argc
, const char **argv
)
2385 int ret
= smb_krb5_renew_ticket(NULL
, NULL
, NULL
, NULL
);
2387 d_printf("failed to renew kerberos ticket: %s\n",
2388 error_message(ret
));
2393 static int net_ads_kerberos_pac(int argc
, const char **argv
)
2395 struct PAC_DATA
*pac
= NULL
;
2396 struct PAC_LOGON_INFO
*info
= NULL
;
2397 TALLOC_CTX
*mem_ctx
= NULL
;
2401 mem_ctx
= talloc_init("net_ads_kerberos_pac");
2406 opt_password
= net_prompt_pass(opt_user_name
);
2408 status
= kerberos_return_pac(mem_ctx
,
2417 2592000, /* one month */
2419 if (!NT_STATUS_IS_OK(status
)) {
2420 d_printf("failed to query kerberos PAC: %s\n",
2425 info
= get_logon_info_from_pac(pac
);
2428 s
= NDR_PRINT_STRUCT_STRING(mem_ctx
, PAC_LOGON_INFO
, info
);
2429 d_printf("The Pac: %s\n", s
);
2434 TALLOC_FREE(mem_ctx
);
2438 static int net_ads_kerberos_kinit(int argc
, const char **argv
)
2440 TALLOC_CTX
*mem_ctx
= NULL
;
2444 mem_ctx
= talloc_init("net_ads_kerberos_kinit");
2449 opt_password
= net_prompt_pass(opt_user_name
);
2451 ret
= kerberos_kinit_password_ext(opt_user_name
,
2459 2592000, /* one month */
2462 d_printf("failed to kinit password: %s\n",
2469 int net_ads_kerberos(int argc
, const char **argv
)
2471 struct functable func
[] = {
2472 {"KINIT", net_ads_kerberos_kinit
},
2473 {"RENEW", net_ads_kerberos_renew
},
2474 {"PAC", net_ads_kerberos_pac
},
2475 {"HELP", net_ads_kerberos_usage
},
2479 return net_run_function(argc
, argv
, func
, net_ads_kerberos_usage
);
2483 int net_ads_help(int argc
, const char **argv
)
2485 struct functable func
[] = {
2486 {"USER", net_ads_user_usage
},
2487 {"GROUP", net_ads_group_usage
},
2488 {"PRINTER", net_ads_printer_usage
},
2489 {"SEARCH", net_ads_search_usage
},
2490 {"INFO", net_ads_info
},
2491 {"JOIN", net_ads_join_usage
},
2492 {"DNS", net_ads_dns_usage
},
2493 {"LEAVE", net_ads_leave
},
2494 {"STATUS", net_ads_status
},
2495 {"PASSWORD", net_ads_password
},
2496 {"CHANGETRUSTPW", net_ads_changetrustpw
},
2500 return net_run_function(argc
, argv
, func
, net_ads_usage
);
2503 int net_ads(int argc
, const char **argv
)
2505 struct functable func
[] = {
2506 {"INFO", net_ads_info
},
2507 {"JOIN", net_ads_join
},
2508 {"TESTJOIN", net_ads_testjoin
},
2509 {"LEAVE", net_ads_leave
},
2510 {"STATUS", net_ads_status
},
2511 {"USER", net_ads_user
},
2512 {"GROUP", net_ads_group
},
2513 {"DNS", net_ads_dns
},
2514 {"PASSWORD", net_ads_password
},
2515 {"CHANGETRUSTPW", net_ads_changetrustpw
},
2516 {"PRINTER", net_ads_printer
},
2517 {"SEARCH", net_ads_search
},
2519 {"SID", net_ads_sid
},
2520 {"WORKGROUP", net_ads_workgroup
},
2521 {"LOOKUP", net_ads_lookup
},
2522 {"KEYTAB", net_ads_keytab
},
2523 {"GPO", net_ads_gpo
},
2524 {"KERBEROS", net_ads_kerberos
},
2525 {"HELP", net_ads_help
},
2529 return net_run_function(argc
, argv
, func
, net_ads_usage
);
2534 static int net_ads_noads(void)
2536 d_fprintf(stderr
, "ADS support not compiled in\n");
2540 int net_ads_keytab(int argc
, const char **argv
)
2542 return net_ads_noads();
2545 int net_ads_kerberos(int argc
, const char **argv
)
2547 return net_ads_noads();
2550 int net_ads_usage(int argc
, const char **argv
)
2552 return net_ads_noads();
2555 int net_ads_help(int argc
, const char **argv
)
2557 return net_ads_noads();
2560 int net_ads_changetrustpw(int argc
, const char **argv
)
2562 return net_ads_noads();
2565 int net_ads_join(int argc
, const char **argv
)
2567 return net_ads_noads();
2570 int net_ads_user(int argc
, const char **argv
)
2572 return net_ads_noads();
2575 int net_ads_group(int argc
, const char **argv
)
2577 return net_ads_noads();
2580 /* this one shouldn't display a message */
2581 int net_ads_check(void)
2586 int net_ads_check_our_domain(void)
2591 int net_ads(int argc
, const char **argv
)
2593 return net_ads_usage(argc
, argv
);
2596 #endif /* WITH_ADS */