r19263: Be more accurate in telling what the sitename problem is in this DEBUG
[Samba.git] / source3 / utils / net_ads.c
blob7902248f94c89dd4f164a5eb81afa04a22755666
1 /*
2 Samba Unix/Linux SMB client library
3 net ads commands
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.
24 #include "includes.h"
25 #include "utils/net.h"
27 #ifdef HAVE_ADS
29 int net_ads_usage(int argc, const char **argv)
31 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
32 d_printf(" Join the local machine to a ADS realm\n");
33 d_printf("leave\n");
34 d_printf(" Remove the local machine from a ADS realm\n");
35 d_printf("testjoin\n");
36 d_printf(" Validates the machine account in the domain\n");
37 d_printf("user\n");
38 d_printf(" List, add, or delete users in the realm\n");
39 d_printf("group\n");
40 d_printf(" List, add, or delete groups in the realm\n");
41 d_printf("info\n");
42 d_printf(" Displays details regarding a specific AD server\n");
43 d_printf("status\n");
44 d_printf(" Display details regarding the machine's account in AD\n");
45 d_printf("lookup\n");
46 d_printf(" Performs CLDAP query of AD domain controllers\n");
47 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
48 d_printf(" Change a user's password using an admin account\n");
49 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
50 d_printf("changetrustpw\n");
51 d_printf(" Change the trust account password of this machine in the AD tree\n");
52 d_printf("printer [info | publish | remove] <printername> <servername>\n");
53 d_printf(" Lookup, add, or remove directory entry for a printer\n");
54 d_printf("{search,dn,sid}\n");
55 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
56 d_printf("keytab\n");
57 d_printf(" Manage a local keytab file based on the machine account in AD\n");
58 d_printf("dns\n");
59 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
60 d_printf(" (using the machine credentials)\n");
62 return -1;
65 /* when we do not have sufficient input parameters to contact a remote domain
66 * we always fall back to our own realm - Guenther*/
68 static const char *assume_own_realm(void)
70 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
71 return lp_realm();
74 return NULL;
78 do a cldap netlogon query
80 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
82 struct cldap_netlogon_reply reply;
84 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
85 d_fprintf(stderr, "CLDAP query failed!\n");
86 return -1;
89 d_printf("Information for Domain Controller: %s\n\n",
90 inet_ntoa(ads->ldap_ip));
92 d_printf("Response Type: ");
93 switch (reply.type) {
94 case SAMLOGON_AD_UNK_R:
95 d_printf("SAMLOGON\n");
96 break;
97 case SAMLOGON_AD_R:
98 d_printf("SAMLOGON_USER\n");
99 break;
100 default:
101 d_printf("0x%x\n", reply.type);
102 break;
104 d_printf("GUID: %s\n",
105 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
106 d_printf("Flags:\n"
107 "\tIs a PDC: %s\n"
108 "\tIs a GC of the forest: %s\n"
109 "\tIs an LDAP server: %s\n"
110 "\tSupports DS: %s\n"
111 "\tIs running a KDC: %s\n"
112 "\tIs running time services: %s\n"
113 "\tIs the closest DC: %s\n"
114 "\tIs writable: %s\n"
115 "\tHas a hardware clock: %s\n"
116 "\tIs a non-domain NC serviced by LDAP server: %s\n",
117 (reply.flags & ADS_PDC) ? "yes" : "no",
118 (reply.flags & ADS_GC) ? "yes" : "no",
119 (reply.flags & ADS_LDAP) ? "yes" : "no",
120 (reply.flags & ADS_DS) ? "yes" : "no",
121 (reply.flags & ADS_KDC) ? "yes" : "no",
122 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
123 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
124 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
125 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
126 (reply.flags & ADS_NDNC) ? "yes" : "no");
128 printf("Forest:\t\t\t%s\n", reply.forest);
129 printf("Domain:\t\t\t%s\n", reply.domain);
130 printf("Domain Controller:\t%s\n", reply.hostname);
132 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
133 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
135 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
136 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
138 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
139 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
141 d_printf("NT Version: %d\n", reply.version);
142 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
143 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
145 return 0;
150 this implements the CLDAP based netlogon lookup requests
151 for finding the domain controller of a ADS domain
153 static int net_ads_lookup(int argc, const char **argv)
155 ADS_STRUCT *ads;
157 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
158 d_fprintf(stderr, "Didn't find the cldap server!\n");
159 return -1;
162 if (!ads->config.realm) {
163 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
164 ads->ldap_port = 389;
167 return net_ads_cldap_netlogon(ads);
172 static int net_ads_info(int argc, const char **argv)
174 ADS_STRUCT *ads;
176 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
177 d_fprintf(stderr, "Didn't find the ldap server!\n");
178 return -1;
181 if (!ads || !ads->config.realm) {
182 d_fprintf(stderr, "Didn't find the ldap server!\n");
183 return -1;
186 /* Try to set the server's current time since we didn't do a full
187 TCP LDAP session initially */
189 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
190 d_fprintf( stderr, "Failed to get server's current time!\n");
193 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
194 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
195 d_printf("Realm: %s\n", ads->config.realm);
196 d_printf("Bind Path: %s\n", ads->config.bind_path);
197 d_printf("LDAP port: %d\n", ads->ldap_port);
198 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
200 d_printf("KDC server: %s\n", ads->auth.kdc_server );
201 d_printf("Server time offset: %d\n", ads->auth.time_offset );
203 return 0;
206 static void use_in_memory_ccache(void) {
207 /* Use in-memory credentials cache so we do not interfere with
208 * existing credentials */
209 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
212 static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
214 ADS_STRUCT *ads = NULL;
215 ADS_STATUS status;
216 BOOL need_password = False;
217 BOOL second_time = False;
218 char *cp;
219 const char *realm = NULL;
220 BOOL tried_closest_dc = False;
221 BOOL closest_dc = False;
222 BOOL site_matches = False;
224 /* lp_realm() should be handled by a command line param,
225 However, the join requires that realm be set in smb.conf
226 and compares our realm with the remote server's so this is
227 ok until someone needs more flexibility */
229 *ads_ret = NULL;
231 retry_connect:
232 if (only_own_domain) {
233 realm = lp_realm();
234 } else {
235 realm = assume_own_realm();
238 ads = ads_init(realm, opt_target_workgroup, opt_host);
240 if (!opt_user_name) {
241 opt_user_name = "administrator";
244 if (opt_user_specified) {
245 need_password = True;
248 retry:
249 if (!opt_password && need_password && !opt_machine_pass) {
250 char *prompt = NULL;
251 asprintf(&prompt,"%s's password: ", opt_user_name);
252 if (!prompt) {
253 ads_destroy(&ads);
254 return ADS_ERROR(LDAP_NO_MEMORY);
256 opt_password = getpass(prompt);
257 free(prompt);
260 if (opt_password) {
261 use_in_memory_ccache();
262 SAFE_FREE(ads->auth.password);
263 ads->auth.password = smb_xstrdup(opt_password);
266 ads->auth.flags |= auth_flags;
267 SAFE_FREE(ads->auth.user_name);
268 ads->auth.user_name = smb_xstrdup(opt_user_name);
271 * If the username is of the form "name@realm",
272 * extract the realm and convert to upper case.
273 * This is only used to establish the connection.
275 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
276 *cp++ = '\0';
277 SAFE_FREE(ads->auth.realm);
278 ads->auth.realm = smb_xstrdup(cp);
279 strupper_m(ads->auth.realm);
282 status = ads_connect(ads);
284 if (!ADS_ERR_OK(status)) {
285 if (!need_password && !second_time) {
286 need_password = True;
287 second_time = True;
288 goto retry;
289 } else {
290 ads_destroy(&ads);
291 return status;
295 /* when contacting our own domain, make sure we use the closest DC.
296 * This is done by reconnecting to ADS because only the first call to
297 * ads_connect will give us our own sitename */
299 closest_dc = (ads->config.flags & ADS_CLOSEST);
300 site_matches = ads_sitename_match(ads);
302 DEBUG(10,("ads_startup_int: DC %s closest DC\n", closest_dc ? "is":"is *NOT*"));
303 DEBUG(10,("ads_startup_int: sitenames %s match\n", site_matches ? "do":"do *NOT*"));
305 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
307 tried_closest_dc = True; /* avoid loop */
309 if (!closest_dc || !site_matches) {
311 namecache_delete(ads->server.realm, 0x1C);
312 namecache_delete(ads->server.workgroup, 0x1C);
314 ads_destroy(&ads);
315 ads = NULL;
317 goto retry_connect;
321 *ads_ret = ads;
322 return status;
325 ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
327 return ads_startup_int(only_own_domain, 0, ads);
330 ADS_STATUS ads_startup_nobind(BOOL only_own_domain, ADS_STRUCT **ads)
332 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
336 Check to see if connection can be made via ads.
337 ads_startup() stores the password in opt_password if it needs to so
338 that rpc or rap can use it without re-prompting.
340 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
342 ADS_STRUCT *ads;
343 ADS_STATUS status;
345 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
346 return -1;
349 ads->auth.flags |= ADS_AUTH_NO_BIND;
351 status = ads_connect(ads);
352 if ( !ADS_ERR_OK(status) ) {
353 return -1;
356 ads_destroy(&ads);
357 return 0;
360 int net_ads_check_our_domain(void)
362 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
365 int net_ads_check(void)
367 return net_ads_check_int(NULL, opt_workgroup, opt_host);
370 determine the netbios workgroup name for a domain
372 static int net_ads_workgroup(int argc, const char **argv)
374 ADS_STRUCT *ads;
375 struct cldap_netlogon_reply reply;
377 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
378 d_fprintf(stderr, "Didn't find the cldap server!\n");
379 return -1;
382 if (!ads->config.realm) {
383 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
384 ads->ldap_port = 389;
387 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
388 d_fprintf(stderr, "CLDAP query failed!\n");
389 return -1;
392 d_printf("Workgroup: %s\n", reply.netbios_domain);
394 ads_destroy(&ads);
396 return 0;
401 static BOOL usergrp_display(char *field, void **values, void *data_area)
403 char **disp_fields = (char **) data_area;
405 if (!field) { /* must be end of record */
406 if (disp_fields[0]) {
407 if (!strchr_m(disp_fields[0], '$')) {
408 if (disp_fields[1])
409 d_printf("%-21.21s %s\n",
410 disp_fields[0], disp_fields[1]);
411 else
412 d_printf("%s\n", disp_fields[0]);
415 SAFE_FREE(disp_fields[0]);
416 SAFE_FREE(disp_fields[1]);
417 return True;
419 if (!values) /* must be new field, indicate string field */
420 return True;
421 if (StrCaseCmp(field, "sAMAccountName") == 0) {
422 disp_fields[0] = SMB_STRDUP((char *) values[0]);
424 if (StrCaseCmp(field, "description") == 0)
425 disp_fields[1] = SMB_STRDUP((char *) values[0]);
426 return True;
429 static int net_ads_user_usage(int argc, const char **argv)
431 return net_help_user(argc, argv);
434 static int ads_user_add(int argc, const char **argv)
436 ADS_STRUCT *ads;
437 ADS_STATUS status;
438 char *upn, *userdn;
439 LDAPMessage *res=NULL;
440 int rc = -1;
442 if (argc < 1) return net_ads_user_usage(argc, argv);
444 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
445 return -1;
448 status = ads_find_user_acct(ads, &res, argv[0]);
450 if (!ADS_ERR_OK(status)) {
451 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
452 goto done;
455 if (ads_count_replies(ads, res)) {
456 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
457 goto done;
460 if (opt_container == NULL) {
461 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
464 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
466 if (!ADS_ERR_OK(status)) {
467 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
468 ads_errstr(status));
469 goto done;
472 /* if no password is to be set, we're done */
473 if (argc == 1) {
474 d_printf("User %s added\n", argv[0]);
475 rc = 0;
476 goto done;
479 /* try setting the password */
480 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
481 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
482 ads->auth.time_offset);
483 safe_free(upn);
484 if (ADS_ERR_OK(status)) {
485 d_printf("User %s added\n", argv[0]);
486 rc = 0;
487 goto done;
490 /* password didn't set, delete account */
491 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
492 argv[0], ads_errstr(status));
493 ads_msgfree(ads, res);
494 status=ads_find_user_acct(ads, &res, argv[0]);
495 if (ADS_ERR_OK(status)) {
496 userdn = ads_get_dn(ads, res);
497 ads_del_dn(ads, userdn);
498 ads_memfree(ads, userdn);
501 done:
502 if (res)
503 ads_msgfree(ads, res);
504 ads_destroy(&ads);
505 return rc;
508 static int ads_user_info(int argc, const char **argv)
510 ADS_STRUCT *ads;
511 ADS_STATUS rc;
512 LDAPMessage *res;
513 const char *attrs[] = {"memberOf", NULL};
514 char *searchstring=NULL;
515 char **grouplist;
516 char *escaped_user;
518 if (argc < 1) {
519 return net_ads_user_usage(argc, argv);
522 escaped_user = escape_ldap_string_alloc(argv[0]);
524 if (!escaped_user) {
525 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
526 return -1;
529 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
530 SAFE_FREE(escaped_user);
531 return -1;
534 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
535 rc = ads_search(ads, &res, searchstring, attrs);
536 safe_free(searchstring);
538 if (!ADS_ERR_OK(rc)) {
539 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
540 ads_destroy(&ads);
541 SAFE_FREE(escaped_user);
542 return -1;
545 grouplist = ldap_get_values((LDAP *)ads->ld,
546 (LDAPMessage *)res, "memberOf");
548 if (grouplist) {
549 int i;
550 char **groupname;
551 for (i=0;grouplist[i];i++) {
552 groupname = ldap_explode_dn(grouplist[i], 1);
553 d_printf("%s\n", groupname[0]);
554 ldap_value_free(groupname);
556 ldap_value_free(grouplist);
559 ads_msgfree(ads, res);
560 ads_destroy(&ads);
561 SAFE_FREE(escaped_user);
562 return 0;
565 static int ads_user_delete(int argc, const char **argv)
567 ADS_STRUCT *ads;
568 ADS_STATUS rc;
569 LDAPMessage *res;
570 char *userdn;
572 if (argc < 1) {
573 return net_ads_user_usage(argc, argv);
576 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
577 return -1;
580 rc = ads_find_user_acct(ads, &res, argv[0]);
581 if (!ADS_ERR_OK(rc)) {
582 d_printf("User %s does not exist.\n", argv[0]);
583 ads_destroy(&ads);
584 return -1;
586 userdn = ads_get_dn(ads, res);
587 ads_msgfree(ads, res);
588 rc = ads_del_dn(ads, userdn);
589 ads_memfree(ads, userdn);
590 if (!ADS_ERR_OK(rc)) {
591 d_printf("User %s deleted\n", argv[0]);
592 ads_destroy(&ads);
593 return 0;
595 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
596 ads_errstr(rc));
597 ads_destroy(&ads);
598 return -1;
601 int net_ads_user(int argc, const char **argv)
603 struct functable func[] = {
604 {"ADD", ads_user_add},
605 {"INFO", ads_user_info},
606 {"DELETE", ads_user_delete},
607 {NULL, NULL}
609 ADS_STRUCT *ads;
610 ADS_STATUS rc;
611 const char *shortattrs[] = {"sAMAccountName", NULL};
612 const char *longattrs[] = {"sAMAccountName", "description", NULL};
613 char *disp_fields[2] = {NULL, NULL};
615 if (argc == 0) {
616 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
617 return -1;
620 if (opt_long_list_entries)
621 d_printf("\nUser name Comment"\
622 "\n-----------------------------\n");
624 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
625 LDAP_SCOPE_SUBTREE,
626 "(objectCategory=user)",
627 opt_long_list_entries ? longattrs :
628 shortattrs, usergrp_display,
629 disp_fields);
630 ads_destroy(&ads);
631 return ADS_ERR_OK(rc) ? 0 : -1;
634 return net_run_function(argc, argv, func, net_ads_user_usage);
637 static int net_ads_group_usage(int argc, const char **argv)
639 return net_help_group(argc, argv);
642 static int ads_group_add(int argc, const char **argv)
644 ADS_STRUCT *ads;
645 ADS_STATUS status;
646 LDAPMessage *res=NULL;
647 int rc = -1;
649 if (argc < 1) {
650 return net_ads_group_usage(argc, argv);
653 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
654 return -1;
657 status = ads_find_user_acct(ads, &res, argv[0]);
659 if (!ADS_ERR_OK(status)) {
660 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
661 goto done;
664 if (ads_count_replies(ads, res)) {
665 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
666 ads_msgfree(ads, res);
667 goto done;
670 if (opt_container == NULL) {
671 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
674 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
676 if (ADS_ERR_OK(status)) {
677 d_printf("Group %s added\n", argv[0]);
678 rc = 0;
679 } else {
680 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
681 ads_errstr(status));
684 done:
685 if (res)
686 ads_msgfree(ads, res);
687 ads_destroy(&ads);
688 return rc;
691 static int ads_group_delete(int argc, const char **argv)
693 ADS_STRUCT *ads;
694 ADS_STATUS rc;
695 LDAPMessage *res;
696 char *groupdn;
698 if (argc < 1) {
699 return net_ads_group_usage(argc, argv);
702 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
703 return -1;
706 rc = ads_find_user_acct(ads, &res, argv[0]);
707 if (!ADS_ERR_OK(rc)) {
708 d_printf("Group %s does not exist.\n", argv[0]);
709 ads_destroy(&ads);
710 return -1;
712 groupdn = ads_get_dn(ads, res);
713 ads_msgfree(ads, res);
714 rc = ads_del_dn(ads, groupdn);
715 ads_memfree(ads, groupdn);
716 if (!ADS_ERR_OK(rc)) {
717 d_printf("Group %s deleted\n", argv[0]);
718 ads_destroy(&ads);
719 return 0;
721 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
722 ads_errstr(rc));
723 ads_destroy(&ads);
724 return -1;
727 int net_ads_group(int argc, const char **argv)
729 struct functable func[] = {
730 {"ADD", ads_group_add},
731 {"DELETE", ads_group_delete},
732 {NULL, NULL}
734 ADS_STRUCT *ads;
735 ADS_STATUS rc;
736 const char *shortattrs[] = {"sAMAccountName", NULL};
737 const char *longattrs[] = {"sAMAccountName", "description", NULL};
738 char *disp_fields[2] = {NULL, NULL};
740 if (argc == 0) {
741 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
742 return -1;
745 if (opt_long_list_entries)
746 d_printf("\nGroup name Comment"\
747 "\n-----------------------------\n");
748 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
749 LDAP_SCOPE_SUBTREE,
750 "(objectCategory=group)",
751 opt_long_list_entries ? longattrs :
752 shortattrs, usergrp_display,
753 disp_fields);
755 ads_destroy(&ads);
756 return ADS_ERR_OK(rc) ? 0 : -1;
758 return net_run_function(argc, argv, func, net_ads_group_usage);
761 static int net_ads_status(int argc, const char **argv)
763 ADS_STRUCT *ads;
764 ADS_STATUS rc;
765 LDAPMessage *res;
767 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
768 return -1;
771 rc = ads_find_machine_acct(ads, &res, global_myname());
772 if (!ADS_ERR_OK(rc)) {
773 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
774 ads_destroy(&ads);
775 return -1;
778 if (ads_count_replies(ads, res) == 0) {
779 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
780 ads_destroy(&ads);
781 return -1;
784 ads_dump(ads, res);
785 ads_destroy(&ads);
786 return 0;
789 /*******************************************************************
790 Leave an AD domain. Windows XP disables the machine account.
791 We'll try the same. The old code would do an LDAP delete.
792 That only worked using the machine creds because added the machine
793 with full control to the computer object's ACL.
794 *******************************************************************/
796 static int net_ads_leave(int argc, const char **argv)
798 ADS_STRUCT *ads = NULL;
799 ADS_STATUS adsret;
800 NTSTATUS status;
801 int ret = -1;
802 struct cli_state *cli = NULL;
803 TALLOC_CTX *ctx;
804 DOM_SID *dom_sid = NULL;
806 if (!secrets_init()) {
807 DEBUG(1,("Failed to initialise secrets database\n"));
808 return -1;
811 if (!(ctx = talloc_init("net_ads_leave"))) {
812 d_fprintf(stderr, "Could not initialise talloc context.\n");
813 return -1;
816 /* The finds a DC and takes care of getting the
817 user creds if necessary */
819 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
820 return -1;
823 /* make RPC calls here */
825 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
826 ads->config.ldap_server_name)) )
828 goto done;
831 saf_store( cli->server_domain, cli->desthost );
833 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
834 goto done;
837 status = netdom_leave_domain(ctx, cli, dom_sid);
839 /* Ty and delete it via LDAP - the old way we used to. */
841 adsret = ads_leave_realm(ads, global_myname());
842 if (ADS_ERR_OK(adsret)) {
843 d_printf("Deleted account for '%s' in realm '%s'\n",
844 global_myname(), ads->config.realm);
845 ret = 0;
846 } else {
847 /* We couldn't delete it - see if the disable succeeded. */
848 if (NT_STATUS_IS_OK(status)) {
849 d_printf("Disabled account for '%s' in realm '%s'\n",
850 global_myname(), ads->config.realm);
851 ret = 0;
852 } else {
853 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
854 global_myname(), ads->config.realm);
858 done:
860 if ( cli )
861 cli_shutdown(cli);
863 ads_destroy(&ads);
864 TALLOC_FREE( ctx );
866 return ret;
869 static NTSTATUS net_ads_join_ok(void)
871 ADS_STRUCT *ads = NULL;
872 ADS_STATUS status;
874 if (!secrets_init()) {
875 DEBUG(1,("Failed to initialise secrets database\n"));
876 return NT_STATUS_ACCESS_DENIED;
879 net_use_machine_password();
881 status = ads_startup(True, &ads);
882 if (!ADS_ERR_OK(status)) {
883 return ads_ntstatus(status);
886 ads_destroy(&ads);
887 return NT_STATUS_OK;
891 check that an existing join is OK
893 int net_ads_testjoin(int argc, const char **argv)
895 NTSTATUS status;
896 use_in_memory_ccache();
898 /* Display success or failure */
899 status = net_ads_join_ok();
900 if (!NT_STATUS_IS_OK(status)) {
901 fprintf(stderr,"Join to domain is not valid: %s\n",
902 get_friendly_nt_error_msg(status));
903 return -1;
906 printf("Join is OK\n");
907 return 0;
910 /*******************************************************************
911 Simple configu checks before beginning the join
912 ********************************************************************/
914 static NTSTATUS check_ads_config( void )
916 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
917 d_printf("Host is not configured as a member server.\n");
918 return NT_STATUS_INVALID_DOMAIN_ROLE;
921 if (strlen(global_myname()) > 15) {
922 d_printf("Our netbios name can be at most 15 chars long, "
923 "\"%s\" is %u chars long\n", global_myname(),
924 (unsigned int)strlen(global_myname()));
925 return NT_STATUS_NAME_TOO_LONG;
928 if ( lp_security() == SEC_ADS && !*lp_realm()) {
929 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
930 "join to succeed.\n");
931 return NT_STATUS_INVALID_PARAMETER;
934 if (!secrets_init()) {
935 DEBUG(1,("Failed to initialise secrets database\n"));
936 /* This is a good bet for failure of secrets_init ... */
937 return NT_STATUS_ACCESS_DENIED;
940 return NT_STATUS_OK;
943 /*******************************************************************
944 Do the domain join
945 ********************************************************************/
947 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
948 struct in_addr *ip, DOM_SID **dom_sid,
949 const char *password)
951 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
952 struct cli_state *cli = NULL;
954 ret = connect_to_ipc_krb5(&cli, ip, servername);
955 if ( !NT_STATUS_IS_OK(ret) ) {
956 goto done;
959 saf_store( cli->server_domain, cli->desthost );
961 ret = netdom_get_domain_sid( ctx, cli, dom_sid );
962 if ( !NT_STATUS_IS_OK(ret) ) {
963 goto done;
966 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
968 done:
969 if ( cli )
970 cli_shutdown(cli);
972 return ret;
975 /*******************************************************************
976 Set a machines dNSHostName and servicePrincipalName attributes
977 ********************************************************************/
979 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
981 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
982 char *new_dn;
983 ADS_MODLIST mods;
984 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
985 char *psp;
986 fstring my_fqdn;
987 LDAPMessage *res = NULL;
988 char *dn_string = NULL;
989 const char *machine_name = global_myname();
990 int count;
992 if ( !machine_name ) {
993 return ADS_ERROR(LDAP_NO_MEMORY);
996 /* Find our DN */
998 status = ads_find_machine_acct(ads_s, &res, machine_name);
999 if (!ADS_ERR_OK(status))
1000 return status;
1002 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1003 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1004 return ADS_ERROR(LDAP_NO_MEMORY);
1007 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1008 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1009 goto done;
1012 new_dn = talloc_strdup(ctx, dn_string);
1013 ads_memfree(ads_s, dn_string);
1014 if (!new_dn) {
1015 return ADS_ERROR(LDAP_NO_MEMORY);
1018 /* Windows only creates HOST/shortname & HOST/fqdn. */
1020 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1021 goto done;
1022 strupper_m(psp);
1023 servicePrincipalName[0] = psp;
1025 name_to_fqdn(my_fqdn, machine_name);
1026 strlower_m(my_fqdn);
1027 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1028 goto done;
1029 servicePrincipalName[1] = psp;
1031 if (!(mods = ads_init_mods(ctx))) {
1032 goto done;
1035 /* fields of primary importance */
1037 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1038 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1040 status = ads_gen_mod(ads_s, new_dn, mods);
1042 done:
1043 ads_msgfree(ads_s, res);
1045 return status;
1048 /*******************************************************************
1049 Set a machines dNSHostName and servicePrincipalName attributes
1050 ********************************************************************/
1052 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1054 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1055 char *new_dn;
1056 ADS_MODLIST mods;
1057 LDAPMessage *res = NULL;
1058 char *dn_string = NULL;
1059 const char *machine_name = global_myname();
1060 int count;
1062 if ( !machine_name ) {
1063 return ADS_ERROR(LDAP_NO_MEMORY);
1066 /* Find our DN */
1068 status = ads_find_machine_acct(ads_s, &res, machine_name);
1069 if (!ADS_ERR_OK(status))
1070 return status;
1072 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1073 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1074 return ADS_ERROR(LDAP_NO_MEMORY);
1077 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1078 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1079 goto done;
1082 new_dn = talloc_strdup(ctx, dn_string);
1083 ads_memfree(ads_s, dn_string);
1084 if (!new_dn) {
1085 return ADS_ERROR(LDAP_NO_MEMORY);
1088 /* now do the mods */
1090 if (!(mods = ads_init_mods(ctx))) {
1091 goto done;
1094 /* fields of primary importance */
1096 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1098 status = ads_gen_mod(ads_s, new_dn, mods);
1100 done:
1101 ads_msgfree(ads_s, res);
1103 return status;
1106 /*******************************************************************
1107 join a domain using ADS (LDAP mods)
1108 ********************************************************************/
1110 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1112 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1113 char *dn, *ou_str;
1114 LDAPMessage *res = NULL;
1116 ou_str = ads_ou_string(ads, ou);
1117 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1118 free(ou_str);
1120 rc = ads_search_dn(ads, &res, dn, NULL);
1121 ads_msgfree(ads, res);
1123 if (ADS_ERR_OK(rc)) {
1124 /* Attempt to create the machine account and bail if this fails.
1125 Assume that the admin wants exactly what they requested */
1127 rc = ads_create_machine_acct( ads, global_myname(), dn );
1128 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1129 rc = ADS_SUCCESS;
1133 SAFE_FREE( dn );
1135 return rc;
1138 /************************************************************************
1139 ************************************************************************/
1141 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1143 uint32 domain_func;
1144 ADS_STATUS status;
1145 fstring salt;
1146 char *std_salt;
1147 LDAPMessage *res = NULL;
1148 const char *machine_name = global_myname();
1150 status = ads_domain_func_level( ads, &domain_func );
1151 if ( !ADS_ERR_OK(status) ) {
1152 DEBUG(2,("Failed to determine domain functional level!\n"));
1153 return False;
1156 /* go ahead and setup the default salt */
1158 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1159 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1160 return False;
1163 fstrcpy( salt, std_salt );
1164 SAFE_FREE( std_salt );
1166 /* if it's a Windows functional domain, we have to look for the UPN */
1168 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1169 char *upn;
1170 int count;
1172 status = ads_find_machine_acct(ads, &res, machine_name);
1173 if (!ADS_ERR_OK(status)) {
1174 return False;
1177 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1178 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1179 return False;
1182 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1183 if ( upn ) {
1184 fstrcpy( salt, upn );
1187 ads_msgfree(ads, res);
1190 return kerberos_secrets_store_des_salt( salt );
1193 /*******************************************************************
1194 Send a DNS update request
1195 *******************************************************************/
1197 #if defined(WITH_DNS_UPDATES)
1198 static BOOL net_update_dns( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1200 int num_addrs;
1201 struct in_addr *iplist = NULL;
1202 struct dns_rr_ns *nameservers = NULL;
1203 int ns_count = 0;
1204 int ret = 0;
1205 NTSTATUS dns_status;
1206 fstring machine_name;
1207 fstring dns_server;
1208 const char *dnsdomain;
1209 ADS_STRUCT *ads_s = NULL;
1211 name_to_fqdn( machine_name, global_myname() );
1212 strlower_m( machine_name );
1214 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1215 d_printf("No DNS domain configured for %s. Unable to perform DNS Update.\n",
1216 machine_name);
1217 goto done;
1219 dnsdomain++;
1221 dns_status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1222 if ( !NT_STATUS_IS_OK(dns_status) || (ns_count == 0)) {
1223 DEBUG(3,("net_ads_join: Failed to find name server for the %s realm\n",
1224 ads->config.realm));
1225 goto done;
1228 /* Get our ip address (not the 127.0.0.x address but a real ip address) */
1230 num_addrs = get_my_ip_address( &iplist );
1231 if ( num_addrs <= 0 ) {
1232 DEBUG(4,("net_ads_join: Failed to find my non-loopback IP addresses!\n"));
1233 ret = -1;
1234 goto done;
1237 /* Drop the user creds */
1239 ads_kdestroy( NULL );
1241 ads_s = ads_init( ads->server.realm, ads->server.workgroup, ads->server.ldap_server );
1242 if ( !ads_s ) {
1243 DEBUG(1,("net_ads_join: ads_init() failed!\n"));
1244 ret = -1;
1245 goto done;
1248 /* kinit with the machine password */
1250 asprintf( &ads_s->auth.user_name, "%s$", global_myname() );
1251 ads_s->auth.password = secrets_fetch_machine_password( lp_workgroup(), NULL, NULL );
1252 ads_s->auth.realm = SMB_STRDUP( lp_realm() );
1253 ads_kinit_password( ads_s );
1255 /* Now perform the dns update - we'll try non-secure and if we fail, we'll
1256 follow it up with a secure update */
1258 fstrcpy( dns_server, nameservers[0].hostname );
1260 ret = DoDNSUpdate(dns_server, dnsdomain, machine_name, iplist, num_addrs );
1261 if ( ret ) {
1262 DEBUG(1, ("Error creating dns update!\n"));
1265 done:
1266 SAFE_FREE( iplist );
1267 if ( ads_s )
1268 ads_destroy( &ads_s );
1270 return (ret == 0);
1272 #endif
1275 /*******************************************************************
1276 utility function to parse an integer parameter from
1277 "parameter = value"
1278 **********************************************************/
1279 static char* get_string_param( const char* param )
1281 char *p;
1283 if ( (p = strchr( param, '=' )) == NULL )
1284 return NULL;
1286 return (p+1);
1289 /*******************************************************************
1290 ********************************************************************/
1292 static int net_ads_join_usage(int argc, const char **argv)
1294 d_printf("net ads join [options]\n");
1295 d_printf("Valid options:\n");
1296 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1297 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1298 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1299 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1300 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1302 return -1;
1305 /*******************************************************************
1306 ********************************************************************/
1308 int net_ads_join(int argc, const char **argv)
1310 ADS_STRUCT *ads = NULL;
1311 ADS_STATUS status;
1312 NTSTATUS nt_status;
1313 char *machine_account = NULL;
1314 const char *short_domain_name = NULL;
1315 char *tmp_password, *password;
1316 struct cldap_netlogon_reply cldap_reply;
1317 TALLOC_CTX *ctx = NULL;
1318 DOM_SID *domain_sid = NULL;
1319 BOOL createupn = False;
1320 const char *machineupn = NULL;
1321 const char *create_in_ou = NULL;
1322 int i;
1324 nt_status = check_ads_config();
1325 if (!NT_STATUS_IS_OK(nt_status)) {
1326 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1327 goto fail;
1330 status = ads_startup(True, &ads);
1331 if (!ADS_ERR_OK(status)) {
1332 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1333 nt_status = ads_ntstatus(status);
1334 goto fail;
1337 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1338 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1339 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1340 lp_realm());
1341 nt_status = NT_STATUS_INVALID_PARAMETER;
1342 goto fail;
1345 if (!(ctx = talloc_init("net_ads_join"))) {
1346 d_fprintf(stderr, "Could not initialise talloc context.\n");
1347 nt_status = NT_STATUS_NO_MEMORY;
1348 goto fail;
1351 /* process additional command line args */
1353 for ( i=0; i<argc; i++ ) {
1354 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1355 createupn = True;
1356 machineupn = get_string_param(argv[i]);
1358 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1359 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1360 d_fprintf(stderr, "Please supply a valid OU path\n");
1361 nt_status = NT_STATUS_INVALID_PARAMETER;
1362 goto fail;
1365 else {
1366 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1367 nt_status = NT_STATUS_INVALID_PARAMETER;
1368 goto fail;
1372 /* If we were given an OU, try to create the machine in
1373 the OU account first and then do the normal RPC join */
1375 if ( create_in_ou ) {
1376 status = net_precreate_machine_acct( ads, create_in_ou );
1377 if ( !ADS_ERR_OK(status) ) {
1378 d_fprintf( stderr, "Failed to pre-create the machine object "
1379 "in OU %s.\n", argv[0]);
1380 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1381 ads_errstr(status)));
1382 nt_status = ads_ntstatus(status);
1383 goto fail;
1387 /* Do the domain join here */
1389 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1390 password = talloc_strdup(ctx, tmp_password);
1392 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1393 &ads->ldap_ip, &domain_sid, password);
1394 if ( !NT_STATUS_IS_OK(nt_status) ) {
1395 DEBUG(1, ("call of net_join_domain failed: %s\n",
1396 get_friendly_nt_error_msg(nt_status)));
1397 goto fail;
1400 /* Check the short name of the domain */
1402 ZERO_STRUCT( cldap_reply );
1404 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1405 ads->server.realm, &cldap_reply ) )
1407 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1408 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1409 d_printf("The workgroup in smb.conf does not match the short\n");
1410 d_printf("domain name obtained from the server.\n");
1411 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1412 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1414 } else {
1415 short_domain_name = lp_workgroup();
1418 d_printf("Using short domain name -- %s\n", short_domain_name);
1420 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1421 value from smb.conf and the string returned from the server. The former is
1422 neede to bootstrap winbindd's first connection to the DC to get the real
1423 short domain name --jerry */
1425 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1426 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1428 /* issue an internal error here for now.
1429 * everything else would mean changing tdb routines. */
1430 nt_status = NT_STATUS_INTERNAL_ERROR;
1431 goto fail;
1434 /* Verify that everything is ok */
1436 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1437 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1438 goto fail;
1441 /* create the dNSHostName & servicePrincipalName values */
1443 status = net_set_machine_spn( ctx, ads );
1444 if ( !ADS_ERR_OK(status) ) {
1446 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1447 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1448 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1450 /* Disable the machine account in AD. Better to fail than to leave
1451 a confused admin. */
1453 if ( net_ads_leave( 0, NULL ) != 0 ) {
1454 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1457 /* clear out the machine password */
1459 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1460 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1462 nt_status = ads_ntstatus(status);
1463 goto fail;
1466 if ( !net_derive_salting_principal( ctx, ads ) ) {
1467 DEBUG(1,("Failed to determine salting principal\n"));
1468 goto fail;
1471 if ( createupn ) {
1472 pstring upn;
1474 /* default to using the short UPN name */
1475 if ( !machineupn ) {
1476 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1477 ads->config.realm );
1478 machineupn = upn;
1481 status = net_set_machine_upn( ctx, ads, machineupn );
1482 if ( !ADS_ERR_OK(status) ) {
1483 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1487 /* Now build the keytab, using the same ADS connection */
1488 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1489 DEBUG(1,("Error creating host keytab!\n"));
1492 #if defined(WITH_DNS_UPDATES)
1493 /* We enter this block with user creds */
1495 if ( !net_update_dns( ctx, ads ) ) {
1496 d_fprintf( stderr, "DNS update failed!\n" );
1499 /* exit from this block using machine creds */
1500 #endif
1502 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1504 SAFE_FREE(machine_account);
1505 TALLOC_FREE( ctx );
1506 ads_destroy(&ads);
1508 return 0;
1510 fail:
1511 /* issue an overall failure message at the end. */
1512 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1514 SAFE_FREE(machine_account);
1515 TALLOC_FREE( ctx );
1516 ads_destroy(&ads);
1518 return -1;
1522 /*******************************************************************
1523 ********************************************************************/
1525 static int net_ads_dns_usage(int argc, const char **argv)
1527 #if defined(WITH_DNS_UPDATES)
1528 d_printf("net ads dns <command>\n");
1529 d_printf("Valid commands:\n");
1530 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1532 return 0;
1533 #else
1534 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1535 return -1;
1536 #endif
1539 /*******************************************************************
1540 ********************************************************************/
1542 static int net_ads_dns(int argc, const char **argv)
1544 #if defined(WITH_DNS_UPDATES)
1545 ADS_STRUCT *ads;
1546 ADS_STATUS status;
1547 TALLOC_CTX *ctx;
1548 BOOL register_dns = False;
1549 int i;
1551 status = ads_startup(True, &ads);
1552 if ( !ADS_ERR_OK(status) ) {
1553 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1554 return -1;
1557 if (!(ctx = talloc_init("net_ads_dns"))) {
1558 DEBUG(0, ("Could not initialise talloc context\n"));
1559 return -1;
1562 /* process additional command line args */
1564 for ( i=0; i<argc; i++ ) {
1565 if ( strequal(argv[i], "register") ) {
1566 register_dns = True;
1568 else {
1569 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1570 return -1;
1574 if ( !net_update_dns( ctx, ads ) ) {
1575 d_fprintf( stderr, "DNS update failed!\n" );
1576 ads_destroy( &ads );
1577 TALLOC_FREE( ctx );
1578 return -1;
1581 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1583 ads_destroy(&ads);
1584 TALLOC_FREE( ctx );
1586 return 0;
1587 #else
1588 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1589 return -1;
1590 #endif
1593 /*******************************************************************
1594 ********************************************************************/
1596 int net_ads_printer_usage(int argc, const char **argv)
1598 d_printf(
1599 "\nnet ads printer search <printer>"
1600 "\n\tsearch for a printer in the directory\n"
1601 "\nnet ads printer info <printer> <server>"
1602 "\n\tlookup info in directory for printer on server"
1603 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1604 "\nnet ads printer publish <printername>"
1605 "\n\tpublish printer in directory"
1606 "\n\t(note: printer name is required)\n"
1607 "\nnet ads printer remove <printername>"
1608 "\n\tremove printer from directory"
1609 "\n\t(note: printer name is required)\n");
1610 return -1;
1613 /*******************************************************************
1614 ********************************************************************/
1616 static int net_ads_printer_search(int argc, const char **argv)
1618 ADS_STRUCT *ads;
1619 ADS_STATUS rc;
1620 LDAPMessage *res = NULL;
1622 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1623 return -1;
1626 rc = ads_find_printers(ads, &res);
1628 if (!ADS_ERR_OK(rc)) {
1629 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1630 ads_msgfree(ads, res);
1631 ads_destroy(&ads);
1632 return -1;
1635 if (ads_count_replies(ads, res) == 0) {
1636 d_fprintf(stderr, "No results found\n");
1637 ads_msgfree(ads, res);
1638 ads_destroy(&ads);
1639 return -1;
1642 ads_dump(ads, res);
1643 ads_msgfree(ads, res);
1644 ads_destroy(&ads);
1645 return 0;
1648 static int net_ads_printer_info(int argc, const char **argv)
1650 ADS_STRUCT *ads;
1651 ADS_STATUS rc;
1652 const char *servername, *printername;
1653 LDAPMessage *res = NULL;
1655 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1656 return -1;
1659 if (argc > 0) {
1660 printername = argv[0];
1661 } else {
1662 printername = "*";
1665 if (argc > 1) {
1666 servername = argv[1];
1667 } else {
1668 servername = global_myname();
1671 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1673 if (!ADS_ERR_OK(rc)) {
1674 d_fprintf(stderr, "Server '%s' not found: %s\n",
1675 servername, ads_errstr(rc));
1676 ads_msgfree(ads, res);
1677 ads_destroy(&ads);
1678 return -1;
1681 if (ads_count_replies(ads, res) == 0) {
1682 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1683 ads_msgfree(ads, res);
1684 ads_destroy(&ads);
1685 return -1;
1688 ads_dump(ads, res);
1689 ads_msgfree(ads, res);
1690 ads_destroy(&ads);
1692 return 0;
1695 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1696 void *buf, size_t len)
1698 return;
1701 static int net_ads_printer_publish(int argc, const char **argv)
1703 ADS_STRUCT *ads;
1704 ADS_STATUS rc;
1705 const char *servername, *printername;
1706 struct cli_state *cli;
1707 struct rpc_pipe_client *pipe_hnd;
1708 struct in_addr server_ip;
1709 NTSTATUS nt_status;
1710 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1711 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1712 char *prt_dn, *srv_dn, **srv_cn;
1713 LDAPMessage *res = NULL;
1715 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1716 return -1;
1719 if (argc < 1) {
1720 return net_ads_printer_usage(argc, argv);
1723 printername = argv[0];
1725 if (argc == 2) {
1726 servername = argv[1];
1727 } else {
1728 servername = global_myname();
1731 /* Get printer data from SPOOLSS */
1733 resolve_name(servername, &server_ip, 0x20);
1735 nt_status = cli_full_connection(&cli, global_myname(), servername,
1736 &server_ip, 0,
1737 "IPC$", "IPC",
1738 opt_user_name, opt_workgroup,
1739 opt_password ? opt_password : "",
1740 CLI_FULL_CONNECTION_USE_KERBEROS,
1741 Undefined, NULL);
1743 if (NT_STATUS_IS_ERR(nt_status)) {
1744 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1745 "for %s\n", servername, printername);
1746 ads_destroy(&ads);
1747 return -1;
1750 /* Publish on AD server */
1752 ads_find_machine_acct(ads, &res, servername);
1754 if (ads_count_replies(ads, res) == 0) {
1755 d_fprintf(stderr, "Could not find machine account for server %s\n",
1756 servername);
1757 ads_destroy(&ads);
1758 return -1;
1761 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1762 srv_cn = ldap_explode_dn(srv_dn, 1);
1764 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1766 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1767 if (!pipe_hnd) {
1768 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1769 servername);
1770 ads_destroy(&ads);
1771 return -1;
1774 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1775 printername))) {
1776 ads_destroy(&ads);
1777 return -1;
1780 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1781 if (!ADS_ERR_OK(rc)) {
1782 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1783 ads_destroy(&ads);
1784 return -1;
1787 d_printf("published printer\n");
1788 ads_destroy(&ads);
1790 return 0;
1793 static int net_ads_printer_remove(int argc, const char **argv)
1795 ADS_STRUCT *ads;
1796 ADS_STATUS rc;
1797 const char *servername;
1798 char *prt_dn;
1799 LDAPMessage *res = NULL;
1801 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1802 return -1;
1805 if (argc < 1) {
1806 return net_ads_printer_usage(argc, argv);
1809 if (argc > 1) {
1810 servername = argv[1];
1811 } else {
1812 servername = global_myname();
1815 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1817 if (!ADS_ERR_OK(rc)) {
1818 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1819 ads_msgfree(ads, res);
1820 ads_destroy(&ads);
1821 return -1;
1824 if (ads_count_replies(ads, res) == 0) {
1825 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1826 ads_msgfree(ads, res);
1827 ads_destroy(&ads);
1828 return -1;
1831 prt_dn = ads_get_dn(ads, res);
1832 ads_msgfree(ads, res);
1833 rc = ads_del_dn(ads, prt_dn);
1834 ads_memfree(ads, prt_dn);
1836 if (!ADS_ERR_OK(rc)) {
1837 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1838 ads_destroy(&ads);
1839 return -1;
1842 ads_destroy(&ads);
1843 return 0;
1846 static int net_ads_printer(int argc, const char **argv)
1848 struct functable func[] = {
1849 {"SEARCH", net_ads_printer_search},
1850 {"INFO", net_ads_printer_info},
1851 {"PUBLISH", net_ads_printer_publish},
1852 {"REMOVE", net_ads_printer_remove},
1853 {NULL, NULL}
1856 return net_run_function(argc, argv, func, net_ads_printer_usage);
1860 static int net_ads_password(int argc, const char **argv)
1862 ADS_STRUCT *ads;
1863 const char *auth_principal = opt_user_name;
1864 const char *auth_password = opt_password;
1865 char *realm = NULL;
1866 char *new_password = NULL;
1867 char *c, *prompt;
1868 const char *user;
1869 ADS_STATUS ret;
1871 if (opt_user_name == NULL || opt_password == NULL) {
1872 d_fprintf(stderr, "You must supply an administrator username/password\n");
1873 return -1;
1876 if (argc < 1) {
1877 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1878 return -1;
1881 user = argv[0];
1882 if (!strchr_m(user, '@')) {
1883 asprintf(&c, "%s@%s", argv[0], lp_realm());
1884 user = c;
1887 use_in_memory_ccache();
1888 c = strchr_m(auth_principal, '@');
1889 if (c) {
1890 realm = ++c;
1891 } else {
1892 realm = lp_realm();
1895 /* use the realm so we can eventually change passwords for users
1896 in realms other than default */
1897 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1898 return -1;
1901 /* we don't actually need a full connect, but it's the easy way to
1902 fill in the KDC's addresss */
1903 ads_connect(ads);
1905 if (!ads || !ads->config.realm) {
1906 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1907 return -1;
1910 if (argv[1]) {
1911 new_password = (char *)argv[1];
1912 } else {
1913 asprintf(&prompt, "Enter new password for %s:", user);
1914 new_password = getpass(prompt);
1915 free(prompt);
1918 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1919 auth_password, user, new_password, ads->auth.time_offset);
1920 if (!ADS_ERR_OK(ret)) {
1921 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1922 ads_destroy(&ads);
1923 return -1;
1926 d_printf("Password change for %s completed.\n", user);
1927 ads_destroy(&ads);
1929 return 0;
1932 int net_ads_changetrustpw(int argc, const char **argv)
1934 ADS_STRUCT *ads;
1935 char *host_principal;
1936 fstring my_name;
1937 ADS_STATUS ret;
1939 if (!secrets_init()) {
1940 DEBUG(1,("Failed to initialise secrets database\n"));
1941 return -1;
1944 net_use_machine_password();
1946 use_in_memory_ccache();
1948 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1949 return -1;
1952 fstrcpy(my_name, global_myname());
1953 strlower_m(my_name);
1954 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1955 d_printf("Changing password for principal: %s\n", host_principal);
1957 ret = ads_change_trust_account_password(ads, host_principal);
1959 if (!ADS_ERR_OK(ret)) {
1960 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1961 ads_destroy(&ads);
1962 SAFE_FREE(host_principal);
1963 return -1;
1966 d_printf("Password change for principal %s succeeded.\n", host_principal);
1968 if (lp_use_kerberos_keytab()) {
1969 d_printf("Attempting to update system keytab with new password.\n");
1970 if (ads_keytab_create_default(ads)) {
1971 d_printf("Failed to update system keytab.\n");
1975 ads_destroy(&ads);
1976 SAFE_FREE(host_principal);
1978 return 0;
1982 help for net ads search
1984 static int net_ads_search_usage(int argc, const char **argv)
1986 d_printf(
1987 "\nnet ads search <expression> <attributes...>\n"\
1988 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1989 "The expression is a standard LDAP search expression, and the\n"\
1990 "attributes are a list of LDAP fields to show in the results\n\n"\
1991 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1993 net_common_flags_usage(argc, argv);
1994 return -1;
1999 general ADS search function. Useful in diagnosing problems in ADS
2001 static int net_ads_search(int argc, const char **argv)
2003 ADS_STRUCT *ads;
2004 ADS_STATUS rc;
2005 const char *ldap_exp;
2006 const char **attrs;
2007 LDAPMessage *res = NULL;
2009 if (argc < 1) {
2010 return net_ads_search_usage(argc, argv);
2013 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2014 return -1;
2017 ldap_exp = argv[0];
2018 attrs = (argv + 1);
2020 rc = ads_do_search_all(ads, ads->config.bind_path,
2021 LDAP_SCOPE_SUBTREE,
2022 ldap_exp, attrs, &res);
2023 if (!ADS_ERR_OK(rc)) {
2024 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2025 ads_destroy(&ads);
2026 return -1;
2029 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2031 /* dump the results */
2032 ads_dump(ads, res);
2034 ads_msgfree(ads, res);
2035 ads_destroy(&ads);
2037 return 0;
2042 help for net ads search
2044 static int net_ads_dn_usage(int argc, const char **argv)
2046 d_printf(
2047 "\nnet ads dn <dn> <attributes...>\n"\
2048 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2049 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2050 "to show in the results\n\n"\
2051 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2053 net_common_flags_usage(argc, argv);
2054 return -1;
2059 general ADS search function. Useful in diagnosing problems in ADS
2061 static int net_ads_dn(int argc, const char **argv)
2063 ADS_STRUCT *ads;
2064 ADS_STATUS rc;
2065 const char *dn;
2066 const char **attrs;
2067 LDAPMessage *res = NULL;
2069 if (argc < 1) {
2070 return net_ads_dn_usage(argc, argv);
2073 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2074 return -1;
2077 dn = argv[0];
2078 attrs = (argv + 1);
2080 rc = ads_do_search_all(ads, dn,
2081 LDAP_SCOPE_BASE,
2082 "(objectclass=*)", attrs, &res);
2083 if (!ADS_ERR_OK(rc)) {
2084 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2085 ads_destroy(&ads);
2086 return -1;
2089 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2091 /* dump the results */
2092 ads_dump(ads, res);
2094 ads_msgfree(ads, res);
2095 ads_destroy(&ads);
2097 return 0;
2101 help for net ads sid search
2103 static int net_ads_sid_usage(int argc, const char **argv)
2105 d_printf(
2106 "\nnet ads sid <sid> <attributes...>\n"\
2107 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2108 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2109 "to show in the results\n\n"\
2110 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2112 net_common_flags_usage(argc, argv);
2113 return -1;
2118 general ADS search function. Useful in diagnosing problems in ADS
2120 static int net_ads_sid(int argc, const char **argv)
2122 ADS_STRUCT *ads;
2123 ADS_STATUS rc;
2124 const char *sid_string;
2125 const char **attrs;
2126 LDAPMessage *res = NULL;
2127 DOM_SID sid;
2129 if (argc < 1) {
2130 return net_ads_sid_usage(argc, argv);
2133 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2134 return -1;
2137 sid_string = argv[0];
2138 attrs = (argv + 1);
2140 if (!string_to_sid(&sid, sid_string)) {
2141 d_fprintf(stderr, "could not convert sid\n");
2142 ads_destroy(&ads);
2143 return -1;
2146 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2147 if (!ADS_ERR_OK(rc)) {
2148 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2149 ads_destroy(&ads);
2150 return -1;
2153 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2155 /* dump the results */
2156 ads_dump(ads, res);
2158 ads_msgfree(ads, res);
2159 ads_destroy(&ads);
2161 return 0;
2165 static int net_ads_keytab_usage(int argc, const char **argv)
2167 d_printf(
2168 "net ads keytab <COMMAND>\n"\
2169 "<COMMAND> can be either:\n"\
2170 " CREATE Creates a fresh keytab\n"\
2171 " ADD Adds new service principal\n"\
2172 " FLUSH Flushes out all keytab entries\n"\
2173 " HELP Prints this help message\n"\
2174 "The ADD command will take arguments, the other commands\n"\
2175 "will not take any arguments. The arguments given to ADD\n"\
2176 "should be a list of principals to add. For example, \n"\
2177 " net ads keytab add srv1 srv2\n"\
2178 "will add principals for the services srv1 and srv2 to the\n"\
2179 "system's keytab.\n"\
2180 "\n"
2182 return -1;
2185 static int net_ads_keytab_flush(int argc, const char **argv)
2187 int ret;
2188 ADS_STRUCT *ads;
2190 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2191 return -1;
2193 ret = ads_keytab_flush(ads);
2194 ads_destroy(&ads);
2195 return ret;
2198 static int net_ads_keytab_add(int argc, const char **argv)
2200 int i;
2201 int ret = 0;
2202 ADS_STRUCT *ads;
2204 d_printf("Processing principals to add...\n");
2205 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2206 return -1;
2208 for (i = 0; i < argc; i++) {
2209 ret |= ads_keytab_add_entry(ads, argv[i]);
2211 ads_destroy(&ads);
2212 return ret;
2215 static int net_ads_keytab_create(int argc, const char **argv)
2217 ADS_STRUCT *ads;
2218 int ret;
2220 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2221 return -1;
2223 ret = ads_keytab_create_default(ads);
2224 ads_destroy(&ads);
2225 return ret;
2228 int net_ads_keytab(int argc, const char **argv)
2230 struct functable func[] = {
2231 {"CREATE", net_ads_keytab_create},
2232 {"ADD", net_ads_keytab_add},
2233 {"FLUSH", net_ads_keytab_flush},
2234 {"HELP", net_ads_keytab_usage},
2235 {NULL, NULL}
2238 if (!lp_use_kerberos_keytab()) {
2239 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2240 use keytab functions.\n");
2243 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2246 int net_ads_help(int argc, const char **argv)
2248 struct functable func[] = {
2249 {"USER", net_ads_user_usage},
2250 {"GROUP", net_ads_group_usage},
2251 {"PRINTER", net_ads_printer_usage},
2252 {"SEARCH", net_ads_search_usage},
2253 {"INFO", net_ads_info},
2254 {"JOIN", net_ads_join_usage},
2255 {"DNS", net_ads_dns_usage},
2256 {"LEAVE", net_ads_leave},
2257 {"STATUS", net_ads_status},
2258 {"PASSWORD", net_ads_password},
2259 {"CHANGETRUSTPW", net_ads_changetrustpw},
2260 {NULL, NULL}
2263 return net_run_function(argc, argv, func, net_ads_usage);
2266 int net_ads(int argc, const char **argv)
2268 struct functable func[] = {
2269 {"INFO", net_ads_info},
2270 {"JOIN", net_ads_join},
2271 {"TESTJOIN", net_ads_testjoin},
2272 {"LEAVE", net_ads_leave},
2273 {"STATUS", net_ads_status},
2274 {"USER", net_ads_user},
2275 {"GROUP", net_ads_group},
2276 {"DNS", net_ads_dns},
2277 {"PASSWORD", net_ads_password},
2278 {"CHANGETRUSTPW", net_ads_changetrustpw},
2279 {"PRINTER", net_ads_printer},
2280 {"SEARCH", net_ads_search},
2281 {"DN", net_ads_dn},
2282 {"SID", net_ads_sid},
2283 {"WORKGROUP", net_ads_workgroup},
2284 {"LOOKUP", net_ads_lookup},
2285 {"KEYTAB", net_ads_keytab},
2286 {"GPO", net_ads_gpo},
2287 {"HELP", net_ads_help},
2288 {NULL, NULL}
2291 return net_run_function(argc, argv, func, net_ads_usage);
2294 #else
2296 static int net_ads_noads(void)
2298 d_fprintf(stderr, "ADS support not compiled in\n");
2299 return -1;
2302 int net_ads_keytab(int argc, const char **argv)
2304 return net_ads_noads();
2307 int net_ads_usage(int argc, const char **argv)
2309 return net_ads_noads();
2312 int net_ads_help(int argc, const char **argv)
2314 return net_ads_noads();
2317 int net_ads_changetrustpw(int argc, const char **argv)
2319 return net_ads_noads();
2322 int net_ads_join(int argc, const char **argv)
2324 return net_ads_noads();
2327 int net_ads_user(int argc, const char **argv)
2329 return net_ads_noads();
2332 int net_ads_group(int argc, const char **argv)
2334 return net_ads_noads();
2337 /* this one shouldn't display a message */
2338 int net_ads_check(void)
2340 return -1;
2343 int net_ads_check_our_domain(void)
2345 return -1;
2348 int net_ads(int argc, const char **argv)
2350 return net_ads_usage(argc, argv);
2353 #endif /* WITH_ADS */