r23041: Remainder of fix for 4630: fix special case of unix_to_nt_time() for
[Samba/nascimento.git] / source3 / utils / net_ads.c
bloba1206bbd5240509e38ccf3d1d726d4d57de74bf4
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;
222 /* lp_realm() should be handled by a command line param,
223 However, the join requires that realm be set in smb.conf
224 and compares our realm with the remote server's so this is
225 ok until someone needs more flexibility */
227 *ads_ret = NULL;
229 retry_connect:
230 if (only_own_domain) {
231 realm = lp_realm();
232 } else {
233 realm = assume_own_realm();
236 ads = ads_init(realm, opt_target_workgroup, opt_host);
238 if (!opt_user_name) {
239 opt_user_name = "administrator";
242 if (opt_user_specified) {
243 need_password = True;
246 retry:
247 if (!opt_password && need_password && !opt_machine_pass) {
248 char *prompt = NULL;
249 asprintf(&prompt,"%s's password: ", opt_user_name);
250 if (!prompt) {
251 ads_destroy(&ads);
252 return ADS_ERROR(LDAP_NO_MEMORY);
254 opt_password = getpass(prompt);
255 free(prompt);
258 if (opt_password) {
259 use_in_memory_ccache();
260 SAFE_FREE(ads->auth.password);
261 ads->auth.password = smb_xstrdup(opt_password);
264 ads->auth.flags |= auth_flags;
265 SAFE_FREE(ads->auth.user_name);
266 ads->auth.user_name = smb_xstrdup(opt_user_name);
269 * If the username is of the form "name@realm",
270 * extract the realm and convert to upper case.
271 * This is only used to establish the connection.
273 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
274 *cp++ = '\0';
275 SAFE_FREE(ads->auth.realm);
276 ads->auth.realm = smb_xstrdup(cp);
277 strupper_m(ads->auth.realm);
280 status = ads_connect(ads);
282 if (!ADS_ERR_OK(status)) {
284 if (NT_STATUS_EQUAL(ads_ntstatus(status),
285 NT_STATUS_NO_LOGON_SERVERS)) {
286 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
287 ads_destroy(&ads);
288 return status;
291 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
292 need_password = True;
293 second_time = True;
294 goto retry;
295 } else {
296 ads_destroy(&ads);
297 return status;
301 /* when contacting our own domain, make sure we use the closest DC.
302 * This is done by reconnecting to ADS because only the first call to
303 * ads_connect will give us our own sitename */
305 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
307 tried_closest_dc = True; /* avoid loop */
309 if (!ads->config.tried_closest_dc) {
311 namecache_delete(ads->server.realm, 0x1C);
312 namecache_delete(ads->server.workgroup, 0x1C);
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;
441 char *ou_str = NULL;
443 if (argc < 1) return net_ads_user_usage(argc, argv);
445 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
446 return -1;
449 status = ads_find_user_acct(ads, &res, argv[0]);
451 if (!ADS_ERR_OK(status)) {
452 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
453 goto done;
456 if (ads_count_replies(ads, res)) {
457 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
458 goto done;
461 if (opt_container) {
462 ou_str = SMB_STRDUP(opt_container);
463 } else {
464 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
467 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
469 if (!ADS_ERR_OK(status)) {
470 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
471 ads_errstr(status));
472 goto done;
475 /* if no password is to be set, we're done */
476 if (argc == 1) {
477 d_printf("User %s added\n", argv[0]);
478 rc = 0;
479 goto done;
482 /* try setting the password */
483 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
484 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
485 ads->auth.time_offset);
486 safe_free(upn);
487 if (ADS_ERR_OK(status)) {
488 d_printf("User %s added\n", argv[0]);
489 rc = 0;
490 goto done;
493 /* password didn't set, delete account */
494 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
495 argv[0], ads_errstr(status));
496 ads_msgfree(ads, res);
497 status=ads_find_user_acct(ads, &res, argv[0]);
498 if (ADS_ERR_OK(status)) {
499 userdn = ads_get_dn(ads, res);
500 ads_del_dn(ads, userdn);
501 ads_memfree(ads, userdn);
504 done:
505 if (res)
506 ads_msgfree(ads, res);
507 ads_destroy(&ads);
508 SAFE_FREE(ou_str);
509 return rc;
512 static int ads_user_info(int argc, const char **argv)
514 ADS_STRUCT *ads;
515 ADS_STATUS rc;
516 LDAPMessage *res;
517 const char *attrs[] = {"memberOf", NULL};
518 char *searchstring=NULL;
519 char **grouplist;
520 char *escaped_user;
522 if (argc < 1) {
523 return net_ads_user_usage(argc, argv);
526 escaped_user = escape_ldap_string_alloc(argv[0]);
528 if (!escaped_user) {
529 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
530 return -1;
533 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
534 SAFE_FREE(escaped_user);
535 return -1;
538 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
539 rc = ads_search(ads, &res, searchstring, attrs);
540 safe_free(searchstring);
542 if (!ADS_ERR_OK(rc)) {
543 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
544 ads_destroy(&ads);
545 SAFE_FREE(escaped_user);
546 return -1;
549 grouplist = ldap_get_values((LDAP *)ads->ld,
550 (LDAPMessage *)res, "memberOf");
552 if (grouplist) {
553 int i;
554 char **groupname;
555 for (i=0;grouplist[i];i++) {
556 groupname = ldap_explode_dn(grouplist[i], 1);
557 d_printf("%s\n", groupname[0]);
558 ldap_value_free(groupname);
560 ldap_value_free(grouplist);
563 ads_msgfree(ads, res);
564 ads_destroy(&ads);
565 SAFE_FREE(escaped_user);
566 return 0;
569 static int ads_user_delete(int argc, const char **argv)
571 ADS_STRUCT *ads;
572 ADS_STATUS rc;
573 LDAPMessage *res = NULL;
574 char *userdn;
576 if (argc < 1) {
577 return net_ads_user_usage(argc, argv);
580 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
581 return -1;
584 rc = ads_find_user_acct(ads, &res, argv[0]);
585 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
586 d_printf("User %s does not exist.\n", argv[0]);
587 ads_msgfree(ads, res);
588 ads_destroy(&ads);
589 return -1;
591 userdn = ads_get_dn(ads, res);
592 ads_msgfree(ads, res);
593 rc = ads_del_dn(ads, userdn);
594 ads_memfree(ads, userdn);
595 if (ADS_ERR_OK(rc)) {
596 d_printf("User %s deleted\n", argv[0]);
597 ads_destroy(&ads);
598 return 0;
600 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
601 ads_errstr(rc));
602 ads_destroy(&ads);
603 return -1;
606 int net_ads_user(int argc, const char **argv)
608 struct functable func[] = {
609 {"ADD", ads_user_add},
610 {"INFO", ads_user_info},
611 {"DELETE", ads_user_delete},
612 {NULL, NULL}
614 ADS_STRUCT *ads;
615 ADS_STATUS rc;
616 const char *shortattrs[] = {"sAMAccountName", NULL};
617 const char *longattrs[] = {"sAMAccountName", "description", NULL};
618 char *disp_fields[2] = {NULL, NULL};
620 if (argc == 0) {
621 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
622 return -1;
625 if (opt_long_list_entries)
626 d_printf("\nUser name Comment"\
627 "\n-----------------------------\n");
629 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
630 LDAP_SCOPE_SUBTREE,
631 "(objectCategory=user)",
632 opt_long_list_entries ? longattrs :
633 shortattrs, usergrp_display,
634 disp_fields);
635 ads_destroy(&ads);
636 return ADS_ERR_OK(rc) ? 0 : -1;
639 return net_run_function(argc, argv, func, net_ads_user_usage);
642 static int net_ads_group_usage(int argc, const char **argv)
644 return net_help_group(argc, argv);
647 static int ads_group_add(int argc, const char **argv)
649 ADS_STRUCT *ads;
650 ADS_STATUS status;
651 LDAPMessage *res=NULL;
652 int rc = -1;
653 char *ou_str = NULL;
655 if (argc < 1) {
656 return net_ads_group_usage(argc, argv);
659 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
660 return -1;
663 status = ads_find_user_acct(ads, &res, argv[0]);
665 if (!ADS_ERR_OK(status)) {
666 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
667 goto done;
670 if (ads_count_replies(ads, res)) {
671 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
672 goto done;
675 if (opt_container) {
676 ou_str = SMB_STRDUP(opt_container);
677 } else {
678 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
681 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
683 if (ADS_ERR_OK(status)) {
684 d_printf("Group %s added\n", argv[0]);
685 rc = 0;
686 } else {
687 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
688 ads_errstr(status));
691 done:
692 if (res)
693 ads_msgfree(ads, res);
694 ads_destroy(&ads);
695 SAFE_FREE(ou_str);
696 return rc;
699 static int ads_group_delete(int argc, const char **argv)
701 ADS_STRUCT *ads;
702 ADS_STATUS rc;
703 LDAPMessage *res = NULL;
704 char *groupdn;
706 if (argc < 1) {
707 return net_ads_group_usage(argc, argv);
710 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
711 return -1;
714 rc = ads_find_user_acct(ads, &res, argv[0]);
715 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
716 d_printf("Group %s does not exist.\n", argv[0]);
717 ads_msgfree(ads, res);
718 ads_destroy(&ads);
719 return -1;
721 groupdn = ads_get_dn(ads, res);
722 ads_msgfree(ads, res);
723 rc = ads_del_dn(ads, groupdn);
724 ads_memfree(ads, groupdn);
725 if (ADS_ERR_OK(rc)) {
726 d_printf("Group %s deleted\n", argv[0]);
727 ads_destroy(&ads);
728 return 0;
730 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
731 ads_errstr(rc));
732 ads_destroy(&ads);
733 return -1;
736 int net_ads_group(int argc, const char **argv)
738 struct functable func[] = {
739 {"ADD", ads_group_add},
740 {"DELETE", ads_group_delete},
741 {NULL, NULL}
743 ADS_STRUCT *ads;
744 ADS_STATUS rc;
745 const char *shortattrs[] = {"sAMAccountName", NULL};
746 const char *longattrs[] = {"sAMAccountName", "description", NULL};
747 char *disp_fields[2] = {NULL, NULL};
749 if (argc == 0) {
750 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
751 return -1;
754 if (opt_long_list_entries)
755 d_printf("\nGroup name Comment"\
756 "\n-----------------------------\n");
757 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
758 LDAP_SCOPE_SUBTREE,
759 "(objectCategory=group)",
760 opt_long_list_entries ? longattrs :
761 shortattrs, usergrp_display,
762 disp_fields);
764 ads_destroy(&ads);
765 return ADS_ERR_OK(rc) ? 0 : -1;
767 return net_run_function(argc, argv, func, net_ads_group_usage);
770 static int net_ads_status(int argc, const char **argv)
772 ADS_STRUCT *ads;
773 ADS_STATUS rc;
774 LDAPMessage *res;
776 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
777 return -1;
780 rc = ads_find_machine_acct(ads, &res, global_myname());
781 if (!ADS_ERR_OK(rc)) {
782 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
783 ads_destroy(&ads);
784 return -1;
787 if (ads_count_replies(ads, res) == 0) {
788 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
789 ads_destroy(&ads);
790 return -1;
793 ads_dump(ads, res);
794 ads_destroy(&ads);
795 return 0;
798 /*******************************************************************
799 Leave an AD domain. Windows XP disables the machine account.
800 We'll try the same. The old code would do an LDAP delete.
801 That only worked using the machine creds because added the machine
802 with full control to the computer object's ACL.
803 *******************************************************************/
805 static int net_ads_leave(int argc, const char **argv)
807 ADS_STRUCT *ads = NULL;
808 ADS_STATUS adsret;
809 NTSTATUS status;
810 int ret = -1;
811 struct cli_state *cli = NULL;
812 TALLOC_CTX *ctx;
813 DOM_SID *dom_sid = NULL;
814 char *short_domain_name = NULL;
816 if (!secrets_init()) {
817 DEBUG(1,("Failed to initialise secrets database\n"));
818 return -1;
821 if (!(ctx = talloc_init("net_ads_leave"))) {
822 d_fprintf(stderr, "Could not initialise talloc context.\n");
823 return -1;
826 /* The finds a DC and takes care of getting the
827 user creds if necessary */
829 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
830 return -1;
833 /* make RPC calls here */
835 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
836 ads->config.ldap_server_name)) )
838 goto done;
841 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
842 goto done;
845 saf_delete( short_domain_name );
847 status = netdom_leave_domain(ctx, cli, dom_sid);
849 /* Try and delete it via LDAP - the old way we used to. */
851 adsret = ads_leave_realm(ads, global_myname());
852 if (ADS_ERR_OK(adsret)) {
853 d_printf("Deleted account for '%s' in realm '%s'\n",
854 global_myname(), ads->config.realm);
855 ret = 0;
856 } else {
857 /* We couldn't delete it - see if the disable succeeded. */
858 if (NT_STATUS_IS_OK(status)) {
859 d_printf("Disabled account for '%s' in realm '%s'\n",
860 global_myname(), ads->config.realm);
861 ret = 0;
862 } else {
863 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
864 global_myname(), ads->config.realm);
868 done:
870 if ( cli )
871 cli_shutdown(cli);
873 ads_destroy(&ads);
874 TALLOC_FREE( ctx );
876 return ret;
879 static NTSTATUS net_ads_join_ok(void)
881 ADS_STRUCT *ads = NULL;
882 ADS_STATUS status;
884 if (!secrets_init()) {
885 DEBUG(1,("Failed to initialise secrets database\n"));
886 return NT_STATUS_ACCESS_DENIED;
889 net_use_machine_password();
891 status = ads_startup(True, &ads);
892 if (!ADS_ERR_OK(status)) {
893 return ads_ntstatus(status);
896 ads_destroy(&ads);
897 return NT_STATUS_OK;
901 check that an existing join is OK
903 int net_ads_testjoin(int argc, const char **argv)
905 NTSTATUS status;
906 use_in_memory_ccache();
908 /* Display success or failure */
909 status = net_ads_join_ok();
910 if (!NT_STATUS_IS_OK(status)) {
911 fprintf(stderr,"Join to domain is not valid: %s\n",
912 get_friendly_nt_error_msg(status));
913 return -1;
916 printf("Join is OK\n");
917 return 0;
920 /*******************************************************************
921 Simple configu checks before beginning the join
922 ********************************************************************/
924 static NTSTATUS check_ads_config( void )
926 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
927 d_printf("Host is not configured as a member server.\n");
928 return NT_STATUS_INVALID_DOMAIN_ROLE;
931 if (strlen(global_myname()) > 15) {
932 d_printf("Our netbios name can be at most 15 chars long, "
933 "\"%s\" is %u chars long\n", global_myname(),
934 (unsigned int)strlen(global_myname()));
935 return NT_STATUS_NAME_TOO_LONG;
938 if ( lp_security() == SEC_ADS && !*lp_realm()) {
939 d_fprintf(stderr, "realm must be set in in %s for ADS "
940 "join to succeed.\n", dyn_CONFIGFILE);
941 return NT_STATUS_INVALID_PARAMETER;
944 if (!secrets_init()) {
945 DEBUG(1,("Failed to initialise secrets database\n"));
946 /* This is a good bet for failure of secrets_init ... */
947 return NT_STATUS_ACCESS_DENIED;
950 return NT_STATUS_OK;
953 /*******************************************************************
954 Do the domain join
955 ********************************************************************/
957 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
958 struct in_addr *ip, char **domain,
959 DOM_SID **dom_sid,
960 const char *password)
962 NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
963 struct cli_state *cli = NULL;
965 ret = connect_to_ipc_krb5(&cli, ip, servername);
966 if ( !NT_STATUS_IS_OK(ret) ) {
967 goto done;
970 ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
971 if ( !NT_STATUS_IS_OK(ret) ) {
972 goto done;
975 /* cli->server_domain is not filled in when using krb5
976 session setups */
978 saf_store( *domain, cli->desthost );
980 ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
982 done:
983 if ( cli )
984 cli_shutdown(cli);
986 return ret;
989 /*******************************************************************
990 Set a machines dNSHostName and servicePrincipalName attributes
991 ********************************************************************/
993 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
995 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
996 char *new_dn;
997 ADS_MODLIST mods;
998 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
999 char *psp;
1000 fstring my_fqdn;
1001 LDAPMessage *res = NULL;
1002 char *dn_string = NULL;
1003 const char *machine_name = global_myname();
1004 int count;
1006 if ( !machine_name ) {
1007 return ADS_ERROR(LDAP_NO_MEMORY);
1010 /* Find our DN */
1012 status = ads_find_machine_acct(ads_s, &res, machine_name);
1013 if (!ADS_ERR_OK(status))
1014 return status;
1016 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1017 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1018 return ADS_ERROR(LDAP_NO_MEMORY);
1021 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1022 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1023 goto done;
1026 new_dn = talloc_strdup(ctx, dn_string);
1027 ads_memfree(ads_s, dn_string);
1028 if (!new_dn) {
1029 return ADS_ERROR(LDAP_NO_MEMORY);
1032 /* Windows only creates HOST/shortname & HOST/fqdn. */
1034 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
1035 goto done;
1036 strupper_m(psp);
1037 servicePrincipalName[0] = psp;
1039 name_to_fqdn(my_fqdn, machine_name);
1040 strlower_m(my_fqdn);
1041 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
1042 goto done;
1043 servicePrincipalName[1] = psp;
1045 if (!(mods = ads_init_mods(ctx))) {
1046 goto done;
1049 /* fields of primary importance */
1051 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1052 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1054 status = ads_gen_mod(ads_s, new_dn, mods);
1056 done:
1057 ads_msgfree(ads_s, res);
1059 return status;
1062 /*******************************************************************
1063 Set a machines dNSHostName and servicePrincipalName attributes
1064 ********************************************************************/
1066 static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
1068 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1069 char *new_dn;
1070 ADS_MODLIST mods;
1071 LDAPMessage *res = NULL;
1072 char *dn_string = NULL;
1073 const char *machine_name = global_myname();
1074 int count;
1076 if ( !machine_name ) {
1077 return ADS_ERROR(LDAP_NO_MEMORY);
1080 /* Find our DN */
1082 status = ads_find_machine_acct(ads_s, &res, machine_name);
1083 if (!ADS_ERR_OK(status))
1084 return status;
1086 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1087 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1088 return ADS_ERROR(LDAP_NO_MEMORY);
1091 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1092 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1093 goto done;
1096 new_dn = talloc_strdup(ctx, dn_string);
1097 ads_memfree(ads_s, dn_string);
1098 if (!new_dn) {
1099 return ADS_ERROR(LDAP_NO_MEMORY);
1102 /* now do the mods */
1104 if (!(mods = ads_init_mods(ctx))) {
1105 goto done;
1108 /* fields of primary importance */
1110 ads_mod_str(ctx, &mods, "userPrincipalName", upn);
1112 status = ads_gen_mod(ads_s, new_dn, mods);
1114 done:
1115 ads_msgfree(ads_s, res);
1117 return status;
1120 /*******************************************************************
1121 Set a machines dNSHostName and servicePrincipalName attributes
1122 ********************************************************************/
1124 static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s,
1125 const char *os_name, const char *os_version )
1127 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
1128 char *new_dn;
1129 ADS_MODLIST mods;
1130 LDAPMessage *res = NULL;
1131 char *dn_string = NULL;
1132 const char *machine_name = global_myname();
1133 int count;
1134 char *os_sp = NULL;
1136 if ( !os_name || !os_version ) {
1137 return ADS_ERROR(LDAP_NO_MEMORY);
1140 /* Find our DN */
1142 status = ads_find_machine_acct(ads_s, &res, machine_name);
1143 if (!ADS_ERR_OK(status))
1144 return status;
1146 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
1147 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1148 return ADS_ERROR(LDAP_NO_MEMORY);
1151 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
1152 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
1153 goto done;
1156 new_dn = talloc_strdup(ctx, dn_string);
1157 ads_memfree(ads_s, dn_string);
1158 if (!new_dn) {
1159 return ADS_ERROR(LDAP_NO_MEMORY);
1162 /* now do the mods */
1164 if (!(mods = ads_init_mods(ctx))) {
1165 goto done;
1168 os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
1170 /* fields of primary importance */
1172 ads_mod_str(ctx, &mods, "operatingSystem", os_name);
1173 ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
1174 if ( os_sp )
1175 ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
1177 status = ads_gen_mod(ads_s, new_dn, mods);
1179 done:
1180 ads_msgfree(ads_s, res);
1181 TALLOC_FREE( os_sp );
1183 return status;
1186 /*******************************************************************
1187 join a domain using ADS (LDAP mods)
1188 ********************************************************************/
1190 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1192 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1193 char *ou_str = NULL;
1194 char *dn = NULL;
1195 LDAPMessage *res = NULL;
1196 BOOL moved;
1198 ou_str = ads_ou_string(ads, ou);
1199 if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
1200 rc = ADS_ERROR(LDAP_NO_MEMORY);
1201 goto done;
1204 rc = ads_search_dn(ads, &res, dn, NULL);
1205 if (!ADS_ERR_OK(rc)) {
1206 d_fprintf(stderr, "The specified OU does not exist.\n");
1207 goto done;
1210 /* Attempt to create the machine account and bail if this fails.
1211 Assume that the admin wants exactly what they requested */
1213 rc = ads_create_machine_acct( ads, global_myname(), dn );
1214 if (ADS_ERR_OK(rc)) {
1215 DEBUG(1, ("machine account created\n"));
1216 goto done;
1218 if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
1219 DEBUG(1, ("machine account creation failed\n"));
1220 goto done;
1223 rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
1224 if (!ADS_ERR_OK(rc)) {
1225 DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
1226 goto done;
1229 if (moved) {
1230 d_printf("The machine account was moved into the specified OU.\n");
1231 } else {
1232 d_printf("The machine account already exists in the specified OU.\n");
1235 done:
1236 ads_msgfree(ads, res);
1237 SAFE_FREE( ou_str );
1238 SAFE_FREE( dn );
1240 return rc;
1243 /************************************************************************
1244 ************************************************************************/
1246 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1248 uint32 domain_func;
1249 ADS_STATUS status;
1250 fstring salt;
1251 char *std_salt;
1252 LDAPMessage *res = NULL;
1253 const char *machine_name = global_myname();
1255 status = ads_domain_func_level( ads, &domain_func );
1256 if ( !ADS_ERR_OK(status) ) {
1257 DEBUG(2,("Failed to determine domain functional level!\n"));
1258 return False;
1261 /* go ahead and setup the default salt */
1263 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1264 d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
1265 return False;
1268 fstrcpy( salt, std_salt );
1269 SAFE_FREE( std_salt );
1271 /* if it's a Windows functional domain, we have to look for the UPN */
1273 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1274 char *upn;
1275 int count;
1277 status = ads_find_machine_acct(ads, &res, machine_name);
1278 if (!ADS_ERR_OK(status)) {
1279 return False;
1282 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1283 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1284 return False;
1287 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1288 if ( upn ) {
1289 fstrcpy( salt, upn );
1292 ads_msgfree(ads, res);
1295 return kerberos_secrets_store_des_salt( salt );
1298 /*******************************************************************
1299 Send a DNS update request
1300 *******************************************************************/
1302 #if defined(WITH_DNS_UPDATES)
1303 #include "dns.h"
1304 DNS_ERROR DoDNSUpdate(char *pszServerName,
1305 const char *pszDomainName,
1306 const char *pszHostName,
1307 const struct in_addr *iplist, int num_addrs );
1310 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
1311 const char *machine_name,
1312 const struct in_addr *addrs,
1313 int num_addrs)
1315 struct dns_rr_ns *nameservers = NULL;
1316 int ns_count = 0;
1317 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1318 DNS_ERROR dns_err;
1319 fstring dns_server;
1320 const char *dnsdomain = NULL;
1321 char *root_domain = NULL;
1323 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
1324 d_printf("No DNS domain configured for %s. "
1325 "Unable to perform DNS Update.\n", machine_name);
1326 status = NT_STATUS_INVALID_PARAMETER;
1327 goto done;
1329 dnsdomain++;
1331 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
1332 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1333 /* Child domains often do not have NS records. Look
1334 for the NS record for the forest root domain
1335 (rootDomainNamingContext in therootDSE) */
1337 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
1338 LDAPMessage *msg = NULL;
1339 char *root_dn;
1340 ADS_STATUS ads_status;
1342 if ( !ads->ld ) {
1343 ads_status = ads_connect( ads );
1344 if ( !ADS_ERR_OK(ads_status) ) {
1345 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
1346 goto done;
1350 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1351 "(objectclass=*)", rootname_attrs, &msg);
1352 if (!ADS_ERR_OK(ads_status)) {
1353 goto done;
1356 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1357 if ( !root_dn ) {
1358 ads_msgfree( ads, msg );
1359 goto done;
1362 root_domain = ads_build_domain( root_dn );
1364 /* cleanup */
1365 ads_msgfree( ads, msg );
1367 /* try again for NS servers */
1369 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1371 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1372 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1373 "realm\n", ads->config.realm));
1374 goto done;
1377 dnsdomain = root_domain;
1381 /* Now perform the dns update - we'll try non-secure and if we fail,
1382 we'll follow it up with a secure update */
1384 fstrcpy( dns_server, nameservers[0].hostname );
1386 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1387 if (!ERR_DNS_IS_OK(dns_err)) {
1388 status = NT_STATUS_UNSUCCESSFUL;
1391 done:
1393 SAFE_FREE( root_domain );
1395 return status;
1398 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1400 int num_addrs;
1401 struct in_addr *iplist = NULL;
1402 fstring machine_name;
1403 NTSTATUS status;
1405 name_to_fqdn( machine_name, global_myname() );
1406 strlower_m( machine_name );
1408 /* Get our ip address (not the 127.0.0.x address but a real ip
1409 * address) */
1411 num_addrs = get_my_ip_address( &iplist );
1412 if ( num_addrs <= 0 ) {
1413 DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
1414 "addresses!\n"));
1415 return NT_STATUS_INVALID_PARAMETER;
1418 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1419 iplist, num_addrs);
1420 SAFE_FREE( iplist );
1421 return status;
1423 #endif
1426 /*******************************************************************
1427 utility function to parse an integer parameter from
1428 "parameter = value"
1429 **********************************************************/
1430 static char* get_string_param( const char* param )
1432 char *p;
1434 if ( (p = strchr( param, '=' )) == NULL )
1435 return NULL;
1437 return (p+1);
1440 /*******************************************************************
1441 ********************************************************************/
1443 static int net_ads_join_usage(int argc, const char **argv)
1445 d_printf("net ads join [options]\n");
1446 d_printf("Valid options:\n");
1447 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1448 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1449 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1450 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1451 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1452 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1453 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1454 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1455 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1456 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1457 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1458 d_printf(" the two other attributes.\n");
1460 return -1;
1463 /*******************************************************************
1464 ********************************************************************/
1466 int net_ads_join(int argc, const char **argv)
1468 ADS_STRUCT *ads = NULL;
1469 ADS_STATUS status;
1470 NTSTATUS nt_status;
1471 char *machine_account = NULL;
1472 char *short_domain_name = NULL;
1473 char *tmp_password, *password;
1474 TALLOC_CTX *ctx = NULL;
1475 DOM_SID *domain_sid = NULL;
1476 BOOL createupn = False;
1477 const char *machineupn = NULL;
1478 const char *create_in_ou = NULL;
1479 int i;
1480 fstring dc_name;
1481 struct in_addr dcip;
1482 const char *os_name = NULL;
1483 const char *os_version = NULL;
1485 nt_status = check_ads_config();
1486 if (!NT_STATUS_IS_OK(nt_status)) {
1487 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1488 goto fail;
1491 /* find a DC to initialize the server affinity cache */
1493 get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
1495 status = ads_startup(True, &ads);
1496 if (!ADS_ERR_OK(status)) {
1497 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1498 nt_status = ads_ntstatus(status);
1499 goto fail;
1502 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1503 d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
1504 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1505 dyn_CONFIGFILE, lp_realm());
1506 nt_status = NT_STATUS_INVALID_PARAMETER;
1507 goto fail;
1510 if (!(ctx = talloc_init("net_ads_join"))) {
1511 d_fprintf(stderr, "Could not initialise talloc context.\n");
1512 nt_status = NT_STATUS_NO_MEMORY;
1513 goto fail;
1516 /* process additional command line args */
1518 for ( i=0; i<argc; i++ ) {
1519 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1520 createupn = True;
1521 machineupn = get_string_param(argv[i]);
1523 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1524 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1525 d_fprintf(stderr, "Please supply a valid OU path.\n");
1526 nt_status = NT_STATUS_INVALID_PARAMETER;
1527 goto fail;
1530 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1531 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1532 d_fprintf(stderr, "Please supply a operating system name.\n");
1533 nt_status = NT_STATUS_INVALID_PARAMETER;
1534 goto fail;
1537 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1538 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1539 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1540 nt_status = NT_STATUS_INVALID_PARAMETER;
1541 goto fail;
1544 else {
1545 d_fprintf(stderr, "Bad option: %s\n", argv[i]);
1546 nt_status = NT_STATUS_INVALID_PARAMETER;
1547 goto fail;
1551 /* If we were given an OU, try to create the machine in
1552 the OU account first and then do the normal RPC join */
1554 if ( create_in_ou ) {
1555 status = net_precreate_machine_acct( ads, create_in_ou );
1556 if ( !ADS_ERR_OK(status) ) {
1557 d_fprintf( stderr, "Failed to pre-create the machine object "
1558 "in OU %s.\n", create_in_ou);
1559 DEBUG(1, ("error calling net_precreate_machine_acct: %s\n",
1560 ads_errstr(status)));
1561 nt_status = ads_ntstatus(status);
1562 goto fail;
1566 /* Do the domain join here */
1568 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1569 password = talloc_strdup(ctx, tmp_password);
1571 nt_status = net_join_domain(ctx, ads->config.ldap_server_name,
1572 &ads->ldap_ip, &short_domain_name, &domain_sid, password);
1573 if ( !NT_STATUS_IS_OK(nt_status) ) {
1574 DEBUG(1, ("call of net_join_domain failed: %s\n",
1575 get_friendly_nt_error_msg(nt_status)));
1576 goto fail;
1579 /* Check the short name of the domain */
1581 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1582 d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
1583 d_printf("domain name obtained from the server.\n");
1584 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1585 d_printf("You should set \"workgroup = %s\" in %s.\n",
1586 short_domain_name, dyn_CONFIGFILE);
1589 d_printf("Using short domain name -- %s\n", short_domain_name);
1591 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1592 value from smb.conf and the string returned from the server. The former is
1593 neede to bootstrap winbindd's first connection to the DC to get the real
1594 short domain name --jerry */
1596 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1597 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1599 /* issue an internal error here for now.
1600 * everything else would mean changing tdb routines. */
1601 nt_status = NT_STATUS_INTERNAL_ERROR;
1602 goto fail;
1605 /* Verify that everything is ok */
1607 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1608 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1609 goto fail;
1612 /* create the dNSHostName & servicePrincipalName values */
1614 status = net_set_machine_spn( ctx, ads );
1615 if ( !ADS_ERR_OK(status) ) {
1617 d_fprintf(stderr, "Failed to set servicePrincipalNames. Please ensure that\n");
1618 d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
1619 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1621 /* Disable the machine account in AD. Better to fail than to leave
1622 a confused admin. */
1624 if ( net_ads_leave( 0, NULL ) != 0 ) {
1625 d_fprintf( stderr, "Failed to disable machine account in AD. Please do so manually.\n");
1628 /* clear out the machine password */
1630 netdom_store_machine_account( lp_workgroup(), domain_sid, "" );
1631 netdom_store_machine_account( short_domain_name, domain_sid, "" );
1633 nt_status = ads_ntstatus(status);
1634 goto fail;
1637 if ( !net_derive_salting_principal( ctx, ads ) ) {
1638 DEBUG(1,("Failed to determine salting principal\n"));
1639 goto fail;
1642 if ( createupn ) {
1643 pstring upn;
1645 /* default to using the short UPN name */
1646 if ( !machineupn ) {
1647 snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(),
1648 ads->config.realm );
1649 machineupn = upn;
1652 status = net_set_machine_upn( ctx, ads, machineupn );
1653 if ( !ADS_ERR_OK(status) ) {
1654 d_fprintf(stderr, "Failed to set userPrincipalName. Are you a Domain Admin?\n");
1658 /* Try to set the operatingSystem attributes if asked */
1660 if ( os_name && os_version ) {
1661 status = net_set_os_attributes( ctx, ads, os_name, os_version );
1662 if ( !ADS_ERR_OK(status) ) {
1663 d_fprintf(stderr, "Failed to set operatingSystem attributes. "
1664 "Are you a Domain Admin?\n");
1668 /* Now build the keytab, using the same ADS connection */
1670 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1671 DEBUG(1,("Error creating host keytab!\n"));
1674 #if defined(WITH_DNS_UPDATES)
1675 /* We enter this block with user creds */
1676 ads_kdestroy( NULL );
1677 ads_destroy(&ads);
1678 ads = NULL;
1680 if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1681 /* kinit with the machine password */
1683 use_in_memory_ccache();
1684 asprintf( &ads->auth.user_name, "%s$", global_myname() );
1685 ads->auth.password = secrets_fetch_machine_password(
1686 lp_workgroup(), NULL, NULL );
1687 ads->auth.realm = SMB_STRDUP( lp_realm() );
1688 ads_kinit_password( ads );
1691 if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
1692 d_fprintf( stderr, "DNS update failed!\n" );
1695 /* exit from this block using machine creds */
1696 #endif
1698 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
1700 SAFE_FREE(machine_account);
1701 TALLOC_FREE( ctx );
1702 ads_destroy(&ads);
1704 return 0;
1706 fail:
1707 /* issue an overall failure message at the end. */
1708 d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
1710 SAFE_FREE(machine_account);
1711 TALLOC_FREE( ctx );
1712 ads_destroy(&ads);
1714 return -1;
1718 /*******************************************************************
1719 ********************************************************************/
1721 static int net_ads_dns_usage(int argc, const char **argv)
1723 #if defined(WITH_DNS_UPDATES)
1724 d_printf("net ads dns <command>\n");
1725 d_printf("Valid commands:\n");
1726 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1728 return 0;
1729 #else
1730 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1731 return -1;
1732 #endif
1735 /*******************************************************************
1736 ********************************************************************/
1738 static int net_ads_dns_register(int argc, const char **argv)
1740 #if defined(WITH_DNS_UPDATES)
1741 ADS_STRUCT *ads;
1742 ADS_STATUS status;
1743 TALLOC_CTX *ctx;
1745 #ifdef DEVELOPER
1746 talloc_enable_leak_report();
1747 #endif
1749 if (argc > 0) {
1750 d_fprintf(stderr, "net ads dns register <name> <ip>\n");
1751 return -1;
1754 if (!(ctx = talloc_init("net_ads_dns"))) {
1755 d_fprintf(stderr, "Could not initialise talloc context\n");
1756 return -1;
1759 status = ads_startup(True, &ads);
1760 if ( !ADS_ERR_OK(status) ) {
1761 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1762 TALLOC_FREE(ctx);
1763 return -1;
1766 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1767 d_fprintf( stderr, "DNS update failed!\n" );
1768 ads_destroy( &ads );
1769 TALLOC_FREE( ctx );
1770 return -1;
1773 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1775 ads_destroy(&ads);
1776 TALLOC_FREE( ctx );
1778 return 0;
1779 #else
1780 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1781 return -1;
1782 #endif
1785 #if defined(WITH_DNS_UPDATES)
1786 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1787 #endif
1789 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1791 #if defined(WITH_DNS_UPDATES)
1792 DNS_ERROR err;
1794 #ifdef DEVELOPER
1795 talloc_enable_leak_report();
1796 #endif
1798 if (argc != 2) {
1799 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1800 "<name>\n");
1801 return -1;
1804 err = do_gethostbyname(argv[0], argv[1]);
1806 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1807 #endif
1808 return 0;
1811 static int net_ads_dns(int argc, const char *argv[])
1813 struct functable func[] = {
1814 {"REGISTER", net_ads_dns_register},
1815 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1816 {NULL, NULL}
1819 return net_run_function(argc, argv, func, net_ads_dns_usage);
1822 /*******************************************************************
1823 ********************************************************************/
1825 int net_ads_printer_usage(int argc, const char **argv)
1827 d_printf(
1828 "\nnet ads printer search <printer>"
1829 "\n\tsearch for a printer in the directory\n"
1830 "\nnet ads printer info <printer> <server>"
1831 "\n\tlookup info in directory for printer on server"
1832 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1833 "\nnet ads printer publish <printername>"
1834 "\n\tpublish printer in directory"
1835 "\n\t(note: printer name is required)\n"
1836 "\nnet ads printer remove <printername>"
1837 "\n\tremove printer from directory"
1838 "\n\t(note: printer name is required)\n");
1839 return -1;
1842 /*******************************************************************
1843 ********************************************************************/
1845 static int net_ads_printer_search(int argc, const char **argv)
1847 ADS_STRUCT *ads;
1848 ADS_STATUS rc;
1849 LDAPMessage *res = NULL;
1851 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1852 return -1;
1855 rc = ads_find_printers(ads, &res);
1857 if (!ADS_ERR_OK(rc)) {
1858 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1859 ads_msgfree(ads, res);
1860 ads_destroy(&ads);
1861 return -1;
1864 if (ads_count_replies(ads, res) == 0) {
1865 d_fprintf(stderr, "No results found\n");
1866 ads_msgfree(ads, res);
1867 ads_destroy(&ads);
1868 return -1;
1871 ads_dump(ads, res);
1872 ads_msgfree(ads, res);
1873 ads_destroy(&ads);
1874 return 0;
1877 static int net_ads_printer_info(int argc, const char **argv)
1879 ADS_STRUCT *ads;
1880 ADS_STATUS rc;
1881 const char *servername, *printername;
1882 LDAPMessage *res = NULL;
1884 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1885 return -1;
1888 if (argc > 0) {
1889 printername = argv[0];
1890 } else {
1891 printername = "*";
1894 if (argc > 1) {
1895 servername = argv[1];
1896 } else {
1897 servername = global_myname();
1900 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1902 if (!ADS_ERR_OK(rc)) {
1903 d_fprintf(stderr, "Server '%s' not found: %s\n",
1904 servername, ads_errstr(rc));
1905 ads_msgfree(ads, res);
1906 ads_destroy(&ads);
1907 return -1;
1910 if (ads_count_replies(ads, res) == 0) {
1911 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1912 ads_msgfree(ads, res);
1913 ads_destroy(&ads);
1914 return -1;
1917 ads_dump(ads, res);
1918 ads_msgfree(ads, res);
1919 ads_destroy(&ads);
1921 return 0;
1924 static int net_ads_printer_publish(int argc, const char **argv)
1926 ADS_STRUCT *ads;
1927 ADS_STATUS rc;
1928 const char *servername, *printername;
1929 struct cli_state *cli;
1930 struct rpc_pipe_client *pipe_hnd;
1931 struct in_addr server_ip;
1932 NTSTATUS nt_status;
1933 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1934 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1935 char *prt_dn, *srv_dn, **srv_cn;
1936 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1937 LDAPMessage *res = NULL;
1939 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1940 talloc_destroy(mem_ctx);
1941 return -1;
1944 if (argc < 1) {
1945 talloc_destroy(mem_ctx);
1946 return net_ads_printer_usage(argc, argv);
1949 printername = argv[0];
1951 if (argc == 2) {
1952 servername = argv[1];
1953 } else {
1954 servername = global_myname();
1957 /* Get printer data from SPOOLSS */
1959 resolve_name(servername, &server_ip, 0x20);
1961 nt_status = cli_full_connection(&cli, global_myname(), servername,
1962 &server_ip, 0,
1963 "IPC$", "IPC",
1964 opt_user_name, opt_workgroup,
1965 opt_password ? opt_password : "",
1966 CLI_FULL_CONNECTION_USE_KERBEROS,
1967 Undefined, NULL);
1969 if (NT_STATUS_IS_ERR(nt_status)) {
1970 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1971 "for %s\n", servername, printername);
1972 ads_destroy(&ads);
1973 talloc_destroy(mem_ctx);
1974 return -1;
1977 /* Publish on AD server */
1979 ads_find_machine_acct(ads, &res, servername);
1981 if (ads_count_replies(ads, res) == 0) {
1982 d_fprintf(stderr, "Could not find machine account for server %s\n",
1983 servername);
1984 ads_destroy(&ads);
1985 talloc_destroy(mem_ctx);
1986 return -1;
1989 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1990 srv_cn = ldap_explode_dn(srv_dn, 1);
1992 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1993 printername_escaped = escape_rdn_val_string_alloc(printername);
1994 if (!srv_cn_escaped || !printername_escaped) {
1995 SAFE_FREE(srv_cn_escaped);
1996 SAFE_FREE(printername_escaped);
1997 d_fprintf(stderr, "Internal error, out of memory!");
1998 ads_destroy(&ads);
1999 talloc_destroy(mem_ctx);
2000 return -1;
2003 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
2005 SAFE_FREE(srv_cn_escaped);
2006 SAFE_FREE(printername_escaped);
2008 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
2009 if (!pipe_hnd) {
2010 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
2011 servername);
2012 SAFE_FREE(prt_dn);
2013 ads_destroy(&ads);
2014 talloc_destroy(mem_ctx);
2015 return -1;
2018 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
2019 printername))) {
2020 SAFE_FREE(prt_dn);
2021 ads_destroy(&ads);
2022 talloc_destroy(mem_ctx);
2023 return -1;
2026 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
2027 if (!ADS_ERR_OK(rc)) {
2028 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
2029 SAFE_FREE(prt_dn);
2030 ads_destroy(&ads);
2031 talloc_destroy(mem_ctx);
2032 return -1;
2035 d_printf("published printer\n");
2036 SAFE_FREE(prt_dn);
2037 ads_destroy(&ads);
2038 talloc_destroy(mem_ctx);
2040 return 0;
2043 static int net_ads_printer_remove(int argc, const char **argv)
2045 ADS_STRUCT *ads;
2046 ADS_STATUS rc;
2047 const char *servername;
2048 char *prt_dn;
2049 LDAPMessage *res = NULL;
2051 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2052 return -1;
2055 if (argc < 1) {
2056 return net_ads_printer_usage(argc, argv);
2059 if (argc > 1) {
2060 servername = argv[1];
2061 } else {
2062 servername = global_myname();
2065 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
2067 if (!ADS_ERR_OK(rc)) {
2068 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
2069 ads_msgfree(ads, res);
2070 ads_destroy(&ads);
2071 return -1;
2074 if (ads_count_replies(ads, res) == 0) {
2075 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
2076 ads_msgfree(ads, res);
2077 ads_destroy(&ads);
2078 return -1;
2081 prt_dn = ads_get_dn(ads, res);
2082 ads_msgfree(ads, res);
2083 rc = ads_del_dn(ads, prt_dn);
2084 ads_memfree(ads, prt_dn);
2086 if (!ADS_ERR_OK(rc)) {
2087 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
2088 ads_destroy(&ads);
2089 return -1;
2092 ads_destroy(&ads);
2093 return 0;
2096 static int net_ads_printer(int argc, const char **argv)
2098 struct functable func[] = {
2099 {"SEARCH", net_ads_printer_search},
2100 {"INFO", net_ads_printer_info},
2101 {"PUBLISH", net_ads_printer_publish},
2102 {"REMOVE", net_ads_printer_remove},
2103 {NULL, NULL}
2106 return net_run_function(argc, argv, func, net_ads_printer_usage);
2110 static int net_ads_password(int argc, const char **argv)
2112 ADS_STRUCT *ads;
2113 const char *auth_principal = opt_user_name;
2114 const char *auth_password = opt_password;
2115 char *realm = NULL;
2116 char *new_password = NULL;
2117 char *c, *prompt;
2118 const char *user;
2119 ADS_STATUS ret;
2121 if (opt_user_name == NULL || opt_password == NULL) {
2122 d_fprintf(stderr, "You must supply an administrator username/password\n");
2123 return -1;
2126 if (argc < 1) {
2127 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
2128 return -1;
2131 user = argv[0];
2132 if (!strchr_m(user, '@')) {
2133 asprintf(&c, "%s@%s", argv[0], lp_realm());
2134 user = c;
2137 use_in_memory_ccache();
2138 c = strchr_m(auth_principal, '@');
2139 if (c) {
2140 realm = ++c;
2141 } else {
2142 realm = lp_realm();
2145 /* use the realm so we can eventually change passwords for users
2146 in realms other than default */
2147 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
2148 return -1;
2151 /* we don't actually need a full connect, but it's the easy way to
2152 fill in the KDC's addresss */
2153 ads_connect(ads);
2155 if (!ads || !ads->config.realm) {
2156 d_fprintf(stderr, "Didn't find the kerberos server!\n");
2157 return -1;
2160 if (argv[1]) {
2161 new_password = (char *)argv[1];
2162 } else {
2163 asprintf(&prompt, "Enter new password for %s:", user);
2164 new_password = getpass(prompt);
2165 free(prompt);
2168 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
2169 auth_password, user, new_password, ads->auth.time_offset);
2170 if (!ADS_ERR_OK(ret)) {
2171 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2172 ads_destroy(&ads);
2173 return -1;
2176 d_printf("Password change for %s completed.\n", user);
2177 ads_destroy(&ads);
2179 return 0;
2182 int net_ads_changetrustpw(int argc, const char **argv)
2184 ADS_STRUCT *ads;
2185 char *host_principal;
2186 fstring my_name;
2187 ADS_STATUS ret;
2189 if (!secrets_init()) {
2190 DEBUG(1,("Failed to initialise secrets database\n"));
2191 return -1;
2194 net_use_machine_password();
2196 use_in_memory_ccache();
2198 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2199 return -1;
2202 fstrcpy(my_name, global_myname());
2203 strlower_m(my_name);
2204 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
2205 d_printf("Changing password for principal: %s\n", host_principal);
2207 ret = ads_change_trust_account_password(ads, host_principal);
2209 if (!ADS_ERR_OK(ret)) {
2210 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
2211 ads_destroy(&ads);
2212 SAFE_FREE(host_principal);
2213 return -1;
2216 d_printf("Password change for principal %s succeeded.\n", host_principal);
2218 if (lp_use_kerberos_keytab()) {
2219 d_printf("Attempting to update system keytab with new password.\n");
2220 if (ads_keytab_create_default(ads)) {
2221 d_printf("Failed to update system keytab.\n");
2225 ads_destroy(&ads);
2226 SAFE_FREE(host_principal);
2228 return 0;
2232 help for net ads search
2234 static int net_ads_search_usage(int argc, const char **argv)
2236 d_printf(
2237 "\nnet ads search <expression> <attributes...>\n"\
2238 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2239 "The expression is a standard LDAP search expression, and the\n"\
2240 "attributes are a list of LDAP fields to show in the results\n\n"\
2241 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
2243 net_common_flags_usage(argc, argv);
2244 return -1;
2249 general ADS search function. Useful in diagnosing problems in ADS
2251 static int net_ads_search(int argc, const char **argv)
2253 ADS_STRUCT *ads;
2254 ADS_STATUS rc;
2255 const char *ldap_exp;
2256 const char **attrs;
2257 LDAPMessage *res = NULL;
2259 if (argc < 1) {
2260 return net_ads_search_usage(argc, argv);
2263 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2264 return -1;
2267 ldap_exp = argv[0];
2268 attrs = (argv + 1);
2270 rc = ads_do_search_all(ads, ads->config.bind_path,
2271 LDAP_SCOPE_SUBTREE,
2272 ldap_exp, attrs, &res);
2273 if (!ADS_ERR_OK(rc)) {
2274 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2275 ads_destroy(&ads);
2276 return -1;
2279 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2281 /* dump the results */
2282 ads_dump(ads, res);
2284 ads_msgfree(ads, res);
2285 ads_destroy(&ads);
2287 return 0;
2292 help for net ads search
2294 static int net_ads_dn_usage(int argc, const char **argv)
2296 d_printf(
2297 "\nnet ads dn <dn> <attributes...>\n"\
2298 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2299 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
2300 "to show in the results\n\n"\
2301 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
2302 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
2304 net_common_flags_usage(argc, argv);
2305 return -1;
2310 general ADS search function. Useful in diagnosing problems in ADS
2312 static int net_ads_dn(int argc, const char **argv)
2314 ADS_STRUCT *ads;
2315 ADS_STATUS rc;
2316 const char *dn;
2317 const char **attrs;
2318 LDAPMessage *res = NULL;
2320 if (argc < 1) {
2321 return net_ads_dn_usage(argc, argv);
2324 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2325 return -1;
2328 dn = argv[0];
2329 attrs = (argv + 1);
2331 rc = ads_do_search_all(ads, dn,
2332 LDAP_SCOPE_BASE,
2333 "(objectclass=*)", attrs, &res);
2334 if (!ADS_ERR_OK(rc)) {
2335 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2336 ads_destroy(&ads);
2337 return -1;
2340 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2342 /* dump the results */
2343 ads_dump(ads, res);
2345 ads_msgfree(ads, res);
2346 ads_destroy(&ads);
2348 return 0;
2352 help for net ads sid search
2354 static int net_ads_sid_usage(int argc, const char **argv)
2356 d_printf(
2357 "\nnet ads sid <sid> <attributes...>\n"\
2358 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
2359 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
2360 "to show in the results\n\n"\
2361 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
2363 net_common_flags_usage(argc, argv);
2364 return -1;
2369 general ADS search function. Useful in diagnosing problems in ADS
2371 static int net_ads_sid(int argc, const char **argv)
2373 ADS_STRUCT *ads;
2374 ADS_STATUS rc;
2375 const char *sid_string;
2376 const char **attrs;
2377 LDAPMessage *res = NULL;
2378 DOM_SID sid;
2380 if (argc < 1) {
2381 return net_ads_sid_usage(argc, argv);
2384 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
2385 return -1;
2388 sid_string = argv[0];
2389 attrs = (argv + 1);
2391 if (!string_to_sid(&sid, sid_string)) {
2392 d_fprintf(stderr, "could not convert sid\n");
2393 ads_destroy(&ads);
2394 return -1;
2397 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
2398 if (!ADS_ERR_OK(rc)) {
2399 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
2400 ads_destroy(&ads);
2401 return -1;
2404 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
2406 /* dump the results */
2407 ads_dump(ads, res);
2409 ads_msgfree(ads, res);
2410 ads_destroy(&ads);
2412 return 0;
2416 static int net_ads_keytab_usage(int argc, const char **argv)
2418 d_printf(
2419 "net ads keytab <COMMAND>\n"\
2420 "<COMMAND> can be either:\n"\
2421 " ADD Adds new service principal\n"\
2422 " CREATE Creates a fresh keytab\n"\
2423 " FLUSH Flushes out all keytab entries\n"\
2424 " HELP Prints this help message\n"\
2425 " LIST List the keytab\n"\
2426 "The ADD command will take arguments, the other commands\n"\
2427 "will not take any arguments. The arguments given to ADD\n"\
2428 "should be a list of principals to add. For example, \n"\
2429 " net ads keytab add srv1 srv2\n"\
2430 "will add principals for the services srv1 and srv2 to the\n"\
2431 "system's keytab.\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 return ads_keytab_list();
2486 int net_ads_keytab(int argc, const char **argv)
2488 struct functable func[] = {
2489 {"CREATE", net_ads_keytab_create},
2490 {"ADD", net_ads_keytab_add},
2491 {"FLUSH", net_ads_keytab_flush},
2492 {"HELP", net_ads_keytab_usage},
2493 {"LIST", net_ads_keytab_list},
2494 {NULL, NULL}
2497 if (!lp_use_kerberos_keytab()) {
2498 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2499 use keytab functions.\n");
2502 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2505 int net_ads_help(int argc, const char **argv)
2507 struct functable func[] = {
2508 {"USER", net_ads_user_usage},
2509 {"GROUP", net_ads_group_usage},
2510 {"PRINTER", net_ads_printer_usage},
2511 {"SEARCH", net_ads_search_usage},
2512 {"INFO", net_ads_info},
2513 {"JOIN", net_ads_join_usage},
2514 {"DNS", net_ads_dns_usage},
2515 {"LEAVE", net_ads_leave},
2516 {"STATUS", net_ads_status},
2517 {"PASSWORD", net_ads_password},
2518 {"CHANGETRUSTPW", net_ads_changetrustpw},
2519 {NULL, NULL}
2522 return net_run_function(argc, argv, func, net_ads_usage);
2525 int net_ads(int argc, const char **argv)
2527 struct functable func[] = {
2528 {"INFO", net_ads_info},
2529 {"JOIN", net_ads_join},
2530 {"TESTJOIN", net_ads_testjoin},
2531 {"LEAVE", net_ads_leave},
2532 {"STATUS", net_ads_status},
2533 {"USER", net_ads_user},
2534 {"GROUP", net_ads_group},
2535 {"DNS", net_ads_dns},
2536 {"PASSWORD", net_ads_password},
2537 {"CHANGETRUSTPW", net_ads_changetrustpw},
2538 {"PRINTER", net_ads_printer},
2539 {"SEARCH", net_ads_search},
2540 {"DN", net_ads_dn},
2541 {"SID", net_ads_sid},
2542 {"WORKGROUP", net_ads_workgroup},
2543 {"LOOKUP", net_ads_lookup},
2544 {"KEYTAB", net_ads_keytab},
2545 {"GPO", net_ads_gpo},
2546 {"HELP", net_ads_help},
2547 {NULL, NULL}
2550 return net_run_function(argc, argv, func, net_ads_usage);
2553 #else
2555 static int net_ads_noads(void)
2557 d_fprintf(stderr, "ADS support not compiled in\n");
2558 return -1;
2561 int net_ads_keytab(int argc, const char **argv)
2563 return net_ads_noads();
2566 int net_ads_usage(int argc, const char **argv)
2568 return net_ads_noads();
2571 int net_ads_help(int argc, const char **argv)
2573 return net_ads_noads();
2576 int net_ads_changetrustpw(int argc, const char **argv)
2578 return net_ads_noads();
2581 int net_ads_join(int argc, const char **argv)
2583 return net_ads_noads();
2586 int net_ads_user(int argc, const char **argv)
2588 return net_ads_noads();
2591 int net_ads_group(int argc, const char **argv)
2593 return net_ads_noads();
2596 /* this one shouldn't display a message */
2597 int net_ads_check(void)
2599 return -1;
2602 int net_ads_check_our_domain(void)
2604 return -1;
2607 int net_ads(int argc, const char **argv)
2609 return net_ads_usage(argc, argv);
2612 #endif /* WITH_ADS */