r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[Samba/bb.git] / source / utils / net_ads.c
blob70f9f621870156e6c0f7f85572b759d8a7dbb64e
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 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "utils/net.h"
26 #ifdef HAVE_ADS
28 int net_ads_usage(int argc, const char **argv)
30 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
31 d_printf(" Join the local machine to a ADS realm\n");
32 d_printf("leave\n");
33 d_printf(" Remove the local machine from a ADS realm\n");
34 d_printf("testjoin\n");
35 d_printf(" Validates the machine account in the domain\n");
36 d_printf("user\n");
37 d_printf(" List, add, or delete users in the realm\n");
38 d_printf("group\n");
39 d_printf(" List, add, or delete groups in the realm\n");
40 d_printf("info\n");
41 d_printf(" Displays details regarding a specific AD server\n");
42 d_printf("status\n");
43 d_printf(" Display details regarding the machine's account in AD\n");
44 d_printf("lookup\n");
45 d_printf(" Performs CLDAP query of AD domain controllers\n");
46 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
47 d_printf(" Change a user's password using an admin account\n");
48 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
49 d_printf("changetrustpw\n");
50 d_printf(" Change the trust account password of this machine in the AD tree\n");
51 d_printf("printer [info | publish | remove] <printername> <servername>\n");
52 d_printf(" Lookup, add, or remove directory entry for a printer\n");
53 d_printf("{search,dn,sid}\n");
54 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
55 d_printf("keytab\n");
56 d_printf(" Manage a local keytab file based on the machine account in AD\n");
57 d_printf("dns\n");
58 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
59 d_printf(" (using the machine credentials)\n");
61 return -1;
64 /* when we do not have sufficient input parameters to contact a remote domain
65 * we always fall back to our own realm - Guenther*/
67 static const char *assume_own_realm(void)
69 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
70 return lp_realm();
73 return NULL;
77 do a cldap netlogon query
79 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
81 struct cldap_netlogon_reply reply;
83 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
84 d_fprintf(stderr, "CLDAP query failed!\n");
85 return -1;
88 d_printf("Information for Domain Controller: %s\n\n",
89 inet_ntoa(ads->ldap_ip));
91 d_printf("Response Type: ");
92 switch (reply.type) {
93 case SAMLOGON_AD_UNK_R:
94 d_printf("SAMLOGON\n");
95 break;
96 case SAMLOGON_AD_R:
97 d_printf("SAMLOGON_USER\n");
98 break;
99 default:
100 d_printf("0x%x\n", reply.type);
101 break;
103 d_printf("GUID: %s\n",
104 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
105 d_printf("Flags:\n"
106 "\tIs a PDC: %s\n"
107 "\tIs a GC of the forest: %s\n"
108 "\tIs an LDAP server: %s\n"
109 "\tSupports DS: %s\n"
110 "\tIs running a KDC: %s\n"
111 "\tIs running time services: %s\n"
112 "\tIs the closest DC: %s\n"
113 "\tIs writable: %s\n"
114 "\tHas a hardware clock: %s\n"
115 "\tIs a non-domain NC serviced by LDAP server: %s\n",
116 (reply.flags & ADS_PDC) ? "yes" : "no",
117 (reply.flags & ADS_GC) ? "yes" : "no",
118 (reply.flags & ADS_LDAP) ? "yes" : "no",
119 (reply.flags & ADS_DS) ? "yes" : "no",
120 (reply.flags & ADS_KDC) ? "yes" : "no",
121 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
122 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
123 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
124 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
125 (reply.flags & ADS_NDNC) ? "yes" : "no");
127 printf("Forest:\t\t\t%s\n", reply.forest);
128 printf("Domain:\t\t\t%s\n", reply.domain);
129 printf("Domain Controller:\t%s\n", reply.hostname);
131 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
132 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
134 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
135 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
137 printf("Server Site Name :\t\t%s\n", reply.server_site_name);
138 printf("Client Site Name :\t\t%s\n", reply.client_site_name);
140 d_printf("NT Version: %d\n", reply.version);
141 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
142 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
144 return 0;
149 this implements the CLDAP based netlogon lookup requests
150 for finding the domain controller of a ADS domain
152 static int net_ads_lookup(int argc, const char **argv)
154 ADS_STRUCT *ads;
156 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
157 d_fprintf(stderr, "Didn't find the cldap server!\n");
158 return -1;
161 if (!ads->config.realm) {
162 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
163 ads->ldap_port = 389;
166 return net_ads_cldap_netlogon(ads);
171 static int net_ads_info(int argc, const char **argv)
173 ADS_STRUCT *ads;
175 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
176 d_fprintf(stderr, "Didn't find the ldap server!\n");
177 return -1;
180 if (!ads || !ads->config.realm) {
181 d_fprintf(stderr, "Didn't find the ldap server!\n");
182 return -1;
185 /* Try to set the server's current time since we didn't do a full
186 TCP LDAP session initially */
188 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
189 d_fprintf( stderr, "Failed to get server's current time!\n");
192 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
193 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
194 d_printf("Realm: %s\n", ads->config.realm);
195 d_printf("Bind Path: %s\n", ads->config.bind_path);
196 d_printf("LDAP port: %d\n", ads->ldap_port);
197 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
199 d_printf("KDC server: %s\n", ads->auth.kdc_server );
200 d_printf("Server time offset: %d\n", ads->auth.time_offset );
202 return 0;
205 static void use_in_memory_ccache(void) {
206 /* Use in-memory credentials cache so we do not interfere with
207 * existing credentials */
208 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
211 static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
213 ADS_STRUCT *ads = NULL;
214 ADS_STATUS status;
215 BOOL need_password = False;
216 BOOL second_time = False;
217 char *cp;
218 const char *realm = NULL;
219 BOOL tried_closest_dc = False;
221 /* lp_realm() should be handled by a command line param,
222 However, the join requires that realm be set in smb.conf
223 and compares our realm with the remote server's so this is
224 ok until someone needs more flexibility */
226 *ads_ret = NULL;
228 retry_connect:
229 if (only_own_domain) {
230 realm = lp_realm();
231 } else {
232 realm = assume_own_realm();
235 ads = ads_init(realm, opt_target_workgroup, opt_host);
237 if (!opt_user_name) {
238 opt_user_name = "administrator";
241 if (opt_user_specified) {
242 need_password = True;
245 retry:
246 if (!opt_password && need_password && !opt_machine_pass) {
247 char *prompt = NULL;
248 asprintf(&prompt,"%s's password: ", opt_user_name);
249 if (!prompt) {
250 ads_destroy(&ads);
251 return ADS_ERROR(LDAP_NO_MEMORY);
253 opt_password = getpass(prompt);
254 free(prompt);
257 if (opt_password) {
258 use_in_memory_ccache();
259 SAFE_FREE(ads->auth.password);
260 ads->auth.password = smb_xstrdup(opt_password);
263 ads->auth.flags |= auth_flags;
264 SAFE_FREE(ads->auth.user_name);
265 ads->auth.user_name = smb_xstrdup(opt_user_name);
268 * If the username is of the form "name@realm",
269 * extract the realm and convert to upper case.
270 * This is only used to establish the connection.
272 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
273 *cp++ = '\0';
274 SAFE_FREE(ads->auth.realm);
275 ads->auth.realm = smb_xstrdup(cp);
276 strupper_m(ads->auth.realm);
279 status = ads_connect(ads);
281 if (!ADS_ERR_OK(status)) {
283 if (NT_STATUS_EQUAL(ads_ntstatus(status),
284 NT_STATUS_NO_LOGON_SERVERS)) {
285 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
286 ads_destroy(&ads);
287 return status;
290 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
291 need_password = True;
292 second_time = True;
293 goto retry;
294 } else {
295 ads_destroy(&ads);
296 return status;
300 /* when contacting our own domain, make sure we use the closest DC.
301 * This is done by reconnecting to ADS because only the first call to
302 * ads_connect will give us our own sitename */
304 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
306 tried_closest_dc = True; /* avoid loop */
308 if (!ads->config.tried_closest_dc) {
310 namecache_delete(ads->server.realm, 0x1C);
311 namecache_delete(ads->server.workgroup, 0x1C);
313 ads_destroy(&ads);
314 ads = NULL;
316 goto retry_connect;
320 *ads_ret = ads;
321 return status;
324 ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
326 return ads_startup_int(only_own_domain, 0, ads);
329 ADS_STATUS ads_startup_nobind(BOOL only_own_domain, ADS_STRUCT **ads)
331 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
335 Check to see if connection can be made via ads.
336 ads_startup() stores the password in opt_password if it needs to so
337 that rpc or rap can use it without re-prompting.
339 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
341 ADS_STRUCT *ads;
342 ADS_STATUS status;
344 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
345 return -1;
348 ads->auth.flags |= ADS_AUTH_NO_BIND;
350 status = ads_connect(ads);
351 if ( !ADS_ERR_OK(status) ) {
352 return -1;
355 ads_destroy(&ads);
356 return 0;
359 int net_ads_check_our_domain(void)
361 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
364 int net_ads_check(void)
366 return net_ads_check_int(NULL, opt_workgroup, opt_host);
369 determine the netbios workgroup name for a domain
371 static int net_ads_workgroup(int argc, const char **argv)
373 ADS_STRUCT *ads;
374 struct cldap_netlogon_reply reply;
376 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
377 d_fprintf(stderr, "Didn't find the cldap server!\n");
378 return -1;
381 if (!ads->config.realm) {
382 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
383 ads->ldap_port = 389;
386 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
387 d_fprintf(stderr, "CLDAP query failed!\n");
388 return -1;
391 d_printf("Workgroup: %s\n", reply.netbios_domain);
393 ads_destroy(&ads);
395 return 0;
400 static BOOL usergrp_display(char *field, void **values, void *data_area)
402 char **disp_fields = (char **) data_area;
404 if (!field) { /* must be end of record */
405 if (disp_fields[0]) {
406 if (!strchr_m(disp_fields[0], '$')) {
407 if (disp_fields[1])
408 d_printf("%-21.21s %s\n",
409 disp_fields[0], disp_fields[1]);
410 else
411 d_printf("%s\n", disp_fields[0]);
414 SAFE_FREE(disp_fields[0]);
415 SAFE_FREE(disp_fields[1]);
416 return True;
418 if (!values) /* must be new field, indicate string field */
419 return True;
420 if (StrCaseCmp(field, "sAMAccountName") == 0) {
421 disp_fields[0] = SMB_STRDUP((char *) values[0]);
423 if (StrCaseCmp(field, "description") == 0)
424 disp_fields[1] = SMB_STRDUP((char *) values[0]);
425 return True;
428 static int net_ads_user_usage(int argc, const char **argv)
430 return net_help_user(argc, argv);
433 static int ads_user_add(int argc, const char **argv)
435 ADS_STRUCT *ads;
436 ADS_STATUS status;
437 char *upn, *userdn;
438 LDAPMessage *res=NULL;
439 int rc = -1;
440 char *ou_str = NULL;
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) {
461 ou_str = SMB_STRDUP(opt_container);
462 } else {
463 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
466 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
468 if (!ADS_ERR_OK(status)) {
469 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
470 ads_errstr(status));
471 goto done;
474 /* if no password is to be set, we're done */
475 if (argc == 1) {
476 d_printf("User %s added\n", argv[0]);
477 rc = 0;
478 goto done;
481 /* try setting the password */
482 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
483 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
484 ads->auth.time_offset);
485 safe_free(upn);
486 if (ADS_ERR_OK(status)) {
487 d_printf("User %s added\n", argv[0]);
488 rc = 0;
489 goto done;
492 /* password didn't set, delete account */
493 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
494 argv[0], ads_errstr(status));
495 ads_msgfree(ads, res);
496 status=ads_find_user_acct(ads, &res, argv[0]);
497 if (ADS_ERR_OK(status)) {
498 userdn = ads_get_dn(ads, res);
499 ads_del_dn(ads, userdn);
500 ads_memfree(ads, userdn);
503 done:
504 if (res)
505 ads_msgfree(ads, res);
506 ads_destroy(&ads);
507 SAFE_FREE(ou_str);
508 return rc;
511 static int ads_user_info(int argc, const char **argv)
513 ADS_STRUCT *ads;
514 ADS_STATUS rc;
515 LDAPMessage *res;
516 const char *attrs[] = {"memberOf", NULL};
517 char *searchstring=NULL;
518 char **grouplist;
519 char *escaped_user;
521 if (argc < 1) {
522 return net_ads_user_usage(argc, argv);
525 escaped_user = escape_ldap_string_alloc(argv[0]);
527 if (!escaped_user) {
528 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
529 return -1;
532 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
533 SAFE_FREE(escaped_user);
534 return -1;
537 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
538 rc = ads_search(ads, &res, searchstring, attrs);
539 safe_free(searchstring);
541 if (!ADS_ERR_OK(rc)) {
542 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
543 ads_destroy(&ads);
544 SAFE_FREE(escaped_user);
545 return -1;
548 grouplist = ldap_get_values((LDAP *)ads->ld,
549 (LDAPMessage *)res, "memberOf");
551 if (grouplist) {
552 int i;
553 char **groupname;
554 for (i=0;grouplist[i];i++) {
555 groupname = ldap_explode_dn(grouplist[i], 1);
556 d_printf("%s\n", groupname[0]);
557 ldap_value_free(groupname);
559 ldap_value_free(grouplist);
562 ads_msgfree(ads, res);
563 ads_destroy(&ads);
564 SAFE_FREE(escaped_user);
565 return 0;
568 static int ads_user_delete(int argc, const char **argv)
570 ADS_STRUCT *ads;
571 ADS_STATUS rc;
572 LDAPMessage *res = NULL;
573 char *userdn;
575 if (argc < 1) {
576 return net_ads_user_usage(argc, argv);
579 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
580 return -1;
583 rc = ads_find_user_acct(ads, &res, argv[0]);
584 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
585 d_printf("User %s does not exist.\n", argv[0]);
586 ads_msgfree(ads, res);
587 ads_destroy(&ads);
588 return -1;
590 userdn = ads_get_dn(ads, res);
591 ads_msgfree(ads, res);
592 rc = ads_del_dn(ads, userdn);
593 ads_memfree(ads, userdn);
594 if (ADS_ERR_OK(rc)) {
595 d_printf("User %s deleted\n", argv[0]);
596 ads_destroy(&ads);
597 return 0;
599 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
600 ads_errstr(rc));
601 ads_destroy(&ads);
602 return -1;
605 int net_ads_user(int argc, const char **argv)
607 struct functable func[] = {
608 {"ADD", ads_user_add},
609 {"INFO", ads_user_info},
610 {"DELETE", ads_user_delete},
611 {NULL, NULL}
613 ADS_STRUCT *ads;
614 ADS_STATUS rc;
615 const char *shortattrs[] = {"sAMAccountName", NULL};
616 const char *longattrs[] = {"sAMAccountName", "description", NULL};
617 char *disp_fields[2] = {NULL, NULL};
619 if (argc == 0) {
620 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
621 return -1;
624 if (opt_long_list_entries)
625 d_printf("\nUser name Comment"\
626 "\n-----------------------------\n");
628 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
629 LDAP_SCOPE_SUBTREE,
630 "(objectCategory=user)",
631 opt_long_list_entries ? longattrs :
632 shortattrs, usergrp_display,
633 disp_fields);
634 ads_destroy(&ads);
635 return ADS_ERR_OK(rc) ? 0 : -1;
638 return net_run_function(argc, argv, func, net_ads_user_usage);
641 static int net_ads_group_usage(int argc, const char **argv)
643 return net_help_group(argc, argv);
646 static int ads_group_add(int argc, const char **argv)
648 ADS_STRUCT *ads;
649 ADS_STATUS status;
650 LDAPMessage *res=NULL;
651 int rc = -1;
652 char *ou_str = NULL;
654 if (argc < 1) {
655 return net_ads_group_usage(argc, argv);
658 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
659 return -1;
662 status = ads_find_user_acct(ads, &res, argv[0]);
664 if (!ADS_ERR_OK(status)) {
665 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
666 goto done;
669 if (ads_count_replies(ads, res)) {
670 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
671 goto done;
674 if (opt_container) {
675 ou_str = SMB_STRDUP(opt_container);
676 } else {
677 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
680 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
682 if (ADS_ERR_OK(status)) {
683 d_printf("Group %s added\n", argv[0]);
684 rc = 0;
685 } else {
686 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
687 ads_errstr(status));
690 done:
691 if (res)
692 ads_msgfree(ads, res);
693 ads_destroy(&ads);
694 SAFE_FREE(ou_str);
695 return rc;
698 static int ads_group_delete(int argc, const char **argv)
700 ADS_STRUCT *ads;
701 ADS_STATUS rc;
702 LDAPMessage *res = NULL;
703 char *groupdn;
705 if (argc < 1) {
706 return net_ads_group_usage(argc, argv);
709 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
710 return -1;
713 rc = ads_find_user_acct(ads, &res, argv[0]);
714 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
715 d_printf("Group %s does not exist.\n", argv[0]);
716 ads_msgfree(ads, res);
717 ads_destroy(&ads);
718 return -1;
720 groupdn = ads_get_dn(ads, res);
721 ads_msgfree(ads, res);
722 rc = ads_del_dn(ads, groupdn);
723 ads_memfree(ads, groupdn);
724 if (ADS_ERR_OK(rc)) {
725 d_printf("Group %s deleted\n", argv[0]);
726 ads_destroy(&ads);
727 return 0;
729 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
730 ads_errstr(rc));
731 ads_destroy(&ads);
732 return -1;
735 int net_ads_group(int argc, const char **argv)
737 struct functable func[] = {
738 {"ADD", ads_group_add},
739 {"DELETE", ads_group_delete},
740 {NULL, NULL}
742 ADS_STRUCT *ads;
743 ADS_STATUS rc;
744 const char *shortattrs[] = {"sAMAccountName", NULL};
745 const char *longattrs[] = {"sAMAccountName", "description", NULL};
746 char *disp_fields[2] = {NULL, NULL};
748 if (argc == 0) {
749 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
750 return -1;
753 if (opt_long_list_entries)
754 d_printf("\nGroup name Comment"\
755 "\n-----------------------------\n");
756 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
757 LDAP_SCOPE_SUBTREE,
758 "(objectCategory=group)",
759 opt_long_list_entries ? longattrs :
760 shortattrs, usergrp_display,
761 disp_fields);
763 ads_destroy(&ads);
764 return ADS_ERR_OK(rc) ? 0 : -1;
766 return net_run_function(argc, argv, func, net_ads_group_usage);
769 static int net_ads_status(int argc, const char **argv)
771 ADS_STRUCT *ads;
772 ADS_STATUS rc;
773 LDAPMessage *res;
775 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
776 return -1;
779 rc = ads_find_machine_acct(ads, &res, global_myname());
780 if (!ADS_ERR_OK(rc)) {
781 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
782 ads_destroy(&ads);
783 return -1;
786 if (ads_count_replies(ads, res) == 0) {
787 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
788 ads_destroy(&ads);
789 return -1;
792 ads_dump(ads, res);
793 ads_destroy(&ads);
794 return 0;
797 /*******************************************************************
798 Leave an AD domain. Windows XP disables the machine account.
799 We'll try the same. The old code would do an LDAP delete.
800 That only worked using the machine creds because added the machine
801 with full control to the computer object's ACL.
802 *******************************************************************/
804 static int net_ads_leave(int argc, const char **argv)
806 ADS_STRUCT *ads = NULL;
807 ADS_STATUS adsret;
808 NTSTATUS status;
809 int ret = -1;
810 struct cli_state *cli = NULL;
811 TALLOC_CTX *ctx;
812 DOM_SID *dom_sid = NULL;
813 char *short_domain_name = NULL;
815 if (!secrets_init()) {
816 DEBUG(1,("Failed to initialise secrets database\n"));
817 return -1;
820 if (!(ctx = talloc_init("net_ads_leave"))) {
821 d_fprintf(stderr, "Could not initialise talloc context.\n");
822 return -1;
825 /* The finds a DC and takes care of getting the
826 user creds if necessary */
828 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
829 return -1;
832 /* make RPC calls here */
834 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
835 ads->config.ldap_server_name)) )
837 goto done;
840 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
841 goto done;
844 saf_delete( short_domain_name );
846 status = netdom_leave_domain(ctx, cli, dom_sid);
848 /* Try and delete it via LDAP - the old way we used to. */
850 adsret = ads_leave_realm(ads, global_myname());
851 if (ADS_ERR_OK(adsret)) {
852 d_printf("Deleted account for '%s' in realm '%s'\n",
853 global_myname(), ads->config.realm);
854 ret = 0;
855 } else {
856 /* We couldn't delete it - see if the disable succeeded. */
857 if (NT_STATUS_IS_OK(status)) {
858 d_printf("Disabled account for '%s' in realm '%s'\n",
859 global_myname(), ads->config.realm);
860 ret = 0;
861 } else {
862 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
863 global_myname(), ads->config.realm);
867 done:
869 if ( cli )
870 cli_shutdown(cli);
872 ads_destroy(&ads);
873 TALLOC_FREE( ctx );
875 return ret;
878 static NTSTATUS net_ads_join_ok(void)
880 ADS_STRUCT *ads = NULL;
881 ADS_STATUS status;
883 if (!secrets_init()) {
884 DEBUG(1,("Failed to initialise secrets database\n"));
885 return NT_STATUS_ACCESS_DENIED;
888 net_use_machine_password();
890 status = ads_startup(True, &ads);
891 if (!ADS_ERR_OK(status)) {
892 return ads_ntstatus(status);
895 ads_destroy(&ads);
896 return NT_STATUS_OK;
900 check that an existing join is OK
902 int net_ads_testjoin(int argc, const char **argv)
904 NTSTATUS status;
905 use_in_memory_ccache();
907 /* Display success or failure */
908 status = net_ads_join_ok();
909 if (!NT_STATUS_IS_OK(status)) {
910 fprintf(stderr,"Join to domain is not valid: %s\n",
911 get_friendly_nt_error_msg(status));
912 return -1;
915 printf("Join is OK\n");
916 return 0;
919 /*******************************************************************
920 Simple configu checks before beginning the join
921 ********************************************************************/
923 static NTSTATUS check_ads_config( void )
925 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
926 d_printf("Host is not configured as a member server.\n");
927 return NT_STATUS_INVALID_DOMAIN_ROLE;
930 if (strlen(global_myname()) > 15) {
931 d_printf("Our netbios name can be at most 15 chars long, "
932 "\"%s\" is %u chars long\n", global_myname(),
933 (unsigned int)strlen(global_myname()));
934 return NT_STATUS_NAME_TOO_LONG;
937 if ( lp_security() == SEC_ADS && !*lp_realm()) {
938 d_fprintf(stderr, "realm must be set in in %s for ADS "
939 "join to succeed.\n", dyn_CONFIGFILE);
940 return NT_STATUS_INVALID_PARAMETER;
943 if (!secrets_init()) {
944 DEBUG(1,("Failed to initialise secrets database\n"));
945 /* This is a good bet for failure of secrets_init ... */
946 return NT_STATUS_ACCESS_DENIED;
949 return NT_STATUS_OK;
952 /*******************************************************************
953 Do the domain join
954 ********************************************************************/
956 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
957 struct in_addr *ip, char **domain,
958 DOM_SID **dom_sid,
959 const char *password)
961 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
962 struct cli_state *cli = NULL;
964 ret = connect_to_ipc_krb5(&cli, ip, servername);
965 if ( !NT_STATUS_IS_OK(ret) ) {
966 goto done;
969 ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
970 if ( !NT_STATUS_IS_OK(ret) ) {
971 goto done;
974 /* cli->server_domain is not filled in when using krb5
975 session setups */
977 saf_store( *domain, cli->desthost );
979 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
981 done:
982 if ( cli )
983 cli_shutdown(cli);
985 return ret;
988 /*******************************************************************
989 Set a machines dNSHostName and servicePrincipalName attributes
990 ********************************************************************/
992 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
994 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
995 char *new_dn;
996 ADS_MODLIST mods;
997 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
998 char *psp;
999 fstring my_fqdn;
1000 LDAPMessage *res = NULL;
1001 char *dn_string = NULL;
1002 const char *machine_name = global_myname();
1003 int count;
1005 if ( !machine_name ) {
1006 return ADS_ERROR(LDAP_NO_MEMORY);
1009 /* Find our DN */
1011 status = ads_find_machine_acct(ads_s, &res, machine_name);
1012 if (!ADS_ERR_OK(status))
1013 return status;
1015 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1016 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1017 return ADS_ERROR(LDAP_NO_MEMORY);
1020 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1021 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1022 goto done;
1025 new_dn = talloc_strdup(ctx, dn_string);
1026 ads_memfree(ads_s, dn_string);
1027 if (!new_dn) {
1028 return ADS_ERROR(LDAP_NO_MEMORY);
1031 /* Windows only creates HOST/shortname & HOST/fqdn. */
1033 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1034 goto done;
1035 strupper_m(psp);
1036 servicePrincipalName[0] = psp;
1038 name_to_fqdn(my_fqdn, machine_name);
1039 strlower_m(my_fqdn);
1040 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1041 goto done;
1042 servicePrincipalName[1] = psp;
1044 if (!(mods = ads_init_mods(ctx))) {
1045 goto done;
1048 /* fields of primary importance */
1050 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1051 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1053 status = ads_gen_mod(ads_s, new_dn, mods);
1055 done:
1056 ads_msgfree(ads_s, res);
1058 return status;
1061 /*******************************************************************
1062 Set a machines dNSHostName and servicePrincipalName attributes
1063 ********************************************************************/
1065 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1067 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1068 char *new_dn;
1069 ADS_MODLIST mods;
1070 LDAPMessage *res = NULL;
1071 char *dn_string = NULL;
1072 const char *machine_name = global_myname();
1073 int count;
1075 if ( !machine_name ) {
1076 return ADS_ERROR(LDAP_NO_MEMORY);
1079 /* Find our DN */
1081 status = ads_find_machine_acct(ads_s, &res, machine_name);
1082 if (!ADS_ERR_OK(status))
1083 return status;
1085 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1086 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1087 return ADS_ERROR(LDAP_NO_MEMORY);
1090 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1091 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1092 goto done;
1095 new_dn = talloc_strdup(ctx, dn_string);
1096 ads_memfree(ads_s, dn_string);
1097 if (!new_dn) {
1098 return ADS_ERROR(LDAP_NO_MEMORY);
1101 /* now do the mods */
1103 if (!(mods = ads_init_mods(ctx))) {
1104 goto done;
1107 /* fields of primary importance */
1109 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1111 status = ads_gen_mod(ads_s, new_dn, mods);
1113 done:
1114 ads_msgfree(ads_s, res);
1116 return status;
1119 /*******************************************************************
1120 Set a machines dNSHostName and servicePrincipalName attributes
1121 ********************************************************************/
1123 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1124 const char *os_name, const char *os_version )
1126 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1127 char *new_dn;
1128 ADS_MODLIST mods;
1129 LDAPMessage *res = NULL;
1130 char *dn_string = NULL;
1131 const char *machine_name = global_myname();
1132 int count;
1133 char *os_sp = NULL;
1135 if ( !os_name || !os_version ) {
1136 return ADS_ERROR(LDAP_NO_MEMORY);
1139 /* Find our DN */
1141 status = ads_find_machine_acct(ads_s, &res, machine_name);
1142 if (!ADS_ERR_OK(status))
1143 return status;
1145 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1146 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1147 return ADS_ERROR(LDAP_NO_MEMORY);
1150 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1151 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1152 goto done;
1155 new_dn = talloc_strdup(ctx, dn_string);
1156 ads_memfree(ads_s, dn_string);
1157 if (!new_dn) {
1158 return ADS_ERROR(LDAP_NO_MEMORY);
1161 /* now do the mods */
1163 if (!(mods = ads_init_mods(ctx))) {
1164 goto done;
1167 os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1169 /* fields of primary importance */
1171 ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1172 ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1173 if ( os_sp )
1174 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1176 status = ads_gen_mod(ads_s, new_dn, mods);
1178 done:
1179 ads_msgfree(ads_s, res);
1180 TALLOC_FREE( os_sp );
1182 return status;
1185 /*******************************************************************
1186 join a domain using ADS (LDAP mods)
1187 ********************************************************************/
1189 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1191 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1192 char *ou_str = NULL;
1193 char *dn = NULL;
1194 LDAPMessage *res = NULL;
1195 BOOL moved;
1197 ou_str = ads_ou_string(ads, ou);
1198 if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1199 rc = ADS_ERROR(LDAP_NO_MEMORY);
1200 goto done;
1203 rc = ads_search_dn(ads, &res, dn, NULL);
1204 if (!ADS_ERR_OK(rc)) {
1205 d_fprintf(stderr, "The specified OU does not exist.\n");
1206 goto done;
1209 /* Attempt to create the machine account and bail if this fails.
1210 Assume that the admin wants exactly what they requested */
1212 rc = ads_create_machine_acct( ads, global_myname(), dn );
1213 if (ADS_ERR_OK(rc)) {
1214 DEBUG(1, ("machine account created\n"));
1215 goto done;
1217 if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1218 DEBUG(1, ("machine account creation failed\n"));
1219 goto done;
1222 rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1223 if (!ADS_ERR_OK(rc)) {
1224 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1225 goto done;
1228 if (moved) {
1229 d_printf("The machine account was moved into the specified OU.\n");
1230 } else {
1231 d_printf("The machine account already exists in the specified OU.\n");
1234 done:
1235 ads_msgfree(ads, res);
1236 SAFE_FREE( ou_str );
1237 SAFE_FREE( dn );
1239 return rc;
1242 /************************************************************************
1243 ************************************************************************/
1245 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1247 uint32 domain_func;
1248 ADS_STATUS status;
1249 fstring salt;
1250 char *std_salt;
1251 LDAPMessage *res = NULL;
1252 const char *machine_name = global_myname();
1254 status = ads_domain_func_level( ads, &domain_func );
1255 if ( !ADS_ERR_OK(status) ) {
1256 DEBUG(2,("Failed to determine domain functional level!\n"));
1257 return False;
1260 /* go ahead and setup the default salt */
1262 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1263 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1264 return False;
1267 fstrcpy( salt, std_salt );
1268 SAFE_FREE( std_salt );
1270 /* if it's a Windows functional domain, we have to look for the UPN */
1272 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1273 char *upn;
1274 int count;
1276 status = ads_find_machine_acct(ads, &res, machine_name);
1277 if (!ADS_ERR_OK(status)) {
1278 return False;
1281 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1282 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1283 return False;
1286 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1287 if ( upn ) {
1288 fstrcpy( salt, upn );
1291 ads_msgfree(ads, res);
1294 return kerberos_secrets_store_des_salt( salt );
1297 /*******************************************************************
1298 Send a DNS update request
1299 *******************************************************************/
1301 #if defined(WITH_DNS_UPDATES)
1302 #include "dns.h"
1303 DNS_ERROR DoDNSUpdate(char *pszServerName,
1304 const char *pszDomainName,
1305 const char *pszHostName,
1306 const struct in_addr *iplist, int num_addrs );
1309 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1310 const char *machine_name,
1311 const struct in_addr *addrs,
1312 int num_addrs)
1314 struct dns_rr_ns *nameservers = NULL;
1315 int ns_count = 0;
1316 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1317 DNS_ERROR dns_err;
1318 fstring dns_server;
1319 const char *dnsdomain = NULL;
1320 char *root_domain = NULL;
1322 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1323 d_printf("No DNS domain configured for %s. "
1324 "Unable to perform DNS Update.\n", machine_name);
1325 status = NT_STATUS_INVALID_PARAMETER;
1326 goto done;
1328 dnsdomain++;
1330 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1331 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1332 /* Child domains often do not have NS records. Look
1333 for the NS record for the forest root domain
1334 (rootDomainNamingContext in therootDSE) */
1336 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1337 LDAPMessage *msg = NULL;
1338 char *root_dn;
1339 ADS_STATUS ads_status;
1341 if ( !ads->ld ) {
1342 ads_status = ads_connect( ads );
1343 if ( !ADS_ERR_OK(ads_status) ) {
1344 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1345 goto done;
1349 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1350 "(objectclass=*)", rootname_attrs, &msg);
1351 if (!ADS_ERR_OK(ads_status)) {
1352 goto done;
1355 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1356 if ( !root_dn ) {
1357 ads_msgfree( ads, msg );
1358 goto done;
1361 root_domain = ads_build_domain( root_dn );
1363 /* cleanup */
1364 ads_msgfree( ads, msg );
1366 /* try again for NS servers */
1368 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1370 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1371 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1372 "realm\n", ads->config.realm));
1373 goto done;
1376 dnsdomain = root_domain;
1380 /* Now perform the dns update - we'll try non-secure and if we fail,
1381 we'll follow it up with a secure update */
1383 fstrcpy( dns_server, nameservers[0].hostname );
1385 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1386 if (!ERR_DNS_IS_OK(dns_err)) {
1387 status = NT_STATUS_UNSUCCESSFUL;
1390 done:
1392 SAFE_FREE( root_domain );
1394 return status;
1397 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1399 int num_addrs;
1400 struct in_addr *iplist = NULL;
1401 fstring machine_name;
1402 NTSTATUS status;
1404 name_to_fqdn( machine_name, global_myname() );
1405 strlower_m( machine_name );
1407 /* Get our ip address (not the 127.0.0.x address but a real ip
1408 * address) */
1410 num_addrs = get_my_ip_address( &iplist );
1411 if ( num_addrs <= 0 ) {
1412 DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
1413 "addresses!\n"));
1414 return NT_STATUS_INVALID_PARAMETER;
1417 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1418 iplist, num_addrs);
1419 SAFE_FREE( iplist );
1420 return status;
1422 #endif
1425 /*******************************************************************
1426 utility function to parse an integer parameter from
1427 "parameter = value"
1428 **********************************************************/
1429 static char* get_string_param( const char* param )
1431 char *p;
1433 if ( (p = strchr( param, '=' )) == NULL )
1434 return NULL;
1436 return (p+1);
1439 /*******************************************************************
1440 ********************************************************************/
1442 static int net_ads_join_usage(int argc, const char **argv)
1444 d_printf("net ads join [options]\n");
1445 d_printf("Valid options:\n");
1446 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1447 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1448 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1449 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1450 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1451 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1452 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1453 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1454 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1455 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1456 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1457 d_printf(" the two other attributes.\n");
1459 return -1;
1462 /*******************************************************************
1463 ********************************************************************/
1465 int net_ads_join(int argc, const char **argv)
1467 ADS_STRUCT *ads = NULL;
1468 ADS_STATUS status;
1469 NTSTATUS nt_status;
1470 char *machine_account = NULL;
1471 char *short_domain_name = NULL;
1472 char *tmp_password, *password;
1473 TALLOC_CTX *ctx = NULL;
1474 DOM_SID *domain_sid = NULL;
1475 BOOL createupn = False;
1476 const char *machineupn = NULL;
1477 const char *create_in_ou = NULL;
1478 int i;
1479 fstring dc_name;
1480 struct in_addr dcip;
1481 const char *os_name = NULL;
1482 const char *os_version = NULL;
1484 nt_status = check_ads_config();
1485 if (!NT_STATUS_IS_OK(nt_status)) {
1486 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1487 goto fail;
1490 /* find a DC to initialize the server affinity cache */
1492 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
1494 status = ads_startup(True, &ads);
1495 if (!ADS_ERR_OK(status)) {
1496 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1497 nt_status = ads_ntstatus(status);
1498 goto fail;
1501 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1502 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1503 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1504 dyn_CONFIGFILE, lp_realm());
1505 nt_status = NT_STATUS_INVALID_PARAMETER;
1506 goto fail;
1509 if (!(ctx = talloc_init("net_ads_join"))) {
1510 d_fprintf(stderr, "Could not initialise talloc context.\n");
1511 nt_status = NT_STATUS_NO_MEMORY;
1512 goto fail;
1515 /* process additional command line args */
1517 for ( i=0; i<argc; i++ ) {
1518 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1519 createupn = True;
1520 machineupn = get_string_param(argv[i]);
1522 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1523 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1524 d_fprintf(stderr, "Please supply a valid OU path.\n");
1525 nt_status = NT_STATUS_INVALID_PARAMETER;
1526 goto fail;
1529 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1530 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1531 d_fprintf(stderr, "Please supply a operating system name.\n");
1532 nt_status = NT_STATUS_INVALID_PARAMETER;
1533 goto fail;
1536 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1537 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1538 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1539 nt_status = NT_STATUS_INVALID_PARAMETER;
1540 goto fail;
1543 else {
1544 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1545 nt_status = NT_STATUS_INVALID_PARAMETER;
1546 goto fail;
1550 /* If we were given an OU, try to create the machine in
1551 the OU account first and then do the normal RPC join */
1553 if ( create_in_ou ) {
1554 status = net_precreate_machine_acct( ads, create_in_ou );
1555 if ( !ADS_ERR_OK(status) ) {
1556 d_fprintf( stderr, "Failed to pre-create the machine object "
1557 "in OU %s.\n", create_in_ou);
1558 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1559 ads_errstr(status)));
1560 nt_status = ads_ntstatus(status);
1561 goto fail;
1565 /* Do the domain join here */
1567 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1568 password = talloc_strdup(ctx, tmp_password);
1570 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1571 &ads->ldap_ip, &short_domain_name, &domain_sid, password);
1572 if ( !NT_STATUS_IS_OK(nt_status) ) {
1573 DEBUG(1, ("call of net_join_domain failed: %s\n",
1574 get_friendly_nt_error_msg(nt_status)));
1575 goto fail;
1578 /* Check the short name of the domain */
1580 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1581 d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
1582 d_printf("domain name obtained from the server.\n");
1583 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1584 d_printf("You should set \"workgroup = %s\" in %s.\n",
1585 short_domain_name, dyn_CONFIGFILE);
1588 d_printf("Using short domain name -- %s\n", short_domain_name);
1590 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1591 value from smb.conf and the string returned from the server. The former is
1592 neede to bootstrap winbindd's first connection to the DC to get the real
1593 short domain name --jerry */
1595 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1596 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1598 /* issue an internal error here for now.
1599 * everything else would mean changing tdb routines. */
1600 nt_status = NT_STATUS_INTERNAL_ERROR;
1601 goto fail;
1604 /* Verify that everything is ok */
1606 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1607 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1608 goto fail;
1611 /* create the dNSHostName & servicePrincipalName values */
1613 status = net_set_machine_spn( ctx, ads );
1614 if ( !ADS_ERR_OK(status) ) {
1616 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1617 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1618 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1620 /* Disable the machine account in AD. Better to fail than to leave
1621 a confused admin. */
1623 if ( net_ads_leave( 0, NULL ) != 0 ) {
1624 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1627 /* clear out the machine password */
1629 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1630 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1632 nt_status = ads_ntstatus(status);
1633 goto fail;
1636 if ( !net_derive_salting_principal( ctx, ads ) ) {
1637 DEBUG(1,("Failed to determine salting principal\n"));
1638 goto fail;
1641 if ( createupn ) {
1642 pstring upn;
1644 /* default to using the short UPN name */
1645 if ( !machineupn ) {
1646 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1647 ads->config.realm );
1648 machineupn = upn;
1651 status = net_set_machine_upn( ctx, ads, machineupn );
1652 if ( !ADS_ERR_OK(status) ) {
1653 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1657 /* Try to set the operatingSystem attributes if asked */
1659 if ( os_name && os_version ) {
1660 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1661 if ( !ADS_ERR_OK(status) ) {
1662 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1663 "Are you a Domain Admin?\n");
1667 /* Now build the keytab, using the same ADS connection */
1669 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1670 DEBUG(1,("Error creating host keytab!\n"));
1673 #if defined(WITH_DNS_UPDATES)
1674 /* We enter this block with user creds */
1675 ads_kdestroy( NULL );
1676 ads_destroy(&ads);
1677 ads = NULL;
1679 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1680 /* kinit with the machine password */
1682 use_in_memory_ccache();
1683 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1684 ads->auth.password = secrets_fetch_machine_password(
1685 lp_workgroup(), NULL, NULL );
1686 ads->auth.realm = SMB_STRDUP( lp_realm() );
1687 ads_kinit_password( ads );
1690 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1691 d_fprintf( stderr, "DNS update failed!\n" );
1694 /* exit from this block using machine creds */
1695 #endif
1697 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1699 SAFE_FREE(machine_account);
1700 TALLOC_FREE( ctx );
1701 ads_destroy(&ads);
1703 return 0;
1705 fail:
1706 /* issue an overall failure message at the end. */
1707 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1709 SAFE_FREE(machine_account);
1710 TALLOC_FREE( ctx );
1711 ads_destroy(&ads);
1713 return -1;
1717 /*******************************************************************
1718 ********************************************************************/
1720 static int net_ads_dns_usage(int argc, const char **argv)
1722 #if defined(WITH_DNS_UPDATES)
1723 d_printf("net ads dns <command>\n");
1724 d_printf("Valid commands:\n");
1725 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1727 return 0;
1728 #else
1729 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1730 return -1;
1731 #endif
1734 /*******************************************************************
1735 ********************************************************************/
1737 static int net_ads_dns_register(int argc, const char **argv)
1739 #if defined(WITH_DNS_UPDATES)
1740 ADS_STRUCT *ads;
1741 ADS_STATUS status;
1742 TALLOC_CTX *ctx;
1744 #ifdef DEVELOPER
1745 talloc_enable_leak_report();
1746 #endif
1748 if (argc > 0) {
1749 d_fprintf(stderr, "net ads dns register <name> <ip>\n");
1750 return -1;
1753 if (!(ctx = talloc_init("net_ads_dns"))) {
1754 d_fprintf(stderr, "Could not initialise talloc context\n");
1755 return -1;
1758 status = ads_startup(True, &ads);
1759 if ( !ADS_ERR_OK(status) ) {
1760 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1761 TALLOC_FREE(ctx);
1762 return -1;
1765 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1766 d_fprintf( stderr, "DNS update failed!\n" );
1767 ads_destroy( &ads );
1768 TALLOC_FREE( ctx );
1769 return -1;
1772 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1774 ads_destroy(&ads);
1775 TALLOC_FREE( ctx );
1777 return 0;
1778 #else
1779 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1780 return -1;
1781 #endif
1784 #if defined(WITH_DNS_UPDATES)
1785 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1786 #endif
1788 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1790 #if defined(WITH_DNS_UPDATES)
1791 DNS_ERROR err;
1793 #ifdef DEVELOPER
1794 talloc_enable_leak_report();
1795 #endif
1797 if (argc != 2) {
1798 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1799 "<name>\n");
1800 return -1;
1803 err = do_gethostbyname(argv[0], argv[1]);
1805 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1806 #endif
1807 return 0;
1810 static int net_ads_dns(int argc, const char *argv[])
1812 struct functable func[] = {
1813 {"REGISTER", net_ads_dns_register},
1814 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1815 {NULL, NULL}
1818 return net_run_function(argc, argv, func, net_ads_dns_usage);
1821 /*******************************************************************
1822 ********************************************************************/
1824 int net_ads_printer_usage(int argc, const char **argv)
1826 d_printf(
1827 "\nnet ads printer search <printer>"
1828 "\n\tsearch for a printer in the directory\n"
1829 "\nnet ads printer info <printer> <server>"
1830 "\n\tlookup info in directory for printer on server"
1831 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1832 "\nnet ads printer publish <printername>"
1833 "\n\tpublish printer in directory"
1834 "\n\t(note: printer name is required)\n"
1835 "\nnet ads printer remove <printername>"
1836 "\n\tremove printer from directory"
1837 "\n\t(note: printer name is required)\n");
1838 return -1;
1841 /*******************************************************************
1842 ********************************************************************/
1844 static int net_ads_printer_search(int argc, const char **argv)
1846 ADS_STRUCT *ads;
1847 ADS_STATUS rc;
1848 LDAPMessage *res = NULL;
1850 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1851 return -1;
1854 rc = ads_find_printers(ads, &res);
1856 if (!ADS_ERR_OK(rc)) {
1857 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1858 ads_msgfree(ads, res);
1859 ads_destroy(&ads);
1860 return -1;
1863 if (ads_count_replies(ads, res) == 0) {
1864 d_fprintf(stderr, "No results found\n");
1865 ads_msgfree(ads, res);
1866 ads_destroy(&ads);
1867 return -1;
1870 ads_dump(ads, res);
1871 ads_msgfree(ads, res);
1872 ads_destroy(&ads);
1873 return 0;
1876 static int net_ads_printer_info(int argc, const char **argv)
1878 ADS_STRUCT *ads;
1879 ADS_STATUS rc;
1880 const char *servername, *printername;
1881 LDAPMessage *res = NULL;
1883 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1884 return -1;
1887 if (argc > 0) {
1888 printername = argv[0];
1889 } else {
1890 printername = "*";
1893 if (argc > 1) {
1894 servername = argv[1];
1895 } else {
1896 servername = global_myname();
1899 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1901 if (!ADS_ERR_OK(rc)) {
1902 d_fprintf(stderr, "Server '%s' not found: %s\n",
1903 servername, ads_errstr(rc));
1904 ads_msgfree(ads, res);
1905 ads_destroy(&ads);
1906 return -1;
1909 if (ads_count_replies(ads, res) == 0) {
1910 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1911 ads_msgfree(ads, res);
1912 ads_destroy(&ads);
1913 return -1;
1916 ads_dump(ads, res);
1917 ads_msgfree(ads, res);
1918 ads_destroy(&ads);
1920 return 0;
1923 static int net_ads_printer_publish(int argc, const char **argv)
1925 ADS_STRUCT *ads;
1926 ADS_STATUS rc;
1927 const char *servername, *printername;
1928 struct cli_state *cli;
1929 struct rpc_pipe_client *pipe_hnd;
1930 struct in_addr server_ip;
1931 NTSTATUS nt_status;
1932 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1933 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1934 char *prt_dn, *srv_dn, **srv_cn;
1935 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1936 LDAPMessage *res = NULL;
1938 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1939 talloc_destroy(mem_ctx);
1940 return -1;
1943 if (argc < 1) {
1944 talloc_destroy(mem_ctx);
1945 return net_ads_printer_usage(argc, argv);
1948 printername = argv[0];
1950 if (argc == 2) {
1951 servername = argv[1];
1952 } else {
1953 servername = global_myname();
1956 /* Get printer data from SPOOLSS */
1958 resolve_name(servername, &server_ip, 0x20);
1960 nt_status = cli_full_connection(&cli, global_myname(), servername,
1961 &server_ip, 0,
1962 "IPC$", "IPC",
1963 opt_user_name, opt_workgroup,
1964 opt_password ? opt_password : "",
1965 CLI_FULL_CONNECTION_USE_KERBEROS,
1966 Undefined, NULL);
1968 if (NT_STATUS_IS_ERR(nt_status)) {
1969 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1970 "for %s\n", servername, printername);
1971 ads_destroy(&ads);
1972 talloc_destroy(mem_ctx);
1973 return -1;
1976 /* Publish on AD server */
1978 ads_find_machine_acct(ads, &res, servername);
1980 if (ads_count_replies(ads, res) == 0) {
1981 d_fprintf(stderr, "Could not find machine account for server %s\n",
1982 servername);
1983 ads_destroy(&ads);
1984 talloc_destroy(mem_ctx);
1985 return -1;
1988 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1989 srv_cn = ldap_explode_dn(srv_dn, 1);
1991 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1992 printername_escaped = escape_rdn_val_string_alloc(printername);
1993 if (!srv_cn_escaped || !printername_escaped) {
1994 SAFE_FREE(srv_cn_escaped);
1995 SAFE_FREE(printername_escaped);
1996 d_fprintf(stderr, "Internal error, out of memory!");
1997 ads_destroy(&ads);
1998 talloc_destroy(mem_ctx);
1999 return -1;
2002 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
2004 SAFE_FREE(srv_cn_escaped);
2005 SAFE_FREE(printername_escaped);
2007 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
2008 if (!pipe_hnd) {
2009 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
2010 servername);
2011 SAFE_FREE(prt_dn);
2012 ads_destroy(&ads);
2013 talloc_destroy(mem_ctx);
2014 return -1;
2017 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
2018 printername))) {
2019 SAFE_FREE(prt_dn);
2020 ads_destroy(&ads);
2021 talloc_destroy(mem_ctx);
2022 return -1;
2025 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
2026 if (!ADS_ERR_OK(rc)) {
2027 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2028 SAFE_FREE(prt_dn);
2029 ads_destroy(&ads);
2030 talloc_destroy(mem_ctx);
2031 return -1;
2034 d_printf("published printer\n");
2035 SAFE_FREE(prt_dn);
2036 ads_destroy(&ads);
2037 talloc_destroy(mem_ctx);
2039 return 0;
2042 static int net_ads_printer_remove(int argc, const char **argv)
2044 ADS_STRUCT *ads;
2045 ADS_STATUS rc;
2046 const char *servername;
2047 char *prt_dn;
2048 LDAPMessage *res = NULL;
2050 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2051 return -1;
2054 if (argc < 1) {
2055 return net_ads_printer_usage(argc, argv);
2058 if (argc > 1) {
2059 servername = argv[1];
2060 } else {
2061 servername = global_myname();
2064 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2066 if (!ADS_ERR_OK(rc)) {
2067 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2068 ads_msgfree(ads, res);
2069 ads_destroy(&ads);
2070 return -1;
2073 if (ads_count_replies(ads, res) == 0) {
2074 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2075 ads_msgfree(ads, res);
2076 ads_destroy(&ads);
2077 return -1;
2080 prt_dn = ads_get_dn(ads, res);
2081 ads_msgfree(ads, res);
2082 rc = ads_del_dn(ads, prt_dn);
2083 ads_memfree(ads, prt_dn);
2085 if (!ADS_ERR_OK(rc)) {
2086 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2087 ads_destroy(&ads);
2088 return -1;
2091 ads_destroy(&ads);
2092 return 0;
2095 static int net_ads_printer(int argc, const char **argv)
2097 struct functable func[] = {
2098 {"SEARCH", net_ads_printer_search},
2099 {"INFO", net_ads_printer_info},
2100 {"PUBLISH", net_ads_printer_publish},
2101 {"REMOVE", net_ads_printer_remove},
2102 {NULL, NULL}
2105 return net_run_function(argc, argv, func, net_ads_printer_usage);
2109 static int net_ads_password(int argc, const char **argv)
2111 ADS_STRUCT *ads;
2112 const char *auth_principal = opt_user_name;
2113 const char *auth_password = opt_password;
2114 char *realm = NULL;
2115 char *new_password = NULL;
2116 char *c, *prompt;
2117 const char *user;
2118 ADS_STATUS ret;
2120 if (opt_user_name == NULL || opt_password == NULL) {
2121 d_fprintf(stderr, "You must supply an administrator username/password\n");
2122 return -1;
2125 if (argc < 1) {
2126 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2127 return -1;
2130 user = argv[0];
2131 if (!strchr_m(user, '@')) {
2132 asprintf(&c, "%s@%s", argv[0], lp_realm());
2133 user = c;
2136 use_in_memory_ccache();
2137 c = strchr_m(auth_principal, '@');
2138 if (c) {
2139 realm = ++c;
2140 } else {
2141 realm = lp_realm();
2144 /* use the realm so we can eventually change passwords for users
2145 in realms other than default */
2146 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2147 return -1;
2150 /* we don't actually need a full connect, but it's the easy way to
2151 fill in the KDC's addresss */
2152 ads_connect(ads);
2154 if (!ads || !ads->config.realm) {
2155 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2156 return -1;
2159 if (argv[1]) {
2160 new_password = (char *)argv[1];
2161 } else {
2162 asprintf(&prompt, "Enter new password for %s:", user);
2163 new_password = getpass(prompt);
2164 free(prompt);
2167 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2168 auth_password, user, new_password, ads->auth.time_offset);
2169 if (!ADS_ERR_OK(ret)) {
2170 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2171 ads_destroy(&ads);
2172 return -1;
2175 d_printf("Password change for %s completed.\n", user);
2176 ads_destroy(&ads);
2178 return 0;
2181 int net_ads_changetrustpw(int argc, const char **argv)
2183 ADS_STRUCT *ads;
2184 char *host_principal;
2185 fstring my_name;
2186 ADS_STATUS ret;
2188 if (!secrets_init()) {
2189 DEBUG(1,("Failed to initialise secrets database\n"));
2190 return -1;
2193 net_use_machine_password();
2195 use_in_memory_ccache();
2197 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2198 return -1;
2201 fstrcpy(my_name, global_myname());
2202 strlower_m(my_name);
2203 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2204 d_printf("Changing password for principal: %s\n", host_principal);
2206 ret = ads_change_trust_account_password(ads, host_principal);
2208 if (!ADS_ERR_OK(ret)) {
2209 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2210 ads_destroy(&ads);
2211 SAFE_FREE(host_principal);
2212 return -1;
2215 d_printf("Password change for principal %s succeeded.\n", host_principal);
2217 if (lp_use_kerberos_keytab()) {
2218 d_printf("Attempting to update system keytab with new password.\n");
2219 if (ads_keytab_create_default(ads)) {
2220 d_printf("Failed to update system keytab.\n");
2224 ads_destroy(&ads);
2225 SAFE_FREE(host_principal);
2227 return 0;
2231 help for net ads search
2233 static int net_ads_search_usage(int argc, const char **argv)
2235 d_printf(
2236 "\nnet ads search <expression> <attributes...>\n"\
2237 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2238 "The expression is a standard LDAP search expression, and the\n"\
2239 "attributes are a list of LDAP fields to show in the results\n\n"\
2240 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2242 net_common_flags_usage(argc, argv);
2243 return -1;
2248 general ADS search function. Useful in diagnosing problems in ADS
2250 static int net_ads_search(int argc, const char **argv)
2252 ADS_STRUCT *ads;
2253 ADS_STATUS rc;
2254 const char *ldap_exp;
2255 const char **attrs;
2256 LDAPMessage *res = NULL;
2258 if (argc < 1) {
2259 return net_ads_search_usage(argc, argv);
2262 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2263 return -1;
2266 ldap_exp = argv[0];
2267 attrs = (argv + 1);
2269 rc = ads_do_search_all(ads, ads->config.bind_path,
2270 LDAP_SCOPE_SUBTREE,
2271 ldap_exp, attrs, &res);
2272 if (!ADS_ERR_OK(rc)) {
2273 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2274 ads_destroy(&ads);
2275 return -1;
2278 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2280 /* dump the results */
2281 ads_dump(ads, res);
2283 ads_msgfree(ads, res);
2284 ads_destroy(&ads);
2286 return 0;
2291 help for net ads search
2293 static int net_ads_dn_usage(int argc, const char **argv)
2295 d_printf(
2296 "\nnet ads dn <dn> <attributes...>\n"\
2297 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2298 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2299 "to show in the results\n\n"\
2300 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2301 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2303 net_common_flags_usage(argc, argv);
2304 return -1;
2309 general ADS search function. Useful in diagnosing problems in ADS
2311 static int net_ads_dn(int argc, const char **argv)
2313 ADS_STRUCT *ads;
2314 ADS_STATUS rc;
2315 const char *dn;
2316 const char **attrs;
2317 LDAPMessage *res = NULL;
2319 if (argc < 1) {
2320 return net_ads_dn_usage(argc, argv);
2323 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2324 return -1;
2327 dn = argv[0];
2328 attrs = (argv + 1);
2330 rc = ads_do_search_all(ads, dn,
2331 LDAP_SCOPE_BASE,
2332 "(objectclass=*)", attrs, &res);
2333 if (!ADS_ERR_OK(rc)) {
2334 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2335 ads_destroy(&ads);
2336 return -1;
2339 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2341 /* dump the results */
2342 ads_dump(ads, res);
2344 ads_msgfree(ads, res);
2345 ads_destroy(&ads);
2347 return 0;
2351 help for net ads sid search
2353 static int net_ads_sid_usage(int argc, const char **argv)
2355 d_printf(
2356 "\nnet ads sid <sid> <attributes...>\n"\
2357 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2358 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2359 "to show in the results\n\n"\
2360 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2362 net_common_flags_usage(argc, argv);
2363 return -1;
2368 general ADS search function. Useful in diagnosing problems in ADS
2370 static int net_ads_sid(int argc, const char **argv)
2372 ADS_STRUCT *ads;
2373 ADS_STATUS rc;
2374 const char *sid_string;
2375 const char **attrs;
2376 LDAPMessage *res = NULL;
2377 DOM_SID sid;
2379 if (argc < 1) {
2380 return net_ads_sid_usage(argc, argv);
2383 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2384 return -1;
2387 sid_string = argv[0];
2388 attrs = (argv + 1);
2390 if (!string_to_sid(&sid, sid_string)) {
2391 d_fprintf(stderr, "could not convert sid\n");
2392 ads_destroy(&ads);
2393 return -1;
2396 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2397 if (!ADS_ERR_OK(rc)) {
2398 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2399 ads_destroy(&ads);
2400 return -1;
2403 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2405 /* dump the results */
2406 ads_dump(ads, res);
2408 ads_msgfree(ads, res);
2409 ads_destroy(&ads);
2411 return 0;
2415 static int net_ads_keytab_usage(int argc, const char **argv)
2417 d_printf(
2418 "net ads keytab <COMMAND>\n"\
2419 "<COMMAND> can be either:\n"\
2420 " ADD Adds new service principal\n"\
2421 " CREATE Creates a fresh keytab\n"\
2422 " FLUSH Flushes out all keytab entries\n"\
2423 " HELP Prints this help message\n"\
2424 " LIST List the keytab\n"\
2425 "The ADD and LIST command will take arguments, the other commands\n"\
2426 "will not take any arguments. The arguments given to ADD\n"\
2427 "should be a list of principals to add. For example, \n"\
2428 " net ads keytab add srv1 srv2\n"\
2429 "will add principals for the services srv1 and srv2 to the\n"\
2430 "system's keytab.\n"\
2431 "The LIST command takes a keytabname.\n"\
2432 "\n"
2434 return -1;
2437 static int net_ads_keytab_flush(int argc, const char **argv)
2439 int ret;
2440 ADS_STRUCT *ads;
2442 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2443 return -1;
2445 ret = ads_keytab_flush(ads);
2446 ads_destroy(&ads);
2447 return ret;
2450 static int net_ads_keytab_add(int argc, const char **argv)
2452 int i;
2453 int ret = 0;
2454 ADS_STRUCT *ads;
2456 d_printf("Processing principals to add...\n");
2457 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2458 return -1;
2460 for (i = 0; i < argc; i++) {
2461 ret |= ads_keytab_add_entry(ads, argv[i]);
2463 ads_destroy(&ads);
2464 return ret;
2467 static int net_ads_keytab_create(int argc, const char **argv)
2469 ADS_STRUCT *ads;
2470 int ret;
2472 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2473 return -1;
2475 ret = ads_keytab_create_default(ads);
2476 ads_destroy(&ads);
2477 return ret;
2480 static int net_ads_keytab_list(int argc, const char **argv)
2482 const char *keytab = NULL;
2484 if (argc >= 1) {
2485 keytab = argv[0];
2488 return ads_keytab_list(keytab);
2492 int net_ads_keytab(int argc, const char **argv)
2494 struct functable func[] = {
2495 {"ADD", net_ads_keytab_add},
2496 {"CREATE", net_ads_keytab_create},
2497 {"FLUSH", net_ads_keytab_flush},
2498 {"HELP", net_ads_keytab_usage},
2499 {"LIST", net_ads_keytab_list},
2500 {NULL, NULL}
2503 if (!lp_use_kerberos_keytab()) {
2504 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2505 use keytab functions.\n");
2508 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2511 int net_ads_help(int argc, const char **argv)
2513 struct functable func[] = {
2514 {"USER", net_ads_user_usage},
2515 {"GROUP", net_ads_group_usage},
2516 {"PRINTER", net_ads_printer_usage},
2517 {"SEARCH", net_ads_search_usage},
2518 {"INFO", net_ads_info},
2519 {"JOIN", net_ads_join_usage},
2520 {"DNS", net_ads_dns_usage},
2521 {"LEAVE", net_ads_leave},
2522 {"STATUS", net_ads_status},
2523 {"PASSWORD", net_ads_password},
2524 {"CHANGETRUSTPW", net_ads_changetrustpw},
2525 {NULL, NULL}
2528 return net_run_function(argc, argv, func, net_ads_usage);
2531 int net_ads(int argc, const char **argv)
2533 struct functable func[] = {
2534 {"INFO", net_ads_info},
2535 {"JOIN", net_ads_join},
2536 {"TESTJOIN", net_ads_testjoin},
2537 {"LEAVE", net_ads_leave},
2538 {"STATUS", net_ads_status},
2539 {"USER", net_ads_user},
2540 {"GROUP", net_ads_group},
2541 {"DNS", net_ads_dns},
2542 {"PASSWORD", net_ads_password},
2543 {"CHANGETRUSTPW", net_ads_changetrustpw},
2544 {"PRINTER", net_ads_printer},
2545 {"SEARCH", net_ads_search},
2546 {"DN", net_ads_dn},
2547 {"SID", net_ads_sid},
2548 {"WORKGROUP", net_ads_workgroup},
2549 {"LOOKUP", net_ads_lookup},
2550 {"KEYTAB", net_ads_keytab},
2551 {"GPO", net_ads_gpo},
2552 {"HELP", net_ads_help},
2553 {NULL, NULL}
2556 return net_run_function(argc, argv, func, net_ads_usage);
2559 #else
2561 static int net_ads_noads(void)
2563 d_fprintf(stderr, "ADS support not compiled in\n");
2564 return -1;
2567 int net_ads_keytab(int argc, const char **argv)
2569 return net_ads_noads();
2572 int net_ads_usage(int argc, const char **argv)
2574 return net_ads_noads();
2577 int net_ads_help(int argc, const char **argv)
2579 return net_ads_noads();
2582 int net_ads_changetrustpw(int argc, const char **argv)
2584 return net_ads_noads();
2587 int net_ads_join(int argc, const char **argv)
2589 return net_ads_noads();
2592 int net_ads_user(int argc, const char **argv)
2594 return net_ads_noads();
2597 int net_ads_group(int argc, const char **argv)
2599 return net_ads_noads();
2602 /* this one shouldn't display a message */
2603 int net_ads_check(void)
2605 return -1;
2608 int net_ads_check_our_domain(void)
2610 return -1;
2613 int net_ads(int argc, const char **argv)
2615 return net_ads_usage(argc, argv);
2618 #endif /* WITH_ADS */