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"
27 /* Macro for checking RPC error codes to make things more readable */
30 #define CHECK_RPC_ERR(rpc, msg) \
31 if (!NT_STATUS_IS_OK(result = rpc)) { \
32 DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
36 #define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
37 if (!NT_STATUS_IS_OK(result = rpc)) { \
38 DEBUG(0, debug_args); \
45 int net_ads_usage(int argc, const char **argv)
48 "\nnet ads join <org_unit>"\
49 "\n\tjoins the local machine to a ADS realm\n"\
51 "\n\tremoves the local machine from a ADS realm\n"\
53 "\n\ttests that an exiting join is OK\n"\
55 "\n\tlist, add, or delete users in the realm\n"\
57 "\n\tlist, add, or delete groups in the realm\n"\
59 "\n\tshows some info on the server\n"\
61 "\n\tdump the machine account details to stdout\n"
63 "\n\tperform a CLDAP search on the server\n"
64 "\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
65 "\n\tchange a user's password using an admin account"\
66 "\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
67 "\nnet ads changetrustpw"\
68 "\n\tchange the trust account password of this machine in the AD tree\n"\
69 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
70 "\n\t lookup, add, or remove directory entry for a printer\n"\
72 "\n\tperform a raw LDAP search and dump the results\n"
74 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
76 "\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
78 "\n\tcreates and updates the kerberos system keytab file\n"
85 do a cldap netlogon query
87 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
89 struct cldap_netlogon_reply reply;
91 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
92 d_fprintf(stderr, "CLDAP query failed!\n");
96 d_printf("Information for Domain Controller: %s\n\n",
97 inet_ntoa(ads->ldap_ip));
99 d_printf("Response Type: ");
100 switch (reply.type) {
101 case SAMLOGON_AD_UNK_R:
102 d_printf("SAMLOGON\n");
105 d_printf("SAMLOGON_USER\n");
108 d_printf("0x%x\n", reply.type);
111 d_printf("GUID: %s\n",
112 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
115 "\tIs a GC of the forest: %s\n"
116 "\tIs an LDAP server: %s\n"
117 "\tSupports DS: %s\n"
118 "\tIs running a KDC: %s\n"
119 "\tIs running time services: %s\n"
120 "\tIs the closest DC: %s\n"
121 "\tIs writable: %s\n"
122 "\tHas a hardware clock: %s\n"
123 "\tIs a non-domain NC serviced by LDAP server: %s\n",
124 (reply.flags & ADS_PDC) ? "yes" : "no",
125 (reply.flags & ADS_GC) ? "yes" : "no",
126 (reply.flags & ADS_LDAP) ? "yes" : "no",
127 (reply.flags & ADS_DS) ? "yes" : "no",
128 (reply.flags & ADS_KDC) ? "yes" : "no",
129 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
130 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
131 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
132 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
133 (reply.flags & ADS_NDNC) ? "yes" : "no");
135 printf("Forest:\t\t\t%s\n", reply.forest);
136 printf("Domain:\t\t\t%s\n", reply.domain);
137 printf("Domain Controller:\t%s\n", reply.hostname);
139 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
140 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
142 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
143 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
145 printf("Site Name:\t\t%s\n", reply.site_name);
146 printf("Site Name (2):\t\t%s\n", reply.site_name_2);
148 d_printf("NT Version: %d\n", reply.version);
149 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
150 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
157 this implements the CLDAP based netlogon lookup requests
158 for finding the domain controller of a ADS domain
160 static int net_ads_lookup(int argc, const char **argv)
164 const char *realm = NULL;
166 if ( strequal(lp_workgroup(), opt_target_workgroup ) )
169 ads = ads_init(realm, opt_target_workgroup, opt_host);
171 ads->auth.flags |= ADS_AUTH_NO_BIND;
174 status = ads_connect(ads);
175 if (!ADS_ERR_OK(status) || !ads) {
176 d_fprintf(stderr, "Didn't find the cldap server!\n");
180 if (!ads->config.realm) {
181 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
182 ads->ldap_port = 389;
185 return net_ads_cldap_netlogon(ads);
190 static int net_ads_info(int argc, const char **argv)
194 if ( (ads = ads_init(lp_realm(), opt_target_workgroup, opt_host)) != NULL ) {
195 ads->auth.flags |= ADS_AUTH_NO_BIND;
200 if (!ads || !ads->config.realm) {
201 d_fprintf(stderr, "Didn't find the ldap server!\n");
205 /* Try to set the server's current time since we didn't do a full
206 TCP LDAP session initially */
208 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
209 d_fprintf( stderr, "Failed to get server's current time!\n");
212 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
213 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
214 d_printf("Realm: %s\n", ads->config.realm);
215 d_printf("Bind Path: %s\n", ads->config.bind_path);
216 d_printf("LDAP port: %d\n", ads->ldap_port);
217 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
219 d_printf("KDC server: %s\n", ads->auth.kdc_server );
220 d_printf("Server time offset: %d\n", ads->auth.time_offset );
225 static void use_in_memory_ccache(void) {
226 /* Use in-memory credentials cache so we do not interfere with
227 * existing credentials */
228 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
231 static ADS_STRUCT *ads_startup(void)
235 BOOL need_password = False;
236 BOOL second_time = False;
239 /* lp_realm() should be handled by a command line param,
240 However, the join requires that realm be set in smb.conf
241 and compares our realm with the remote server's so this is
242 ok until someone needs more flexibility */
244 ads = ads_init(lp_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) {
257 asprintf(&prompt,"%s's password: ", opt_user_name);
258 opt_password = getpass(prompt);
263 use_in_memory_ccache();
264 ads->auth.password = smb_xstrdup(opt_password);
267 ads->auth.user_name = smb_xstrdup(opt_user_name);
270 * If the username is of the form "name@realm",
271 * extract the realm and convert to upper case.
272 * This is only used to establish the connection.
274 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
276 ads->auth.realm = smb_xstrdup(cp);
277 strupper_m(ads->auth.realm);
280 status = ads_connect(ads);
282 if (!ADS_ERR_OK(status)) {
283 if (!need_password && !second_time) {
284 need_password = True;
288 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
298 Check to see if connection can be made via ads.
299 ads_startup() stores the password in opt_password if it needs to so
300 that rpc or rap can use it without re-prompting.
302 int net_ads_check(void)
307 if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == NULL ) {
311 ads->auth.flags |= ADS_AUTH_NO_BIND;
313 status = ads_connect(ads);
314 if ( !ADS_ERR_OK(status) ) {
323 determine the netbios workgroup name for a domain
325 static int net_ads_workgroup(int argc, const char **argv)
329 const char *realm = NULL;
330 struct cldap_netlogon_reply reply;
332 if ( strequal(lp_workgroup(), opt_target_workgroup ) )
335 ads = ads_init(realm, opt_target_workgroup, opt_host);
337 ads->auth.flags |= ADS_AUTH_NO_BIND;
340 status = ads_connect(ads);
341 if (!ADS_ERR_OK(status) || !ads) {
342 d_fprintf(stderr, "Didn't find the cldap server!\n");
346 if (!ads->config.realm) {
347 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
348 ads->ldap_port = 389;
351 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
352 d_fprintf(stderr, "CLDAP query failed!\n");
356 d_printf("Workgroup: %s\n", reply.netbios_domain);
365 static BOOL usergrp_display(char *field, void **values, void *data_area)
367 char **disp_fields = (char **) data_area;
369 if (!field) { /* must be end of record */
370 if (disp_fields[0]) {
371 if (!strchr_m(disp_fields[0], '$')) {
373 d_printf("%-21.21s %s\n",
374 disp_fields[0], disp_fields[1]);
376 d_printf("%s\n", disp_fields[0]);
379 SAFE_FREE(disp_fields[0]);
380 SAFE_FREE(disp_fields[1]);
383 if (!values) /* must be new field, indicate string field */
385 if (StrCaseCmp(field, "sAMAccountName") == 0) {
386 disp_fields[0] = SMB_STRDUP((char *) values[0]);
388 if (StrCaseCmp(field, "description") == 0)
389 disp_fields[1] = SMB_STRDUP((char *) values[0]);
393 static int net_ads_user_usage(int argc, const char **argv)
395 return net_help_user(argc, argv);
398 static int ads_user_add(int argc, const char **argv)
406 if (argc < 1) return net_ads_user_usage(argc, argv);
408 if (!(ads = ads_startup())) {
412 status = ads_find_user_acct(ads, &res, argv[0]);
414 if (!ADS_ERR_OK(status)) {
415 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
419 if (ads_count_replies(ads, res)) {
420 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
424 if (opt_container == NULL) {
425 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
428 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
430 if (!ADS_ERR_OK(status)) {
431 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
436 /* if no password is to be set, we're done */
438 d_printf("User %s added\n", argv[0]);
443 /* try setting the password */
444 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
445 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
446 ads->auth.time_offset);
448 if (ADS_ERR_OK(status)) {
449 d_printf("User %s added\n", argv[0]);
454 /* password didn't set, delete account */
455 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
456 argv[0], ads_errstr(status));
457 ads_msgfree(ads, res);
458 status=ads_find_user_acct(ads, &res, argv[0]);
459 if (ADS_ERR_OK(status)) {
460 userdn = ads_get_dn(ads, res);
461 ads_del_dn(ads, userdn);
462 ads_memfree(ads, userdn);
467 ads_msgfree(ads, res);
472 static int ads_user_info(int argc, const char **argv)
477 const char *attrs[] = {"memberOf", NULL};
478 char *searchstring=NULL;
483 return net_ads_user_usage(argc, argv);
486 escaped_user = escape_ldap_string_alloc(argv[0]);
489 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
493 if (!(ads = ads_startup())) {
494 SAFE_FREE(escaped_user);
498 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
499 rc = ads_search(ads, &res, searchstring, attrs);
500 safe_free(searchstring);
502 if (!ADS_ERR_OK(rc)) {
503 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
505 SAFE_FREE(escaped_user);
509 grouplist = ldap_get_values((LDAP *)ads->ld,
510 (LDAPMessage *)res, "memberOf");
515 for (i=0;grouplist[i];i++) {
516 groupname = ldap_explode_dn(grouplist[i], 1);
517 d_printf("%s\n", groupname[0]);
518 ldap_value_free(groupname);
520 ldap_value_free(grouplist);
523 ads_msgfree(ads, res);
525 SAFE_FREE(escaped_user);
529 static int ads_user_delete(int argc, const char **argv)
537 return net_ads_user_usage(argc, argv);
540 if (!(ads = ads_startup())) {
544 rc = ads_find_user_acct(ads, &res, argv[0]);
545 if (!ADS_ERR_OK(rc)) {
546 DEBUG(0, ("User %s does not exist\n", argv[0]));
550 userdn = ads_get_dn(ads, res);
551 ads_msgfree(ads, res);
552 rc = ads_del_dn(ads, userdn);
553 ads_memfree(ads, userdn);
554 if (!ADS_ERR_OK(rc)) {
555 d_printf("User %s deleted\n", argv[0]);
559 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
565 int net_ads_user(int argc, const char **argv)
567 struct functable func[] = {
568 {"ADD", ads_user_add},
569 {"INFO", ads_user_info},
570 {"DELETE", ads_user_delete},
575 const char *shortattrs[] = {"sAMAccountName", NULL};
576 const char *longattrs[] = {"sAMAccountName", "description", NULL};
577 char *disp_fields[2] = {NULL, NULL};
580 if (!(ads = ads_startup())) {
584 if (opt_long_list_entries)
585 d_printf("\nUser name Comment"\
586 "\n-----------------------------\n");
588 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
590 "(objectCategory=user)",
591 opt_long_list_entries ? longattrs :
592 shortattrs, usergrp_display,
595 return ADS_ERR_OK(rc) ? 0 : -1;
598 return net_run_function(argc, argv, func, net_ads_user_usage);
601 static int net_ads_group_usage(int argc, const char **argv)
603 return net_help_group(argc, argv);
606 static int ads_group_add(int argc, const char **argv)
614 return net_ads_group_usage(argc, argv);
617 if (!(ads = ads_startup())) {
621 status = ads_find_user_acct(ads, &res, argv[0]);
623 if (!ADS_ERR_OK(status)) {
624 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
628 if (ads_count_replies(ads, res)) {
629 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
630 ads_msgfree(ads, res);
634 if (opt_container == NULL) {
635 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
638 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
640 if (ADS_ERR_OK(status)) {
641 d_printf("Group %s added\n", argv[0]);
644 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
650 ads_msgfree(ads, res);
655 static int ads_group_delete(int argc, const char **argv)
663 return net_ads_group_usage(argc, argv);
666 if (!(ads = ads_startup())) {
670 rc = ads_find_user_acct(ads, &res, argv[0]);
671 if (!ADS_ERR_OK(rc)) {
672 DEBUG(0, ("Group %s does not exist\n", argv[0]));
676 groupdn = ads_get_dn(ads, res);
677 ads_msgfree(ads, res);
678 rc = ads_del_dn(ads, groupdn);
679 ads_memfree(ads, groupdn);
680 if (!ADS_ERR_OK(rc)) {
681 d_printf("Group %s deleted\n", argv[0]);
685 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
691 int net_ads_group(int argc, const char **argv)
693 struct functable func[] = {
694 {"ADD", ads_group_add},
695 {"DELETE", ads_group_delete},
700 const char *shortattrs[] = {"sAMAccountName", NULL};
701 const char *longattrs[] = {"sAMAccountName", "description", NULL};
702 char *disp_fields[2] = {NULL, NULL};
705 if (!(ads = ads_startup())) {
709 if (opt_long_list_entries)
710 d_printf("\nGroup name Comment"\
711 "\n-----------------------------\n");
712 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
714 "(objectCategory=group)",
715 opt_long_list_entries ? longattrs :
716 shortattrs, usergrp_display,
720 return ADS_ERR_OK(rc) ? 0 : -1;
722 return net_run_function(argc, argv, func, net_ads_group_usage);
725 static int net_ads_status(int argc, const char **argv)
731 if (!(ads = ads_startup())) {
735 rc = ads_find_machine_acct(ads, &res, global_myname());
736 if (!ADS_ERR_OK(rc)) {
737 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
742 if (ads_count_replies(ads, res) == 0) {
743 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
753 /*******************************************************************
754 Leave an AD domain. Windows XP disables the machine account.
755 We'll try the same. The old code would do an LDAP delete.
756 That only worked using the machine creds because added the machine
757 with full control to the computer object's ACL.
758 *******************************************************************/
759 static int net_ads_leave(int argc, const char **argv)
761 ADS_STRUCT *ads = NULL;
763 struct cli_state *cli = NULL;
765 DOM_SID *dom_sid = NULL;
767 if (!secrets_init()) {
768 DEBUG(1,("Failed to initialise secrets database\n"));
772 if (!(ctx = talloc_init("net_ads_leave"))) {
773 DEBUG(0, ("Could not initialise talloc context\n"));
777 /* The finds a DC and takes care of getting the
778 user creds if necessary */
780 if (!(ads = ads_startup())) {
784 /* make RPC calls here */
786 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
787 ads->config.ldap_server_name)) )
792 saf_store( cli->server_domain, cli->desthost );
794 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
798 if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
799 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
800 global_myname(), ads->config.realm);
804 d_printf("Disabled account for '%s' in realm '%s'\n",
805 global_myname(), ads->config.realm);
819 static int net_ads_join_ok(void)
821 ADS_STRUCT *ads = NULL;
823 if (!secrets_init()) {
824 DEBUG(1,("Failed to initialise secrets database\n"));
828 net_use_machine_password();
830 if (!(ads = ads_startup())) {
839 check that an existing join is OK
841 int net_ads_testjoin(int argc, const char **argv)
843 use_in_memory_ccache();
845 /* Display success or failure */
846 if (net_ads_join_ok() != 0) {
847 fprintf(stderr,"Join to domain is not valid\n");
851 printf("Join is OK\n");
855 /*******************************************************************
856 Simple configu checks before beginning the join
857 ********************************************************************/
859 static int check_ads_config( void )
861 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
862 d_printf("Host is not configured as a member server.\n");
866 if (strlen(global_myname()) > 15) {
867 d_printf("Our netbios name can be at most 15 chars long, "
868 "\"%s\" is %u chars long\n",
869 global_myname(), (unsigned int)strlen(global_myname()));
873 if ( lp_security() == SEC_ADS && !*lp_realm()) {
874 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
875 "join to succeed.\n");
879 if (!secrets_init()) {
880 DEBUG(1,("Failed to initialise secrets database\n"));
887 /*******************************************************************
889 ********************************************************************/
891 static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
892 struct in_addr *ip, DOM_SID **dom_sid, const char *password )
895 struct cli_state *cli = NULL;
897 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
900 saf_store( cli->server_domain, cli->desthost );
902 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
905 if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
906 password, ND_TYPE_AD )) )
920 /*******************************************************************
921 Set a machines dNSHostName and servicePrincipalName attributes
922 ********************************************************************/
924 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
926 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
927 char *host_upn, *new_dn;
929 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
932 LDAPMessage *res = NULL;
933 char *dn_string = NULL;
934 const char *machine_name = global_myname();
937 if ( !machine_name ) {
938 return ADS_ERROR(LDAP_NO_MEMORY);
943 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
944 if (!ADS_ERR_OK(status))
947 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
948 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
949 return ADS_ERROR(LDAP_NO_MEMORY);
952 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
953 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
957 new_dn = talloc_strdup(ctx, dn_string);
958 ads_memfree(ads_s, dn_string);
960 return ADS_ERROR(LDAP_NO_MEMORY);
963 /* Windows only creates HOST/shortname & HOST/fqdn. We create
964 the UPN as well so that 'kinit -k' will work. You can only
965 request a TGT for entries with a UPN in AD. */
967 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
970 servicePrincipalName[0] = psp;
972 name_to_fqdn(my_fqdn, machine_name);
974 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
976 servicePrincipalName[1] = psp;
978 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
981 /* now do the mods */
983 if (!(mods = ads_init_mods(ctx))) {
987 /* fields of primary importance */
989 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
990 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
992 status = ads_gen_mod(ads_s, new_dn, mods);
995 ads_msgfree(ads_s, res);
1001 /*******************************************************************
1002 join a domain using ADS (LDAP mods)
1003 ********************************************************************/
1005 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1007 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1009 LDAPMessage *res = NULL;
1011 ou_str = ads_ou_string(ads, ou);
1012 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1015 rc = ads_search_dn(ads, &res, dn, NULL);
1016 ads_msgfree(ads, res);
1018 if (ADS_ERR_OK(rc)) {
1019 /* Attempt to create the machine account and bail if this fails.
1020 Assume that the admin wants exactly what they requested */
1022 rc = ads_create_machine_acct( ads, global_myname(), dn );
1023 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1033 /************************************************************************
1034 ************************************************************************/
1036 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1042 LDAPMessage *res = NULL;
1043 const char *machine_name = global_myname();
1045 status = ads_domain_func_level( ads, &domain_func );
1046 if ( !ADS_ERR_OK(status) ) {
1047 DEBUG(2,("Failed to determine domain functional level!\n"));
1051 /* go ahead and setup the default salt */
1053 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1054 DEBUG(0,("net_derive_salting_principal: failed to obtain stanard DES salt\n"));
1058 fstrcpy( salt, std_salt );
1059 SAFE_FREE( std_salt );
1061 /* if it's a Windows functional domain, we have to look for the UPN */
1063 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1067 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1068 if (!ADS_ERR_OK(status)) {
1072 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1073 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1077 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1079 fstrcpy( salt, upn );
1082 ads_msgfree(ads, res);
1085 return kerberos_secrets_store_des_salt( salt );
1088 /*******************************************************************
1089 join a domain using ADS (LDAP mods)
1090 ********************************************************************/
1092 int net_ads_join(int argc, const char **argv)
1096 char *machine_account = NULL;
1097 const char *short_domain_name = NULL;
1098 char *tmp_password, *password;
1099 struct cldap_netlogon_reply cldap_reply;
1101 DOM_SID *domain_sid = NULL;
1103 if ( check_ads_config() != 0 ) {
1104 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1108 if ( (ads = ads_startup()) == NULL ) {
1112 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1113 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1114 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1120 if (!(ctx = talloc_init("net_ads_join"))) {
1121 DEBUG(0, ("Could not initialise talloc context\n"));
1125 /* If we were given an OU, try to create the machine in the OU account
1126 first and then do the normal RPC join */
1129 status = net_precreate_machine_acct( ads, argv[0] );
1130 if ( !ADS_ERR_OK(status) ) {
1131 d_fprintf( stderr, "Failed to pre-create the machine object "
1132 "in OU %s.\n", argv[0]);
1133 ads_destroy( &ads );
1138 /* Do the domain join here */
1140 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1141 password = talloc_strdup(ctx, tmp_password);
1143 if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1144 d_fprintf(stderr, "Failed to join domain!\n");
1148 /* Check the short name of the domain */
1150 ZERO_STRUCT( cldap_reply );
1152 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1153 ads->server.realm, &cldap_reply ) )
1155 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1156 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1157 d_printf("The workgroup in smb.conf does not match the short\n");
1158 d_printf("domain name obtained from the server.\n");
1159 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1160 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1163 short_domain_name = lp_workgroup();
1166 d_printf("Using short domain name -- %s\n", short_domain_name);
1168 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1169 value from smb.conf and the string returned from the server. The former is
1170 neede to bootstrap winbindd's first connection to the DC to get the real
1171 short domain name --jerry */
1173 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1174 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1180 /* Verify that everything is ok */
1182 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1183 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1187 /* create the dNSHostName & servicePrincipalName values */
1189 status = net_set_machine_spn( ctx, ads );
1190 if ( !ADS_ERR_OK(status) ) {
1191 d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
1192 d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
1193 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1198 if ( !net_derive_salting_principal( ctx, ads ) ) {
1199 DEBUG(1,("Failed to determine salting principal\n"));
1204 /* Now build the keytab, using the same ADS connection */
1205 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1206 DEBUG(1,("Error creating host keytab!\n"));
1209 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1211 SAFE_FREE(machine_account);
1218 /*******************************************************************
1219 ********************************************************************/
1221 int net_ads_printer_usage(int argc, const char **argv)
1224 "\nnet ads printer search <printer>"
1225 "\n\tsearch for a printer in the directory\n"
1226 "\nnet ads printer info <printer> <server>"
1227 "\n\tlookup info in directory for printer on server"
1228 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1229 "\nnet ads printer publish <printername>"
1230 "\n\tpublish printer in directory"
1231 "\n\t(note: printer name is required)\n"
1232 "\nnet ads printer remove <printername>"
1233 "\n\tremove printer from directory"
1234 "\n\t(note: printer name is required)\n");
1238 /*******************************************************************
1239 ********************************************************************/
1241 static int net_ads_printer_search(int argc, const char **argv)
1247 if (!(ads = ads_startup())) {
1251 rc = ads_find_printers(ads, &res);
1253 if (!ADS_ERR_OK(rc)) {
1254 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1255 ads_msgfree(ads, res);
1260 if (ads_count_replies(ads, res) == 0) {
1261 d_fprintf(stderr, "No results found\n");
1262 ads_msgfree(ads, res);
1268 ads_msgfree(ads, res);
1273 static int net_ads_printer_info(int argc, const char **argv)
1277 const char *servername, *printername;
1280 if (!(ads = ads_startup())) {
1285 printername = argv[0];
1291 servername = argv[1];
1293 servername = global_myname();
1296 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1298 if (!ADS_ERR_OK(rc)) {
1299 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1300 ads_msgfree(ads, res);
1305 if (ads_count_replies(ads, res) == 0) {
1306 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1307 ads_msgfree(ads, res);
1313 ads_msgfree(ads, res);
1319 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1320 void *buf, size_t len)
1325 static int net_ads_printer_publish(int argc, const char **argv)
1329 const char *servername, *printername;
1330 struct cli_state *cli;
1331 struct rpc_pipe_client *pipe_hnd;
1332 struct in_addr server_ip;
1334 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1335 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1336 char *prt_dn, *srv_dn, **srv_cn;
1339 if (!(ads = ads_startup())) {
1344 return net_ads_printer_usage(argc, argv);
1347 printername = argv[0];
1350 servername = argv[1];
1352 servername = global_myname();
1355 /* Get printer data from SPOOLSS */
1357 resolve_name(servername, &server_ip, 0x20);
1359 nt_status = cli_full_connection(&cli, global_myname(), servername,
1362 opt_user_name, opt_workgroup,
1363 opt_password ? opt_password : "",
1364 CLI_FULL_CONNECTION_USE_KERBEROS,
1367 if (NT_STATUS_IS_ERR(nt_status)) {
1368 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1369 "for %s\n", servername, printername);
1374 /* Publish on AD server */
1376 ads_find_machine_acct(ads, &res, servername);
1378 if (ads_count_replies(ads, res) == 0) {
1379 d_fprintf(stderr, "Could not find machine account for server %s\n",
1385 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1386 srv_cn = ldap_explode_dn(srv_dn, 1);
1388 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1390 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1392 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1398 get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1401 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1402 if (!ADS_ERR_OK(rc)) {
1403 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1408 d_printf("published printer\n");
1414 static int net_ads_printer_remove(int argc, const char **argv)
1418 const char *servername;
1422 if (!(ads = ads_startup())) {
1427 return net_ads_printer_usage(argc, argv);
1431 servername = argv[1];
1433 servername = global_myname();
1436 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1438 if (!ADS_ERR_OK(rc)) {
1439 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1440 ads_msgfree(ads, res);
1445 if (ads_count_replies(ads, res) == 0) {
1446 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1447 ads_msgfree(ads, res);
1452 prt_dn = ads_get_dn(ads, res);
1453 ads_msgfree(ads, res);
1454 rc = ads_del_dn(ads, prt_dn);
1455 ads_memfree(ads, prt_dn);
1457 if (!ADS_ERR_OK(rc)) {
1458 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1467 static int net_ads_printer(int argc, const char **argv)
1469 struct functable func[] = {
1470 {"SEARCH", net_ads_printer_search},
1471 {"INFO", net_ads_printer_info},
1472 {"PUBLISH", net_ads_printer_publish},
1473 {"REMOVE", net_ads_printer_remove},
1477 return net_run_function(argc, argv, func, net_ads_printer_usage);
1481 static int net_ads_password(int argc, const char **argv)
1484 const char *auth_principal = opt_user_name;
1485 const char *auth_password = opt_password;
1487 char *new_password = NULL;
1492 if (opt_user_name == NULL || opt_password == NULL) {
1493 d_fprintf(stderr, "You must supply an administrator username/password\n");
1498 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1503 if (!strchr_m(user, '@')) {
1504 asprintf(&c, "%s@%s", argv[0], lp_realm());
1508 use_in_memory_ccache();
1509 c = strchr_m(auth_principal, '@');
1516 /* use the realm so we can eventually change passwords for users
1517 in realms other than default */
1518 if (!(ads = ads_init(realm, opt_workgroup, NULL))) {
1522 /* we don't actually need a full connect, but it's the easy way to
1523 fill in the KDC's addresss */
1526 if (!ads || !ads->config.realm) {
1527 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1532 new_password = (char *)argv[1];
1534 asprintf(&prompt, "Enter new password for %s:", user);
1535 new_password = getpass(prompt);
1539 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1540 auth_password, user, new_password, ads->auth.time_offset);
1541 if (!ADS_ERR_OK(ret)) {
1542 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1547 d_printf("Password change for %s completed.\n", user);
1553 int net_ads_changetrustpw(int argc, const char **argv)
1556 char *host_principal;
1560 if (!secrets_init()) {
1561 DEBUG(1,("Failed to initialise secrets database\n"));
1565 net_use_machine_password();
1567 use_in_memory_ccache();
1569 if (!(ads = ads_startup())) {
1573 fstrcpy(my_name, global_myname());
1574 strlower_m(my_name);
1575 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1576 d_printf("Changing password for principal: %s\n", host_principal);
1578 ret = ads_change_trust_account_password(ads, host_principal);
1580 if (!ADS_ERR_OK(ret)) {
1581 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1583 SAFE_FREE(host_principal);
1587 d_printf("Password change for principal %s succeeded.\n", host_principal);
1589 if (lp_use_kerberos_keytab()) {
1590 d_printf("Attempting to update system keytab with new password.\n");
1591 if (ads_keytab_create_default(ads)) {
1592 d_printf("Failed to update system keytab.\n");
1597 SAFE_FREE(host_principal);
1603 help for net ads search
1605 static int net_ads_search_usage(int argc, const char **argv)
1608 "\nnet ads search <expression> <attributes...>\n"\
1609 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1610 "The expression is a standard LDAP search expression, and the\n"\
1611 "attributes are a list of LDAP fields to show in the results\n\n"\
1612 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1614 net_common_flags_usage(argc, argv);
1620 general ADS search function. Useful in diagnosing problems in ADS
1622 static int net_ads_search(int argc, const char **argv)
1626 const char *ldap_exp;
1631 return net_ads_search_usage(argc, argv);
1634 if (!(ads = ads_startup())) {
1641 rc = ads_do_search_all(ads, ads->config.bind_path,
1643 ldap_exp, attrs, &res);
1644 if (!ADS_ERR_OK(rc)) {
1645 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1650 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1652 /* dump the results */
1655 ads_msgfree(ads, res);
1663 help for net ads search
1665 static int net_ads_dn_usage(int argc, const char **argv)
1668 "\nnet ads dn <dn> <attributes...>\n"\
1669 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1670 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1671 "to show in the results\n\n"\
1672 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1674 net_common_flags_usage(argc, argv);
1680 general ADS search function. Useful in diagnosing problems in ADS
1682 static int net_ads_dn(int argc, const char **argv)
1691 return net_ads_dn_usage(argc, argv);
1694 if (!(ads = ads_startup())) {
1701 rc = ads_do_search_all(ads, dn,
1703 "(objectclass=*)", attrs, &res);
1704 if (!ADS_ERR_OK(rc)) {
1705 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1710 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1712 /* dump the results */
1715 ads_msgfree(ads, res);
1722 help for net ads sid search
1724 static int net_ads_sid_usage(int argc, const char **argv)
1727 "\nnet ads sid <sid> <attributes...>\n"\
1728 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1729 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1730 "to show in the results\n\n"\
1731 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1733 net_common_flags_usage(argc, argv);
1739 general ADS search function. Useful in diagnosing problems in ADS
1741 static int net_ads_sid(int argc, const char **argv)
1745 const char *sid_string;
1751 return net_ads_sid_usage(argc, argv);
1754 if (!(ads = ads_startup())) {
1758 sid_string = argv[0];
1761 if (!string_to_sid(&sid, sid_string)) {
1762 d_fprintf(stderr, "could not convert sid\n");
1767 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1768 if (!ADS_ERR_OK(rc)) {
1769 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1774 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1776 /* dump the results */
1779 ads_msgfree(ads, res);
1786 static int net_ads_keytab_usage(int argc, const char **argv)
1789 "net ads keytab <COMMAND>\n"\
1790 "<COMMAND> can be either:\n"\
1791 " CREATE Creates a fresh keytab\n"\
1792 " ADD Adds new service principal\n"\
1793 " FLUSH Flushes out all keytab entries\n"\
1794 " HELP Prints this help message\n"\
1795 "The ADD command will take arguments, the other commands\n"\
1796 "will not take any arguments. The arguments given to ADD\n"\
1797 "should be a list of principals to add. For example, \n"\
1798 " net ads keytab add srv1 srv2\n"\
1799 "will add principals for the services srv1 and srv2 to the\n"\
1800 "system's keytab.\n"\
1806 static int net_ads_keytab_flush(int argc, const char **argv)
1811 if (!(ads = ads_startup())) {
1814 ret = ads_keytab_flush(ads);
1819 static int net_ads_keytab_add(int argc, const char **argv)
1825 d_printf("Processing principals to add...\n");
1826 if (!(ads = ads_startup())) {
1829 for (i = 0; i < argc; i++) {
1830 ret |= ads_keytab_add_entry(ads, argv[i]);
1836 static int net_ads_keytab_create(int argc, const char **argv)
1841 if (!(ads = ads_startup())) {
1844 ret = ads_keytab_create_default(ads);
1849 int net_ads_keytab(int argc, const char **argv)
1851 struct functable func[] = {
1852 {"CREATE", net_ads_keytab_create},
1853 {"ADD", net_ads_keytab_add},
1854 {"FLUSH", net_ads_keytab_flush},
1855 {"HELP", net_ads_keytab_usage},
1859 if (!lp_use_kerberos_keytab()) {
1860 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1861 use keytab functions.\n");
1864 return net_run_function(argc, argv, func, net_ads_keytab_usage);
1867 int net_ads_help(int argc, const char **argv)
1869 struct functable func[] = {
1870 {"USER", net_ads_user_usage},
1871 {"GROUP", net_ads_group_usage},
1872 {"PRINTER", net_ads_printer_usage},
1873 {"SEARCH", net_ads_search_usage},
1875 {"INFO", net_ads_info},
1876 {"JOIN", net_ads_join},
1877 {"JOIN2", net_ads_join2},
1878 {"LEAVE", net_ads_leave},
1879 {"STATUS", net_ads_status},
1880 {"PASSWORD", net_ads_password},
1881 {"CHANGETRUSTPW", net_ads_changetrustpw},
1886 return net_run_function(argc, argv, func, net_ads_usage);
1889 int net_ads(int argc, const char **argv)
1891 struct functable func[] = {
1892 {"INFO", net_ads_info},
1893 {"JOIN", net_ads_join},
1894 {"TESTJOIN", net_ads_testjoin},
1895 {"LEAVE", net_ads_leave},
1896 {"STATUS", net_ads_status},
1897 {"USER", net_ads_user},
1898 {"GROUP", net_ads_group},
1899 {"PASSWORD", net_ads_password},
1900 {"CHANGETRUSTPW", net_ads_changetrustpw},
1901 {"PRINTER", net_ads_printer},
1902 {"SEARCH", net_ads_search},
1904 {"SID", net_ads_sid},
1905 {"WORKGROUP", net_ads_workgroup},
1906 {"LOOKUP", net_ads_lookup},
1907 {"KEYTAB", net_ads_keytab},
1908 {"HELP", net_ads_help},
1912 return net_run_function(argc, argv, func, net_ads_usage);
1917 static int net_ads_noads(void)
1919 d_fprintf(stderr, "ADS support not compiled in\n");
1923 int net_ads_keytab(int argc, const char **argv)
1925 return net_ads_noads();
1928 int net_ads_usage(int argc, const char **argv)
1930 return net_ads_noads();
1933 int net_ads_help(int argc, const char **argv)
1935 return net_ads_noads();
1938 int net_ads_changetrustpw(int argc, const char **argv)
1940 return net_ads_noads();
1943 int net_ads_join(int argc, const char **argv)
1945 return net_ads_noads();
1948 int net_ads_user(int argc, const char **argv)
1950 return net_ads_noads();
1953 int net_ads_group(int argc, const char **argv)
1955 return net_ads_noads();
1958 /* this one shouldn't display a message */
1959 int net_ads_check(void)
1964 int net_ads(int argc, const char **argv)
1966 return net_ads_usage(argc, argv);