libads: Use libnbt for CLDAP reply parsing.
[Samba.git] / source / utils / net_ads.c
blobaf55430fac0b8378a8f6439022b4aa33f4014b14
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 #include "libnet/libnet.h"
28 #ifdef HAVE_ADS
30 int net_ads_usage(int argc, const char **argv)
32 d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
33 d_printf(" Join the local machine to a ADS realm\n");
34 d_printf("leave\n");
35 d_printf(" Remove the local machine from a ADS realm\n");
36 d_printf("testjoin\n");
37 d_printf(" Validates the machine account in the domain\n");
38 d_printf("user\n");
39 d_printf(" List, add, or delete users in the realm\n");
40 d_printf("group\n");
41 d_printf(" List, add, or delete groups in the realm\n");
42 d_printf("info\n");
43 d_printf(" Displays details regarding a specific AD server\n");
44 d_printf("status\n");
45 d_printf(" Display details regarding the machine's account in AD\n");
46 d_printf("lookup\n");
47 d_printf(" Performs CLDAP query of AD domain controllers\n");
48 d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
49 d_printf(" Change a user's password using an admin account\n");
50 d_printf(" (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
51 d_printf("changetrustpw\n");
52 d_printf(" Change the trust account password of this machine in the AD tree\n");
53 d_printf("printer [info | publish | remove] <printername> <servername>\n");
54 d_printf(" Lookup, add, or remove directory entry for a printer\n");
55 d_printf("{search,dn,sid}\n");
56 d_printf(" Issue LDAP search queries using a general filter, by DN, or by SID\n");
57 d_printf("keytab\n");
58 d_printf(" Manage a local keytab file based on the machine account in AD\n");
59 d_printf("dns\n");
60 d_printf(" Issue a dynamic DNS update request the server's hostname\n");
61 d_printf(" (using the machine credentials)\n");
63 return -1;
66 /* when we do not have sufficient input parameters to contact a remote domain
67 * we always fall back to our own realm - Guenther*/
69 static const char *assume_own_realm(void)
71 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
72 return lp_realm();
75 return NULL;
79 do a cldap netlogon query
81 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
83 char addr[INET6_ADDRSTRLEN];
84 struct nbt_cldap_netlogon_5 reply;
86 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
87 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
88 d_fprintf(stderr, "CLDAP query failed!\n");
89 return -1;
92 d_printf("Information for Domain Controller: %s\n\n",
93 addr);
95 d_printf("Response Type: ");
96 switch (reply.type) {
97 case SAMLOGON_AD_UNK_R:
98 d_printf("SAMLOGON\n");
99 break;
100 case SAMLOGON_AD_R:
101 d_printf("SAMLOGON_USER\n");
102 break;
103 default:
104 d_printf("0x%x\n", reply.type);
105 break;
108 d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), reply.domain_uuid));
110 d_printf("Flags:\n"
111 "\tIs a PDC: %s\n"
112 "\tIs a GC of the forest: %s\n"
113 "\tIs an LDAP server: %s\n"
114 "\tSupports DS: %s\n"
115 "\tIs running a KDC: %s\n"
116 "\tIs running time services: %s\n"
117 "\tIs the closest DC: %s\n"
118 "\tIs writable: %s\n"
119 "\tHas a hardware clock: %s\n"
120 "\tIs a non-domain NC serviced by LDAP server: %s\n",
121 (reply.server_type & ADS_PDC) ? "yes" : "no",
122 (reply.server_type & ADS_GC) ? "yes" : "no",
123 (reply.server_type & ADS_LDAP) ? "yes" : "no",
124 (reply.server_type & ADS_DS) ? "yes" : "no",
125 (reply.server_type & ADS_KDC) ? "yes" : "no",
126 (reply.server_type & ADS_TIMESERV) ? "yes" : "no",
127 (reply.server_type & ADS_CLOSEST) ? "yes" : "no",
128 (reply.server_type & ADS_WRITABLE) ? "yes" : "no",
129 (reply.server_type & ADS_GOOD_TIMESERV) ? "yes" : "no",
130 (reply.server_type & ADS_NDNC) ? "yes" : "no");
132 printf("Forest:\t\t\t%s\n", reply.forest);
133 printf("Domain:\t\t\t%s\n", reply.dns_domain);
134 printf("Domain Controller:\t%s\n", reply.pdc_dns_name);
136 printf("Pre-Win2k Domain:\t%s\n", reply.domain);
137 printf("Pre-Win2k Hostname:\t%s\n", reply.pdc_name);
139 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
141 printf("Server Site Name :\t\t%s\n", reply.server_site);
142 printf("Client Site Name :\t\t%s\n", reply.client_site);
144 d_printf("NT Version: %d\n", reply.nt_version);
145 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
146 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
148 return 0;
152 this implements the CLDAP based netlogon lookup requests
153 for finding the domain controller of a ADS domain
155 static int net_ads_lookup(int argc, const char **argv)
157 ADS_STRUCT *ads;
159 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
160 d_fprintf(stderr, "Didn't find the cldap server!\n");
161 return -1;
164 if (!ads->config.realm) {
165 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
166 ads->ldap.port = 389;
169 return net_ads_cldap_netlogon(ads);
174 static int net_ads_info(int argc, const char **argv)
176 ADS_STRUCT *ads;
177 char addr[INET6_ADDRSTRLEN];
179 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
180 d_fprintf(stderr, "Didn't find the ldap server!\n");
181 return -1;
184 if (!ads || !ads->config.realm) {
185 d_fprintf(stderr, "Didn't find the ldap server!\n");
186 return -1;
189 /* Try to set the server's current time since we didn't do a full
190 TCP LDAP session initially */
192 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
193 d_fprintf( stderr, "Failed to get server's current time!\n");
196 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
198 d_printf("LDAP server: %s\n", addr);
199 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
200 d_printf("Realm: %s\n", ads->config.realm);
201 d_printf("Bind Path: %s\n", ads->config.bind_path);
202 d_printf("LDAP port: %d\n", ads->ldap.port);
203 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
205 d_printf("KDC server: %s\n", ads->auth.kdc_server );
206 d_printf("Server time offset: %d\n", ads->auth.time_offset );
208 return 0;
211 static void use_in_memory_ccache(void) {
212 /* Use in-memory credentials cache so we do not interfere with
213 * existing credentials */
214 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
217 static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
219 ADS_STRUCT *ads = NULL;
220 ADS_STATUS status;
221 bool need_password = False;
222 bool second_time = False;
223 char *cp;
224 const char *realm = NULL;
225 bool tried_closest_dc = False;
227 /* lp_realm() should be handled by a command line param,
228 However, the join requires that realm be set in smb.conf
229 and compares our realm with the remote server's so this is
230 ok until someone needs more flexibility */
232 *ads_ret = NULL;
234 retry_connect:
235 if (only_own_domain) {
236 realm = lp_realm();
237 } else {
238 realm = assume_own_realm();
241 ads = ads_init(realm, opt_target_workgroup, opt_host);
243 if (!opt_user_name) {
244 opt_user_name = "administrator";
247 if (opt_user_specified) {
248 need_password = True;
251 retry:
252 if (!opt_password && need_password && !opt_machine_pass) {
253 opt_password = net_prompt_pass(opt_user_name);
254 if (!opt_password) {
255 ads_destroy(&ads);
256 return ADS_ERROR(LDAP_NO_MEMORY);
260 if (opt_password) {
261 use_in_memory_ccache();
262 SAFE_FREE(ads->auth.password);
263 ads->auth.password = smb_xstrdup(opt_password);
266 ads->auth.flags |= auth_flags;
267 SAFE_FREE(ads->auth.user_name);
268 ads->auth.user_name = smb_xstrdup(opt_user_name);
271 * If the username is of the form "name@realm",
272 * extract the realm and convert to upper case.
273 * This is only used to establish the connection.
275 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
276 *cp++ = '\0';
277 SAFE_FREE(ads->auth.realm);
278 ads->auth.realm = smb_xstrdup(cp);
279 strupper_m(ads->auth.realm);
282 status = ads_connect(ads);
284 if (!ADS_ERR_OK(status)) {
286 if (NT_STATUS_EQUAL(ads_ntstatus(status),
287 NT_STATUS_NO_LOGON_SERVERS)) {
288 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
289 ads_destroy(&ads);
290 return status;
293 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
294 need_password = True;
295 second_time = True;
296 goto retry;
297 } else {
298 ads_destroy(&ads);
299 return status;
303 /* when contacting our own domain, make sure we use the closest DC.
304 * This is done by reconnecting to ADS because only the first call to
305 * ads_connect will give us our own sitename */
307 if ((only_own_domain || !opt_host) && !tried_closest_dc) {
309 tried_closest_dc = True; /* avoid loop */
311 if (!ads->config.tried_closest_dc) {
313 namecache_delete(ads->server.realm, 0x1C);
314 namecache_delete(ads->server.workgroup, 0x1C);
316 ads_destroy(&ads);
317 ads = NULL;
319 goto retry_connect;
323 *ads_ret = ads;
324 return status;
327 ADS_STATUS ads_startup(bool only_own_domain, ADS_STRUCT **ads)
329 return ads_startup_int(only_own_domain, 0, ads);
332 ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
334 return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
338 Check to see if connection can be made via ads.
339 ads_startup() stores the password in opt_password if it needs to so
340 that rpc or rap can use it without re-prompting.
342 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
344 ADS_STRUCT *ads;
345 ADS_STATUS status;
347 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
348 return -1;
351 ads->auth.flags |= ADS_AUTH_NO_BIND;
353 status = ads_connect(ads);
354 if ( !ADS_ERR_OK(status) ) {
355 return -1;
358 ads_destroy(&ads);
359 return 0;
362 int net_ads_check_our_domain(void)
364 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
367 int net_ads_check(void)
369 return net_ads_check_int(NULL, opt_workgroup, opt_host);
373 determine the netbios workgroup name for a domain
375 static int net_ads_workgroup(int argc, const char **argv)
377 ADS_STRUCT *ads;
378 char addr[INET6_ADDRSTRLEN];
379 struct nbt_cldap_netlogon_5 reply;
381 if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
382 d_fprintf(stderr, "Didn't find the cldap server!\n");
383 return -1;
386 if (!ads->config.realm) {
387 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
388 ads->ldap.port = 389;
391 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
392 if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
393 d_fprintf(stderr, "CLDAP query failed!\n");
394 return -1;
397 d_printf("Workgroup: %s\n", reply.domain);
399 ads_destroy(&ads);
401 return 0;
406 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
408 char **disp_fields = (char **) data_area;
410 if (!field) { /* must be end of record */
411 if (disp_fields[0]) {
412 if (!strchr_m(disp_fields[0], '$')) {
413 if (disp_fields[1])
414 d_printf("%-21.21s %s\n",
415 disp_fields[0], disp_fields[1]);
416 else
417 d_printf("%s\n", disp_fields[0]);
420 SAFE_FREE(disp_fields[0]);
421 SAFE_FREE(disp_fields[1]);
422 return True;
424 if (!values) /* must be new field, indicate string field */
425 return True;
426 if (StrCaseCmp(field, "sAMAccountName") == 0) {
427 disp_fields[0] = SMB_STRDUP((char *) values[0]);
429 if (StrCaseCmp(field, "description") == 0)
430 disp_fields[1] = SMB_STRDUP((char *) values[0]);
431 return True;
434 static int net_ads_user_usage(int argc, const char **argv)
436 return net_help_user(argc, argv);
439 static int ads_user_add(int argc, const char **argv)
441 ADS_STRUCT *ads;
442 ADS_STATUS status;
443 char *upn, *userdn;
444 LDAPMessage *res=NULL;
445 int rc = -1;
446 char *ou_str = NULL;
448 if (argc < 1) return net_ads_user_usage(argc, argv);
450 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
451 return -1;
454 status = ads_find_user_acct(ads, &res, argv[0]);
456 if (!ADS_ERR_OK(status)) {
457 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
458 goto done;
461 if (ads_count_replies(ads, res)) {
462 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
463 goto done;
466 if (opt_container) {
467 ou_str = SMB_STRDUP(opt_container);
468 } else {
469 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
472 status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
474 if (!ADS_ERR_OK(status)) {
475 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
476 ads_errstr(status));
477 goto done;
480 /* if no password is to be set, we're done */
481 if (argc == 1) {
482 d_printf("User %s added\n", argv[0]);
483 rc = 0;
484 goto done;
487 /* try setting the password */
488 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
489 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
490 ads->auth.time_offset);
491 safe_free(upn);
492 if (ADS_ERR_OK(status)) {
493 d_printf("User %s added\n", argv[0]);
494 rc = 0;
495 goto done;
498 /* password didn't set, delete account */
499 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
500 argv[0], ads_errstr(status));
501 ads_msgfree(ads, res);
502 status=ads_find_user_acct(ads, &res, argv[0]);
503 if (ADS_ERR_OK(status)) {
504 userdn = ads_get_dn(ads, res);
505 ads_del_dn(ads, userdn);
506 ads_memfree(ads, userdn);
509 done:
510 if (res)
511 ads_msgfree(ads, res);
512 ads_destroy(&ads);
513 SAFE_FREE(ou_str);
514 return rc;
517 static int ads_user_info(int argc, const char **argv)
519 ADS_STRUCT *ads;
520 ADS_STATUS rc;
521 LDAPMessage *res;
522 const char *attrs[] = {"memberOf", NULL};
523 char *searchstring=NULL;
524 char **grouplist;
525 char *escaped_user;
527 if (argc < 1) {
528 return net_ads_user_usage(argc, argv);
531 escaped_user = escape_ldap_string_alloc(argv[0]);
533 if (!escaped_user) {
534 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
535 return -1;
538 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
539 SAFE_FREE(escaped_user);
540 return -1;
543 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
544 rc = ads_search(ads, &res, searchstring, attrs);
545 safe_free(searchstring);
547 if (!ADS_ERR_OK(rc)) {
548 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
549 ads_destroy(&ads);
550 SAFE_FREE(escaped_user);
551 return -1;
554 grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
555 (LDAPMessage *)res, "memberOf");
557 if (grouplist) {
558 int i;
559 char **groupname;
560 for (i=0;grouplist[i];i++) {
561 groupname = ldap_explode_dn(grouplist[i], 1);
562 d_printf("%s\n", groupname[0]);
563 ldap_value_free(groupname);
565 ldap_value_free(grouplist);
568 ads_msgfree(ads, res);
569 ads_destroy(&ads);
570 SAFE_FREE(escaped_user);
571 return 0;
574 static int ads_user_delete(int argc, const char **argv)
576 ADS_STRUCT *ads;
577 ADS_STATUS rc;
578 LDAPMessage *res = NULL;
579 char *userdn;
581 if (argc < 1) {
582 return net_ads_user_usage(argc, argv);
585 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
586 return -1;
589 rc = ads_find_user_acct(ads, &res, argv[0]);
590 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
591 d_printf("User %s does not exist.\n", argv[0]);
592 ads_msgfree(ads, res);
593 ads_destroy(&ads);
594 return -1;
596 userdn = ads_get_dn(ads, res);
597 ads_msgfree(ads, res);
598 rc = ads_del_dn(ads, userdn);
599 ads_memfree(ads, userdn);
600 if (ADS_ERR_OK(rc)) {
601 d_printf("User %s deleted\n", argv[0]);
602 ads_destroy(&ads);
603 return 0;
605 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
606 ads_errstr(rc));
607 ads_destroy(&ads);
608 return -1;
611 int net_ads_user(int argc, const char **argv)
613 struct functable func[] = {
614 {"ADD", ads_user_add},
615 {"INFO", ads_user_info},
616 {"DELETE", ads_user_delete},
617 {NULL, NULL}
619 ADS_STRUCT *ads;
620 ADS_STATUS rc;
621 const char *shortattrs[] = {"sAMAccountName", NULL};
622 const char *longattrs[] = {"sAMAccountName", "description", NULL};
623 char *disp_fields[2] = {NULL, NULL};
625 if (argc == 0) {
626 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
627 return -1;
630 if (opt_long_list_entries)
631 d_printf("\nUser name Comment"\
632 "\n-----------------------------\n");
634 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
635 LDAP_SCOPE_SUBTREE,
636 "(objectCategory=user)",
637 opt_long_list_entries ? longattrs :
638 shortattrs, usergrp_display,
639 disp_fields);
640 ads_destroy(&ads);
641 return ADS_ERR_OK(rc) ? 0 : -1;
644 return net_run_function(argc, argv, func, net_ads_user_usage);
647 static int net_ads_group_usage(int argc, const char **argv)
649 return net_help_group(argc, argv);
652 static int ads_group_add(int argc, const char **argv)
654 ADS_STRUCT *ads;
655 ADS_STATUS status;
656 LDAPMessage *res=NULL;
657 int rc = -1;
658 char *ou_str = NULL;
660 if (argc < 1) {
661 return net_ads_group_usage(argc, argv);
664 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
665 return -1;
668 status = ads_find_user_acct(ads, &res, argv[0]);
670 if (!ADS_ERR_OK(status)) {
671 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
672 goto done;
675 if (ads_count_replies(ads, res)) {
676 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
677 goto done;
680 if (opt_container) {
681 ou_str = SMB_STRDUP(opt_container);
682 } else {
683 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
686 status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
688 if (ADS_ERR_OK(status)) {
689 d_printf("Group %s added\n", argv[0]);
690 rc = 0;
691 } else {
692 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
693 ads_errstr(status));
696 done:
697 if (res)
698 ads_msgfree(ads, res);
699 ads_destroy(&ads);
700 SAFE_FREE(ou_str);
701 return rc;
704 static int ads_group_delete(int argc, const char **argv)
706 ADS_STRUCT *ads;
707 ADS_STATUS rc;
708 LDAPMessage *res = NULL;
709 char *groupdn;
711 if (argc < 1) {
712 return net_ads_group_usage(argc, argv);
715 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
716 return -1;
719 rc = ads_find_user_acct(ads, &res, argv[0]);
720 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
721 d_printf("Group %s does not exist.\n", argv[0]);
722 ads_msgfree(ads, res);
723 ads_destroy(&ads);
724 return -1;
726 groupdn = ads_get_dn(ads, res);
727 ads_msgfree(ads, res);
728 rc = ads_del_dn(ads, groupdn);
729 ads_memfree(ads, groupdn);
730 if (ADS_ERR_OK(rc)) {
731 d_printf("Group %s deleted\n", argv[0]);
732 ads_destroy(&ads);
733 return 0;
735 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
736 ads_errstr(rc));
737 ads_destroy(&ads);
738 return -1;
741 int net_ads_group(int argc, const char **argv)
743 struct functable func[] = {
744 {"ADD", ads_group_add},
745 {"DELETE", ads_group_delete},
746 {NULL, NULL}
748 ADS_STRUCT *ads;
749 ADS_STATUS rc;
750 const char *shortattrs[] = {"sAMAccountName", NULL};
751 const char *longattrs[] = {"sAMAccountName", "description", NULL};
752 char *disp_fields[2] = {NULL, NULL};
754 if (argc == 0) {
755 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
756 return -1;
759 if (opt_long_list_entries)
760 d_printf("\nGroup name Comment"\
761 "\n-----------------------------\n");
762 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
763 LDAP_SCOPE_SUBTREE,
764 "(objectCategory=group)",
765 opt_long_list_entries ? longattrs :
766 shortattrs, usergrp_display,
767 disp_fields);
769 ads_destroy(&ads);
770 return ADS_ERR_OK(rc) ? 0 : -1;
772 return net_run_function(argc, argv, func, net_ads_group_usage);
775 static int net_ads_status(int argc, const char **argv)
777 ADS_STRUCT *ads;
778 ADS_STATUS rc;
779 LDAPMessage *res;
781 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
782 return -1;
785 rc = ads_find_machine_acct(ads, &res, global_myname());
786 if (!ADS_ERR_OK(rc)) {
787 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
788 ads_destroy(&ads);
789 return -1;
792 if (ads_count_replies(ads, res) == 0) {
793 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
794 ads_destroy(&ads);
795 return -1;
798 ads_dump(ads, res);
799 ads_destroy(&ads);
800 return 0;
803 /*******************************************************************
804 Leave an AD domain. Windows XP disables the machine account.
805 We'll try the same. The old code would do an LDAP delete.
806 That only worked using the machine creds because added the machine
807 with full control to the computer object's ACL.
808 *******************************************************************/
810 static int net_ads_leave(int argc, const char **argv)
812 TALLOC_CTX *ctx;
813 struct libnet_UnjoinCtx *r = NULL;
814 WERROR werr;
816 if (!*lp_realm()) {
817 d_fprintf(stderr, "No realm set, are we joined ?\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 use_in_memory_ccache();
828 werr = libnet_init_UnjoinCtx(ctx, &r);
829 if (!W_ERROR_IS_OK(werr)) {
830 d_fprintf(stderr, "Could not initialise unjoin context.\n");
831 return -1;
834 r->in.debug = true;
835 r->in.dc_name = opt_host;
836 r->in.domain_name = lp_realm();
837 r->in.admin_account = opt_user_name;
838 r->in.admin_password = net_prompt_pass(opt_user_name);
839 r->in.modify_config = lp_config_backend_is_registry();
840 r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
841 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
843 werr = libnet_Unjoin(ctx, r);
844 if (!W_ERROR_IS_OK(werr)) {
845 d_printf("Failed to leave domain: %s\n",
846 r->out.error_string ? r->out.error_string :
847 get_friendly_werror_msg(werr));
848 goto done;
851 if (W_ERROR_IS_OK(werr)) {
852 d_printf("Deleted account for '%s' in realm '%s'\n",
853 r->in.machine_name, r->out.dns_domain_name);
854 goto done;
857 /* We couldn't delete it - see if the disable succeeded. */
858 if (r->out.disabled_machine_account) {
859 d_printf("Disabled account for '%s' in realm '%s'\n",
860 r->in.machine_name, r->out.dns_domain_name);
861 werr = WERR_OK;
862 goto done;
865 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
866 r->in.machine_name, r->out.dns_domain_name);
868 done:
869 TALLOC_FREE(r);
870 TALLOC_FREE(ctx);
872 if (W_ERROR_IS_OK(werr)) {
873 return 0;
876 return -1;
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_krb_machine_account();
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 WERROR 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 WERR_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 WERR_INVALID_COMPUTER_NAME;
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", get_dyn_CONFIGFILE());
941 return WERR_INVALID_PARAM;
944 return WERR_OK;
947 /*******************************************************************
948 Send a DNS update request
949 *******************************************************************/
951 #if defined(WITH_DNS_UPDATES)
952 #include "dns.h"
953 DNS_ERROR DoDNSUpdate(char *pszServerName,
954 const char *pszDomainName, const char *pszHostName,
955 const struct sockaddr_storage *sslist,
956 size_t num_addrs );
958 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
959 const char *machine_name,
960 const struct sockaddr_storage *addrs,
961 int num_addrs)
963 struct dns_rr_ns *nameservers = NULL;
964 int ns_count = 0;
965 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
966 DNS_ERROR dns_err;
967 fstring dns_server;
968 const char *dnsdomain = NULL;
969 char *root_domain = NULL;
971 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
972 d_printf("No DNS domain configured for %s. "
973 "Unable to perform DNS Update.\n", machine_name);
974 status = NT_STATUS_INVALID_PARAMETER;
975 goto done;
977 dnsdomain++;
979 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
980 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
981 /* Child domains often do not have NS records. Look
982 for the NS record for the forest root domain
983 (rootDomainNamingContext in therootDSE) */
985 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
986 LDAPMessage *msg = NULL;
987 char *root_dn;
988 ADS_STATUS ads_status;
990 if ( !ads->ldap.ld ) {
991 ads_status = ads_connect( ads );
992 if ( !ADS_ERR_OK(ads_status) ) {
993 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
994 goto done;
998 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
999 "(objectclass=*)", rootname_attrs, &msg);
1000 if (!ADS_ERR_OK(ads_status)) {
1001 goto done;
1004 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1005 if ( !root_dn ) {
1006 ads_msgfree( ads, msg );
1007 goto done;
1010 root_domain = ads_build_domain( root_dn );
1012 /* cleanup */
1013 ads_msgfree( ads, msg );
1015 /* try again for NS servers */
1017 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1019 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1020 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1021 "realm\n", ads->config.realm));
1022 goto done;
1025 dnsdomain = root_domain;
1029 /* Now perform the dns update - we'll try non-secure and if we fail,
1030 we'll follow it up with a secure update */
1032 fstrcpy( dns_server, nameservers[0].hostname );
1034 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1035 if (!ERR_DNS_IS_OK(dns_err)) {
1036 status = NT_STATUS_UNSUCCESSFUL;
1039 done:
1041 SAFE_FREE( root_domain );
1043 return status;
1046 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1048 int num_addrs;
1049 struct sockaddr_storage *iplist = NULL;
1050 fstring machine_name;
1051 NTSTATUS status;
1053 name_to_fqdn( machine_name, global_myname() );
1054 strlower_m( machine_name );
1056 /* Get our ip address (not the 127.0.0.x address but a real ip
1057 * address) */
1059 num_addrs = get_my_ip_address( &iplist );
1060 if ( num_addrs <= 0 ) {
1061 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1062 "addresses!\n"));
1063 return NT_STATUS_INVALID_PARAMETER;
1066 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1067 iplist, num_addrs);
1068 SAFE_FREE( iplist );
1069 return status;
1071 #endif
1074 /*******************************************************************
1075 ********************************************************************/
1077 static int net_ads_join_usage(int argc, const char **argv)
1079 d_printf("net ads join [options]\n");
1080 d_printf("Valid options:\n");
1081 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1082 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1083 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1084 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1085 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1086 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1087 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1088 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1089 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1090 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1091 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1092 d_printf(" the two other attributes.\n");
1094 return -1;
1097 /*******************************************************************
1098 ********************************************************************/
1100 int net_ads_join(int argc, const char **argv)
1102 TALLOC_CTX *ctx = NULL;
1103 struct libnet_JoinCtx *r = NULL;
1104 const char *domain = lp_realm();
1105 WERROR werr = WERR_SETUP_NOT_JOINED;
1106 bool createupn = False;
1107 const char *machineupn = NULL;
1108 const char *create_in_ou = NULL;
1109 int i;
1110 const char *os_name = NULL;
1111 const char *os_version = NULL;
1112 bool modify_config = lp_config_backend_is_registry();
1114 if (!modify_config) {
1116 werr = check_ads_config();
1117 if (!W_ERROR_IS_OK(werr)) {
1118 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1119 goto fail;
1123 if (!(ctx = talloc_init("net_ads_join"))) {
1124 d_fprintf(stderr, "Could not initialise talloc context.\n");
1125 werr = WERR_NOMEM;
1126 goto fail;
1129 use_in_memory_ccache();
1131 werr = libnet_init_JoinCtx(ctx, &r);
1132 if (!W_ERROR_IS_OK(werr)) {
1133 goto fail;
1136 /* process additional command line args */
1138 for ( i=0; i<argc; i++ ) {
1139 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1140 createupn = True;
1141 machineupn = get_string_param(argv[i]);
1143 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1144 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1145 d_fprintf(stderr, "Please supply a valid OU path.\n");
1146 werr = WERR_INVALID_PARAM;
1147 goto fail;
1150 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1151 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1152 d_fprintf(stderr, "Please supply a operating system name.\n");
1153 werr = WERR_INVALID_PARAM;
1154 goto fail;
1157 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1158 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1159 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1160 werr = WERR_INVALID_PARAM;
1161 goto fail;
1164 else {
1165 domain = argv[i];
1169 if (!*domain) {
1170 d_fprintf(stderr, "Please supply a valid domain name\n");
1171 werr = WERR_INVALID_PARAM;
1172 goto fail;
1175 /* Do the domain join here */
1177 r->in.domain_name = domain;
1178 r->in.create_upn = createupn;
1179 r->in.upn = machineupn;
1180 r->in.account_ou = create_in_ou;
1181 r->in.os_name = os_name;
1182 r->in.os_version = os_version;
1183 r->in.dc_name = opt_host;
1184 r->in.admin_account = opt_user_name;
1185 r->in.admin_password = net_prompt_pass(opt_user_name);
1186 r->in.debug = true;
1187 r->in.modify_config = modify_config;
1188 r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
1189 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
1190 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
1192 werr = libnet_Join(ctx, r);
1193 if (!W_ERROR_IS_OK(werr)) {
1194 goto fail;
1197 /* Check the short name of the domain */
1199 if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
1200 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1201 d_printf("domain name obtained from the server.\n");
1202 d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
1203 d_printf("You should set \"workgroup = %s\" in %s.\n",
1204 r->out.netbios_domain_name, get_dyn_CONFIGFILE());
1207 d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
1209 if (r->out.dns_domain_name) {
1210 d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
1211 r->out.dns_domain_name);
1212 } else {
1213 d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
1214 r->out.netbios_domain_name);
1217 #if defined(WITH_DNS_UPDATES)
1218 if (r->out.domain_is_ad) {
1219 /* We enter this block with user creds */
1220 ADS_STRUCT *ads_dns = NULL;
1222 if ( (ads_dns = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1223 /* kinit with the machine password */
1225 use_in_memory_ccache();
1226 asprintf( &ads_dns->auth.user_name, "%s$", global_myname() );
1227 ads_dns->auth.password = secrets_fetch_machine_password(
1228 r->out.netbios_domain_name, NULL, NULL );
1229 ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
1230 ads_kinit_password( ads_dns );
1233 if ( !ads_dns || !NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns )) ) {
1234 d_fprintf( stderr, "DNS update failed!\n" );
1237 /* exit from this block using machine creds */
1238 ads_destroy(&ads_dns);
1240 #endif
1241 TALLOC_FREE(r);
1242 TALLOC_FREE( ctx );
1244 return 0;
1246 fail:
1247 /* issue an overall failure message at the end. */
1248 d_printf("Failed to join domain: %s\n",
1249 r && r->out.error_string ? r->out.error_string :
1250 get_friendly_werror_msg(werr));
1251 TALLOC_FREE( ctx );
1253 return -1;
1256 /*******************************************************************
1257 ********************************************************************/
1259 static int net_ads_dns_usage(int argc, const char **argv)
1261 #if defined(WITH_DNS_UPDATES)
1262 d_printf("net ads dns <command>\n");
1263 d_printf("Valid commands:\n");
1264 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1266 return 0;
1267 #else
1268 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1269 return -1;
1270 #endif
1273 /*******************************************************************
1274 ********************************************************************/
1276 static int net_ads_dns_register(int argc, const char **argv)
1278 #if defined(WITH_DNS_UPDATES)
1279 ADS_STRUCT *ads;
1280 ADS_STATUS status;
1281 TALLOC_CTX *ctx;
1283 #ifdef DEVELOPER
1284 talloc_enable_leak_report();
1285 #endif
1287 if (argc > 0) {
1288 d_fprintf(stderr, "net ads dns register\n");
1289 return -1;
1292 if (!(ctx = talloc_init("net_ads_dns"))) {
1293 d_fprintf(stderr, "Could not initialise talloc context\n");
1294 return -1;
1297 status = ads_startup(True, &ads);
1298 if ( !ADS_ERR_OK(status) ) {
1299 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1300 TALLOC_FREE(ctx);
1301 return -1;
1304 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1305 d_fprintf( stderr, "DNS update failed!\n" );
1306 ads_destroy( &ads );
1307 TALLOC_FREE( ctx );
1308 return -1;
1311 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1313 ads_destroy(&ads);
1314 TALLOC_FREE( ctx );
1316 return 0;
1317 #else
1318 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1319 return -1;
1320 #endif
1323 #if defined(WITH_DNS_UPDATES)
1324 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1325 #endif
1327 static int net_ads_dns_gethostbyname(int argc, const char **argv)
1329 #if defined(WITH_DNS_UPDATES)
1330 DNS_ERROR err;
1332 #ifdef DEVELOPER
1333 talloc_enable_leak_report();
1334 #endif
1336 if (argc != 2) {
1337 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1338 "<name>\n");
1339 return -1;
1342 err = do_gethostbyname(argv[0], argv[1]);
1344 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1345 #endif
1346 return 0;
1349 static int net_ads_dns(int argc, const char *argv[])
1351 struct functable func[] = {
1352 {"REGISTER", net_ads_dns_register},
1353 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1354 {NULL, NULL}
1357 return net_run_function(argc, argv, func, net_ads_dns_usage);
1360 /*******************************************************************
1361 ********************************************************************/
1363 int net_ads_printer_usage(int argc, const char **argv)
1365 d_printf(
1366 "\nnet ads printer search <printer>"
1367 "\n\tsearch for a printer in the directory\n"
1368 "\nnet ads printer info <printer> <server>"
1369 "\n\tlookup info in directory for printer on server"
1370 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1371 "\nnet ads printer publish <printername>"
1372 "\n\tpublish printer in directory"
1373 "\n\t(note: printer name is required)\n"
1374 "\nnet ads printer remove <printername>"
1375 "\n\tremove printer from directory"
1376 "\n\t(note: printer name is required)\n");
1377 return -1;
1380 /*******************************************************************
1381 ********************************************************************/
1383 static int net_ads_printer_search(int argc, const char **argv)
1385 ADS_STRUCT *ads;
1386 ADS_STATUS rc;
1387 LDAPMessage *res = NULL;
1389 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1390 return -1;
1393 rc = ads_find_printers(ads, &res);
1395 if (!ADS_ERR_OK(rc)) {
1396 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1397 ads_msgfree(ads, res);
1398 ads_destroy(&ads);
1399 return -1;
1402 if (ads_count_replies(ads, res) == 0) {
1403 d_fprintf(stderr, "No results found\n");
1404 ads_msgfree(ads, res);
1405 ads_destroy(&ads);
1406 return -1;
1409 ads_dump(ads, res);
1410 ads_msgfree(ads, res);
1411 ads_destroy(&ads);
1412 return 0;
1415 static int net_ads_printer_info(int argc, const char **argv)
1417 ADS_STRUCT *ads;
1418 ADS_STATUS rc;
1419 const char *servername, *printername;
1420 LDAPMessage *res = NULL;
1422 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1423 return -1;
1426 if (argc > 0) {
1427 printername = argv[0];
1428 } else {
1429 printername = "*";
1432 if (argc > 1) {
1433 servername = argv[1];
1434 } else {
1435 servername = global_myname();
1438 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1440 if (!ADS_ERR_OK(rc)) {
1441 d_fprintf(stderr, "Server '%s' not found: %s\n",
1442 servername, ads_errstr(rc));
1443 ads_msgfree(ads, res);
1444 ads_destroy(&ads);
1445 return -1;
1448 if (ads_count_replies(ads, res) == 0) {
1449 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1450 ads_msgfree(ads, res);
1451 ads_destroy(&ads);
1452 return -1;
1455 ads_dump(ads, res);
1456 ads_msgfree(ads, res);
1457 ads_destroy(&ads);
1459 return 0;
1462 static int net_ads_printer_publish(int argc, const char **argv)
1464 ADS_STRUCT *ads;
1465 ADS_STATUS rc;
1466 const char *servername, *printername;
1467 struct cli_state *cli;
1468 struct rpc_pipe_client *pipe_hnd;
1469 struct sockaddr_storage server_ss;
1470 NTSTATUS nt_status;
1471 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1472 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1473 char *prt_dn, *srv_dn, **srv_cn;
1474 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1475 LDAPMessage *res = NULL;
1477 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1478 talloc_destroy(mem_ctx);
1479 return -1;
1482 if (argc < 1) {
1483 talloc_destroy(mem_ctx);
1484 return net_ads_printer_usage(argc, argv);
1487 printername = argv[0];
1489 if (argc == 2) {
1490 servername = argv[1];
1491 } else {
1492 servername = global_myname();
1495 /* Get printer data from SPOOLSS */
1497 resolve_name(servername, &server_ss, 0x20);
1499 nt_status = cli_full_connection(&cli, global_myname(), servername,
1500 &server_ss, 0,
1501 "IPC$", "IPC",
1502 opt_user_name, opt_workgroup,
1503 opt_password ? opt_password : "",
1504 CLI_FULL_CONNECTION_USE_KERBEROS,
1505 Undefined, NULL);
1507 if (NT_STATUS_IS_ERR(nt_status)) {
1508 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1509 "for %s\n", servername, printername);
1510 ads_destroy(&ads);
1511 talloc_destroy(mem_ctx);
1512 return -1;
1515 /* Publish on AD server */
1517 ads_find_machine_acct(ads, &res, servername);
1519 if (ads_count_replies(ads, res) == 0) {
1520 d_fprintf(stderr, "Could not find machine account for server %s\n",
1521 servername);
1522 ads_destroy(&ads);
1523 talloc_destroy(mem_ctx);
1524 return -1;
1527 srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1528 srv_cn = ldap_explode_dn(srv_dn, 1);
1530 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1531 printername_escaped = escape_rdn_val_string_alloc(printername);
1532 if (!srv_cn_escaped || !printername_escaped) {
1533 SAFE_FREE(srv_cn_escaped);
1534 SAFE_FREE(printername_escaped);
1535 d_fprintf(stderr, "Internal error, out of memory!");
1536 ads_destroy(&ads);
1537 talloc_destroy(mem_ctx);
1538 return -1;
1541 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1543 SAFE_FREE(srv_cn_escaped);
1544 SAFE_FREE(printername_escaped);
1546 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1547 if (!pipe_hnd) {
1548 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1549 servername);
1550 SAFE_FREE(prt_dn);
1551 ads_destroy(&ads);
1552 talloc_destroy(mem_ctx);
1553 return -1;
1556 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1557 printername))) {
1558 SAFE_FREE(prt_dn);
1559 ads_destroy(&ads);
1560 talloc_destroy(mem_ctx);
1561 return -1;
1564 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1565 if (!ADS_ERR_OK(rc)) {
1566 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1567 SAFE_FREE(prt_dn);
1568 ads_destroy(&ads);
1569 talloc_destroy(mem_ctx);
1570 return -1;
1573 d_printf("published printer\n");
1574 SAFE_FREE(prt_dn);
1575 ads_destroy(&ads);
1576 talloc_destroy(mem_ctx);
1578 return 0;
1581 static int net_ads_printer_remove(int argc, const char **argv)
1583 ADS_STRUCT *ads;
1584 ADS_STATUS rc;
1585 const char *servername;
1586 char *prt_dn;
1587 LDAPMessage *res = NULL;
1589 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1590 return -1;
1593 if (argc < 1) {
1594 return net_ads_printer_usage(argc, argv);
1597 if (argc > 1) {
1598 servername = argv[1];
1599 } else {
1600 servername = global_myname();
1603 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1605 if (!ADS_ERR_OK(rc)) {
1606 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1607 ads_msgfree(ads, res);
1608 ads_destroy(&ads);
1609 return -1;
1612 if (ads_count_replies(ads, res) == 0) {
1613 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1614 ads_msgfree(ads, res);
1615 ads_destroy(&ads);
1616 return -1;
1619 prt_dn = ads_get_dn(ads, res);
1620 ads_msgfree(ads, res);
1621 rc = ads_del_dn(ads, prt_dn);
1622 ads_memfree(ads, prt_dn);
1624 if (!ADS_ERR_OK(rc)) {
1625 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1626 ads_destroy(&ads);
1627 return -1;
1630 ads_destroy(&ads);
1631 return 0;
1634 static int net_ads_printer(int argc, const char **argv)
1636 struct functable func[] = {
1637 {"SEARCH", net_ads_printer_search},
1638 {"INFO", net_ads_printer_info},
1639 {"PUBLISH", net_ads_printer_publish},
1640 {"REMOVE", net_ads_printer_remove},
1641 {NULL, NULL}
1644 return net_run_function(argc, argv, func, net_ads_printer_usage);
1648 static int net_ads_password(int argc, const char **argv)
1650 ADS_STRUCT *ads;
1651 const char *auth_principal = opt_user_name;
1652 const char *auth_password = opt_password;
1653 char *realm = NULL;
1654 char *new_password = NULL;
1655 char *c, *prompt;
1656 const char *user;
1657 ADS_STATUS ret;
1659 if (opt_user_name == NULL || opt_password == NULL) {
1660 d_fprintf(stderr, "You must supply an administrator username/password\n");
1661 return -1;
1664 if (argc < 1) {
1665 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1666 return -1;
1669 user = argv[0];
1670 if (!strchr_m(user, '@')) {
1671 asprintf(&c, "%s@%s", argv[0], lp_realm());
1672 user = c;
1675 use_in_memory_ccache();
1676 c = strchr_m(auth_principal, '@');
1677 if (c) {
1678 realm = ++c;
1679 } else {
1680 realm = lp_realm();
1683 /* use the realm so we can eventually change passwords for users
1684 in realms other than default */
1685 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1686 return -1;
1689 /* we don't actually need a full connect, but it's the easy way to
1690 fill in the KDC's addresss */
1691 ads_connect(ads);
1693 if (!ads->config.realm) {
1694 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1695 return -1;
1698 if (argv[1]) {
1699 new_password = (char *)argv[1];
1700 } else {
1701 asprintf(&prompt, "Enter new password for %s:", user);
1702 new_password = getpass(prompt);
1703 free(prompt);
1706 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1707 auth_password, user, new_password, ads->auth.time_offset);
1708 if (!ADS_ERR_OK(ret)) {
1709 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1710 ads_destroy(&ads);
1711 return -1;
1714 d_printf("Password change for %s completed.\n", user);
1715 ads_destroy(&ads);
1717 return 0;
1720 int net_ads_changetrustpw(int argc, const char **argv)
1722 ADS_STRUCT *ads;
1723 char *host_principal;
1724 fstring my_name;
1725 ADS_STATUS ret;
1727 if (!secrets_init()) {
1728 DEBUG(1,("Failed to initialise secrets database\n"));
1729 return -1;
1732 net_use_krb_machine_account();
1734 use_in_memory_ccache();
1736 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1737 return -1;
1740 fstrcpy(my_name, global_myname());
1741 strlower_m(my_name);
1742 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1743 d_printf("Changing password for principal: %s\n", host_principal);
1745 ret = ads_change_trust_account_password(ads, host_principal);
1747 if (!ADS_ERR_OK(ret)) {
1748 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1749 ads_destroy(&ads);
1750 SAFE_FREE(host_principal);
1751 return -1;
1754 d_printf("Password change for principal %s succeeded.\n", host_principal);
1756 if (lp_use_kerberos_keytab()) {
1757 d_printf("Attempting to update system keytab with new password.\n");
1758 if (ads_keytab_create_default(ads)) {
1759 d_printf("Failed to update system keytab.\n");
1763 ads_destroy(&ads);
1764 SAFE_FREE(host_principal);
1766 return 0;
1770 help for net ads search
1772 static int net_ads_search_usage(int argc, const char **argv)
1774 d_printf(
1775 "\nnet ads search <expression> <attributes...>\n"\
1776 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1777 "The expression is a standard LDAP search expression, and the\n"\
1778 "attributes are a list of LDAP fields to show in the results\n\n"\
1779 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1781 net_common_flags_usage(argc, argv);
1782 return -1;
1787 general ADS search function. Useful in diagnosing problems in ADS
1789 static int net_ads_search(int argc, const char **argv)
1791 ADS_STRUCT *ads;
1792 ADS_STATUS rc;
1793 const char *ldap_exp;
1794 const char **attrs;
1795 LDAPMessage *res = NULL;
1797 if (argc < 1) {
1798 return net_ads_search_usage(argc, argv);
1801 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1802 return -1;
1805 ldap_exp = argv[0];
1806 attrs = (argv + 1);
1808 rc = ads_do_search_all(ads, ads->config.bind_path,
1809 LDAP_SCOPE_SUBTREE,
1810 ldap_exp, attrs, &res);
1811 if (!ADS_ERR_OK(rc)) {
1812 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1813 ads_destroy(&ads);
1814 return -1;
1817 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1819 /* dump the results */
1820 ads_dump(ads, res);
1822 ads_msgfree(ads, res);
1823 ads_destroy(&ads);
1825 return 0;
1830 help for net ads search
1832 static int net_ads_dn_usage(int argc, const char **argv)
1834 d_printf(
1835 "\nnet ads dn <dn> <attributes...>\n"\
1836 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1837 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1838 "to show in the results\n\n"\
1839 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1840 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
1842 net_common_flags_usage(argc, argv);
1843 return -1;
1848 general ADS search function. Useful in diagnosing problems in ADS
1850 static int net_ads_dn(int argc, const char **argv)
1852 ADS_STRUCT *ads;
1853 ADS_STATUS rc;
1854 const char *dn;
1855 const char **attrs;
1856 LDAPMessage *res = NULL;
1858 if (argc < 1) {
1859 return net_ads_dn_usage(argc, argv);
1862 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1863 return -1;
1866 dn = argv[0];
1867 attrs = (argv + 1);
1869 rc = ads_do_search_all(ads, dn,
1870 LDAP_SCOPE_BASE,
1871 "(objectclass=*)", attrs, &res);
1872 if (!ADS_ERR_OK(rc)) {
1873 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1874 ads_destroy(&ads);
1875 return -1;
1878 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1880 /* dump the results */
1881 ads_dump(ads, res);
1883 ads_msgfree(ads, res);
1884 ads_destroy(&ads);
1886 return 0;
1890 help for net ads sid search
1892 static int net_ads_sid_usage(int argc, const char **argv)
1894 d_printf(
1895 "\nnet ads sid <sid> <attributes...>\n"\
1896 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1897 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1898 "to show in the results\n\n"\
1899 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1901 net_common_flags_usage(argc, argv);
1902 return -1;
1907 general ADS search function. Useful in diagnosing problems in ADS
1909 static int net_ads_sid(int argc, const char **argv)
1911 ADS_STRUCT *ads;
1912 ADS_STATUS rc;
1913 const char *sid_string;
1914 const char **attrs;
1915 LDAPMessage *res = NULL;
1916 DOM_SID sid;
1918 if (argc < 1) {
1919 return net_ads_sid_usage(argc, argv);
1922 if (!ADS_ERR_OK(ads_startup(False, &ads))) {
1923 return -1;
1926 sid_string = argv[0];
1927 attrs = (argv + 1);
1929 if (!string_to_sid(&sid, sid_string)) {
1930 d_fprintf(stderr, "could not convert sid\n");
1931 ads_destroy(&ads);
1932 return -1;
1935 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1936 if (!ADS_ERR_OK(rc)) {
1937 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1938 ads_destroy(&ads);
1939 return -1;
1942 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1944 /* dump the results */
1945 ads_dump(ads, res);
1947 ads_msgfree(ads, res);
1948 ads_destroy(&ads);
1950 return 0;
1954 static int net_ads_keytab_usage(int argc, const char **argv)
1956 d_printf(
1957 "net ads keytab <COMMAND>\n"\
1958 "<COMMAND> can be either:\n"\
1959 " ADD Adds new service principal\n"\
1960 " CREATE Creates a fresh keytab\n"\
1961 " FLUSH Flushes out all keytab entries\n"\
1962 " HELP Prints this help message\n"\
1963 " LIST List the keytab\n"\
1964 "The ADD and LIST command will take arguments, the other commands\n"\
1965 "will not take any arguments. The arguments given to ADD\n"\
1966 "should be a list of principals to add. For example, \n"\
1967 " net ads keytab add srv1 srv2\n"\
1968 "will add principals for the services srv1 and srv2 to the\n"\
1969 "system's keytab.\n"\
1970 "The LIST command takes a keytabname.\n"\
1971 "\n"
1973 return -1;
1976 static int net_ads_keytab_flush(int argc, const char **argv)
1978 int ret;
1979 ADS_STRUCT *ads;
1981 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1982 return -1;
1984 ret = ads_keytab_flush(ads);
1985 ads_destroy(&ads);
1986 return ret;
1989 static int net_ads_keytab_add(int argc, const char **argv)
1991 int i;
1992 int ret = 0;
1993 ADS_STRUCT *ads;
1995 d_printf("Processing principals to add...\n");
1996 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
1997 return -1;
1999 for (i = 0; i < argc; i++) {
2000 ret |= ads_keytab_add_entry(ads, argv[i]);
2002 ads_destroy(&ads);
2003 return ret;
2006 static int net_ads_keytab_create(int argc, const char **argv)
2008 ADS_STRUCT *ads;
2009 int ret;
2011 if (!ADS_ERR_OK(ads_startup(True, &ads))) {
2012 return -1;
2014 ret = ads_keytab_create_default(ads);
2015 ads_destroy(&ads);
2016 return ret;
2019 static int net_ads_keytab_list(int argc, const char **argv)
2021 const char *keytab = NULL;
2023 if (argc >= 1) {
2024 keytab = argv[0];
2027 return ads_keytab_list(keytab);
2031 int net_ads_keytab(int argc, const char **argv)
2033 struct functable func[] = {
2034 {"ADD", net_ads_keytab_add},
2035 {"CREATE", net_ads_keytab_create},
2036 {"FLUSH", net_ads_keytab_flush},
2037 {"HELP", net_ads_keytab_usage},
2038 {"LIST", net_ads_keytab_list},
2039 {NULL, NULL}
2042 if (!lp_use_kerberos_keytab()) {
2043 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2044 use keytab functions.\n");
2047 return net_run_function(argc, argv, func, net_ads_keytab_usage);
2050 static int net_ads_kerberos_usage(int argc, const char **argv)
2052 d_printf(
2053 "net ads kerberos <COMMAND>\n"\
2054 "<COMMAND> can be either:\n"\
2055 " RENEW Renew TGT from existing credential cache\n"\
2056 " PAC Dumps the Kerberos PAC\n"\
2057 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2058 "\n"
2061 return -1;
2064 static int net_ads_kerberos_renew(int argc, const char **argv)
2066 int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2067 if (ret) {
2068 d_printf("failed to renew kerberos ticket: %s\n",
2069 error_message(ret));
2071 return ret;
2074 static int net_ads_kerberos_pac(int argc, const char **argv)
2076 struct PAC_DATA *pac = NULL;
2077 struct PAC_LOGON_INFO *info = NULL;
2078 TALLOC_CTX *mem_ctx = NULL;
2079 NTSTATUS status;
2080 int ret = -1;
2082 mem_ctx = talloc_init("net_ads_kerberos_pac");
2083 if (!mem_ctx) {
2084 goto out;
2087 opt_password = net_prompt_pass(opt_user_name);
2089 status = kerberos_return_pac(mem_ctx,
2090 opt_user_name,
2091 opt_password,
2093 NULL,
2094 NULL,
2095 NULL,
2096 True,
2097 True,
2098 2592000, /* one month */
2099 &pac);
2100 if (!NT_STATUS_IS_OK(status)) {
2101 d_printf("failed to query kerberos PAC: %s\n",
2102 nt_errstr(status));
2103 goto out;
2106 info = get_logon_info_from_pac(pac);
2107 if (info) {
2108 const char *s;
2109 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
2110 d_printf("The Pac: %s\n", s);
2113 ret = 0;
2114 out:
2115 TALLOC_FREE(mem_ctx);
2116 return ret;
2119 static int net_ads_kerberos_kinit(int argc, const char **argv)
2121 TALLOC_CTX *mem_ctx = NULL;
2122 int ret = -1;
2123 NTSTATUS status;
2125 mem_ctx = talloc_init("net_ads_kerberos_kinit");
2126 if (!mem_ctx) {
2127 goto out;
2130 opt_password = net_prompt_pass(opt_user_name);
2132 ret = kerberos_kinit_password_ext(opt_user_name,
2133 opt_password,
2135 NULL,
2136 NULL,
2137 NULL,
2138 True,
2139 True,
2140 2592000, /* one month */
2141 &status);
2142 if (ret) {
2143 d_printf("failed to kinit password: %s\n",
2144 nt_errstr(status));
2146 out:
2147 return ret;
2150 int net_ads_kerberos(int argc, const char **argv)
2152 struct functable func[] = {
2153 {"KINIT", net_ads_kerberos_kinit},
2154 {"RENEW", net_ads_kerberos_renew},
2155 {"PAC", net_ads_kerberos_pac},
2156 {"HELP", net_ads_kerberos_usage},
2157 {NULL, NULL}
2160 return net_run_function(argc, argv, func, net_ads_kerberos_usage);
2164 int net_ads_help(int argc, const char **argv)
2166 struct functable func[] = {
2167 {"USER", net_ads_user_usage},
2168 {"GROUP", net_ads_group_usage},
2169 {"PRINTER", net_ads_printer_usage},
2170 {"SEARCH", net_ads_search_usage},
2171 {"INFO", net_ads_info},
2172 {"JOIN", net_ads_join_usage},
2173 {"DNS", net_ads_dns_usage},
2174 {"LEAVE", net_ads_leave},
2175 {"STATUS", net_ads_status},
2176 {"PASSWORD", net_ads_password},
2177 {"CHANGETRUSTPW", net_ads_changetrustpw},
2178 {NULL, NULL}
2181 return net_run_function(argc, argv, func, net_ads_usage);
2184 int net_ads(int argc, const char **argv)
2186 struct functable func[] = {
2187 {"INFO", net_ads_info},
2188 {"JOIN", net_ads_join},
2189 {"TESTJOIN", net_ads_testjoin},
2190 {"LEAVE", net_ads_leave},
2191 {"STATUS", net_ads_status},
2192 {"USER", net_ads_user},
2193 {"GROUP", net_ads_group},
2194 {"DNS", net_ads_dns},
2195 {"PASSWORD", net_ads_password},
2196 {"CHANGETRUSTPW", net_ads_changetrustpw},
2197 {"PRINTER", net_ads_printer},
2198 {"SEARCH", net_ads_search},
2199 {"DN", net_ads_dn},
2200 {"SID", net_ads_sid},
2201 {"WORKGROUP", net_ads_workgroup},
2202 {"LOOKUP", net_ads_lookup},
2203 {"KEYTAB", net_ads_keytab},
2204 {"GPO", net_ads_gpo},
2205 {"KERBEROS", net_ads_kerberos},
2206 {"HELP", net_ads_help},
2207 {NULL, NULL}
2210 return net_run_function(argc, argv, func, net_ads_usage);
2213 #else
2215 static int net_ads_noads(void)
2217 d_fprintf(stderr, "ADS support not compiled in\n");
2218 return -1;
2221 int net_ads_keytab(int argc, const char **argv)
2223 return net_ads_noads();
2226 int net_ads_kerberos(int argc, const char **argv)
2228 return net_ads_noads();
2231 int net_ads_usage(int argc, const char **argv)
2233 return net_ads_noads();
2236 int net_ads_help(int argc, const char **argv)
2238 return net_ads_noads();
2241 int net_ads_changetrustpw(int argc, const char **argv)
2243 return net_ads_noads();
2246 int net_ads_join(int argc, const char **argv)
2248 return net_ads_noads();
2251 int net_ads_user(int argc, const char **argv)
2253 return net_ads_noads();
2256 int net_ads_group(int argc, const char **argv)
2258 return net_ads_noads();
2261 /* this one shouldn't display a message */
2262 int net_ads_check(void)
2264 return -1;
2267 int net_ads_check_our_domain(void)
2269 return -1;
2272 int net_ads(int argc, const char **argv)
2274 return net_ads_usage(argc, argv);
2277 #endif /* WITH_ADS */