net: The top level help function for net cmd is always net_cmd_usage
[Samba.git] / source / utils / net_ads.c
blob8053b24051bf9c3238696c8ae0ddcc1d3254e260
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_help(struct net_context *c, 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(struct net_context *c)
71 if (!c->opt_host && strequal(lp_workgroup(), c->opt_target_workgroup)) {
72 return lp_realm();
75 return NULL;
79 do a cldap netlogon query
81 static int net_ads_cldap_netlogon(struct net_context *c, 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_5(talloc_tos(), 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 & NBT_SERVER_PDC) ? "yes" : "no",
122 (reply.server_type & NBT_SERVER_GC) ? "yes" : "no",
123 (reply.server_type & NBT_SERVER_LDAP) ? "yes" : "no",
124 (reply.server_type & NBT_SERVER_DS) ? "yes" : "no",
125 (reply.server_type & NBT_SERVER_KDC) ? "yes" : "no",
126 (reply.server_type & NBT_SERVER_TIMESERV) ? "yes" : "no",
127 (reply.server_type & NBT_SERVER_CLOSEST) ? "yes" : "no",
128 (reply.server_type & NBT_SERVER_WRITABLE) ? "yes" : "no",
129 (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? "yes" : "no",
130 (reply.server_type & DS_SERVER_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(struct net_context *c, int argc, const char **argv)
157 ADS_STRUCT *ads;
159 if (!ADS_ERR_OK(ads_startup_nobind(c, 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 *, c->opt_target_workgroup);
166 ads->ldap.port = 389;
169 return net_ads_cldap_netlogon(c, ads);
174 static int net_ads_info(struct net_context *c, int argc, const char **argv)
176 ADS_STRUCT *ads;
177 char addr[INET6_ADDRSTRLEN];
179 if (!ADS_ERR_OK(ads_startup_nobind(c, 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(struct net_context *c, bool only_own_domain,
218 uint32 auth_flags, ADS_STRUCT **ads_ret)
220 ADS_STRUCT *ads = NULL;
221 ADS_STATUS status;
222 bool need_password = false;
223 bool second_time = false;
224 char *cp;
225 const char *realm = NULL;
226 bool tried_closest_dc = false;
228 /* lp_realm() should be handled by a command line param,
229 However, the join requires that realm be set in smb.conf
230 and compares our realm with the remote server's so this is
231 ok until someone needs more flexibility */
233 *ads_ret = NULL;
235 retry_connect:
236 if (only_own_domain) {
237 realm = lp_realm();
238 } else {
239 realm = assume_own_realm(c);
242 ads = ads_init(realm, c->opt_target_workgroup, c->opt_host);
244 if (!c->opt_user_name) {
245 c->opt_user_name = "administrator";
248 if (c->opt_user_specified) {
249 need_password = true;
252 retry:
253 if (!c->opt_password && need_password && !c->opt_machine_pass) {
254 c->opt_password = net_prompt_pass(c, c->opt_user_name);
255 if (!c->opt_password) {
256 ads_destroy(&ads);
257 return ADS_ERROR(LDAP_NO_MEMORY);
261 if (c->opt_password) {
262 use_in_memory_ccache();
263 SAFE_FREE(ads->auth.password);
264 ads->auth.password = smb_xstrdup(c->opt_password);
267 ads->auth.flags |= auth_flags;
268 SAFE_FREE(ads->auth.user_name);
269 ads->auth.user_name = smb_xstrdup(c->opt_user_name);
272 * If the username is of the form "name@realm",
273 * extract the realm and convert to upper case.
274 * This is only used to establish the connection.
276 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
277 *cp++ = '\0';
278 SAFE_FREE(ads->auth.realm);
279 ads->auth.realm = smb_xstrdup(cp);
280 strupper_m(ads->auth.realm);
283 status = ads_connect(ads);
285 if (!ADS_ERR_OK(status)) {
287 if (NT_STATUS_EQUAL(ads_ntstatus(status),
288 NT_STATUS_NO_LOGON_SERVERS)) {
289 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
290 ads_destroy(&ads);
291 return status;
294 if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
295 need_password = true;
296 second_time = true;
297 goto retry;
298 } else {
299 ads_destroy(&ads);
300 return status;
304 /* when contacting our own domain, make sure we use the closest DC.
305 * This is done by reconnecting to ADS because only the first call to
306 * ads_connect will give us our own sitename */
308 if ((only_own_domain || !c->opt_host) && !tried_closest_dc) {
310 tried_closest_dc = true; /* avoid loop */
312 if (!ads->config.tried_closest_dc) {
314 namecache_delete(ads->server.realm, 0x1C);
315 namecache_delete(ads->server.workgroup, 0x1C);
317 ads_destroy(&ads);
318 ads = NULL;
320 goto retry_connect;
324 *ads_ret = ads;
325 return status;
328 ADS_STATUS ads_startup(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
330 return ads_startup_int(c, only_own_domain, 0, ads);
333 ADS_STATUS ads_startup_nobind(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
335 return ads_startup_int(c, only_own_domain, ADS_AUTH_NO_BIND, ads);
339 Check to see if connection can be made via ads.
340 ads_startup() stores the password in opt_password if it needs to so
341 that rpc or rap can use it without re-prompting.
343 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
345 ADS_STRUCT *ads;
346 ADS_STATUS status;
348 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
349 return -1;
352 ads->auth.flags |= ADS_AUTH_NO_BIND;
354 status = ads_connect(ads);
355 if ( !ADS_ERR_OK(status) ) {
356 return -1;
359 ads_destroy(&ads);
360 return 0;
363 int net_ads_check_our_domain(struct net_context *c)
365 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
368 int net_ads_check(struct net_context *c)
370 return net_ads_check_int(NULL, c->opt_workgroup, c->opt_host);
374 determine the netbios workgroup name for a domain
376 static int net_ads_workgroup(struct net_context *c, int argc, const char **argv)
378 ADS_STRUCT *ads;
379 char addr[INET6_ADDRSTRLEN];
380 struct nbt_cldap_netlogon_5 reply;
382 if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
383 d_fprintf(stderr, "Didn't find the cldap server!\n");
384 return -1;
387 if (!ads->config.realm) {
388 ads->config.realm = CONST_DISCARD(char *, c->opt_target_workgroup);
389 ads->ldap.port = 389;
392 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
393 if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
394 d_fprintf(stderr, "CLDAP query failed!\n");
395 return -1;
398 d_printf("Workgroup: %s\n", reply.domain);
400 ads_destroy(&ads);
402 return 0;
407 static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
409 char **disp_fields = (char **) data_area;
411 if (!field) { /* must be end of record */
412 if (disp_fields[0]) {
413 if (!strchr_m(disp_fields[0], '$')) {
414 if (disp_fields[1])
415 d_printf("%-21.21s %s\n",
416 disp_fields[0], disp_fields[1]);
417 else
418 d_printf("%s\n", disp_fields[0]);
421 SAFE_FREE(disp_fields[0]);
422 SAFE_FREE(disp_fields[1]);
423 return true;
425 if (!values) /* must be new field, indicate string field */
426 return true;
427 if (StrCaseCmp(field, "sAMAccountName") == 0) {
428 disp_fields[0] = SMB_STRDUP((char *) values[0]);
430 if (StrCaseCmp(field, "description") == 0)
431 disp_fields[1] = SMB_STRDUP((char *) values[0]);
432 return true;
435 static int net_ads_user_usage(struct net_context *c, int argc, const char **argv)
437 return net_user_usage(c, argc, argv);
440 static int ads_user_add(struct net_context *c, int argc, const char **argv)
442 ADS_STRUCT *ads;
443 ADS_STATUS status;
444 char *upn, *userdn;
445 LDAPMessage *res=NULL;
446 int rc = -1;
447 char *ou_str = NULL;
449 if (argc < 1) return net_ads_user_usage(c, argc, argv);
451 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
452 return -1;
455 status = ads_find_user_acct(ads, &res, argv[0]);
457 if (!ADS_ERR_OK(status)) {
458 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
459 goto done;
462 if (ads_count_replies(ads, res)) {
463 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
464 goto done;
467 if (c->opt_container) {
468 ou_str = SMB_STRDUP(c->opt_container);
469 } else {
470 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
473 status = ads_add_user_acct(ads, argv[0], ou_str, c->opt_comment);
475 if (!ADS_ERR_OK(status)) {
476 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
477 ads_errstr(status));
478 goto done;
481 /* if no password is to be set, we're done */
482 if (argc == 1) {
483 d_printf("User %s added\n", argv[0]);
484 rc = 0;
485 goto done;
488 /* try setting the password */
489 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
490 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
491 ads->auth.time_offset);
492 safe_free(upn);
493 if (ADS_ERR_OK(status)) {
494 d_printf("User %s added\n", argv[0]);
495 rc = 0;
496 goto done;
499 /* password didn't set, delete account */
500 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
501 argv[0], ads_errstr(status));
502 ads_msgfree(ads, res);
503 status=ads_find_user_acct(ads, &res, argv[0]);
504 if (ADS_ERR_OK(status)) {
505 userdn = ads_get_dn(ads, res);
506 ads_del_dn(ads, userdn);
507 ads_memfree(ads, userdn);
510 done:
511 if (res)
512 ads_msgfree(ads, res);
513 ads_destroy(&ads);
514 SAFE_FREE(ou_str);
515 return rc;
518 static int ads_user_info(struct net_context *c, int argc, const char **argv)
520 ADS_STRUCT *ads;
521 ADS_STATUS rc;
522 LDAPMessage *res;
523 const char *attrs[] = {"memberOf", NULL};
524 char *searchstring=NULL;
525 char **grouplist;
526 char *escaped_user;
528 if (argc < 1) {
529 return net_ads_user_usage(c, argc, argv);
532 escaped_user = escape_ldap_string_alloc(argv[0]);
534 if (!escaped_user) {
535 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
536 return -1;
539 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
540 SAFE_FREE(escaped_user);
541 return -1;
544 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
545 rc = ads_search(ads, &res, searchstring, attrs);
546 safe_free(searchstring);
548 if (!ADS_ERR_OK(rc)) {
549 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
550 ads_destroy(&ads);
551 SAFE_FREE(escaped_user);
552 return -1;
555 grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
556 (LDAPMessage *)res, "memberOf");
558 if (grouplist) {
559 int i;
560 char **groupname;
561 for (i=0;grouplist[i];i++) {
562 groupname = ldap_explode_dn(grouplist[i], 1);
563 d_printf("%s\n", groupname[0]);
564 ldap_value_free(groupname);
566 ldap_value_free(grouplist);
569 ads_msgfree(ads, res);
570 ads_destroy(&ads);
571 SAFE_FREE(escaped_user);
572 return 0;
575 static int ads_user_delete(struct net_context *c, int argc, const char **argv)
577 ADS_STRUCT *ads;
578 ADS_STATUS rc;
579 LDAPMessage *res = NULL;
580 char *userdn;
582 if (argc < 1) {
583 return net_ads_user_usage(c, argc, argv);
586 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
587 return -1;
590 rc = ads_find_user_acct(ads, &res, argv[0]);
591 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
592 d_printf("User %s does not exist.\n", argv[0]);
593 ads_msgfree(ads, res);
594 ads_destroy(&ads);
595 return -1;
597 userdn = ads_get_dn(ads, res);
598 ads_msgfree(ads, res);
599 rc = ads_del_dn(ads, userdn);
600 ads_memfree(ads, userdn);
601 if (ADS_ERR_OK(rc)) {
602 d_printf("User %s deleted\n", argv[0]);
603 ads_destroy(&ads);
604 return 0;
606 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
607 ads_errstr(rc));
608 ads_destroy(&ads);
609 return -1;
612 int net_ads_user(struct net_context *c, int argc, const char **argv)
614 struct functable func[] = {
615 {"ADD", ads_user_add},
616 {"INFO", ads_user_info},
617 {"DELETE", ads_user_delete},
618 {NULL, NULL}
620 ADS_STRUCT *ads;
621 ADS_STATUS rc;
622 const char *shortattrs[] = {"sAMAccountName", NULL};
623 const char *longattrs[] = {"sAMAccountName", "description", NULL};
624 char *disp_fields[2] = {NULL, NULL};
626 if (argc == 0) {
627 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
628 return -1;
631 if (c->opt_long_list_entries)
632 d_printf("\nUser name Comment"\
633 "\n-----------------------------\n");
635 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
636 LDAP_SCOPE_SUBTREE,
637 "(objectCategory=user)",
638 c->opt_long_list_entries ? longattrs :
639 shortattrs, usergrp_display,
640 disp_fields);
641 ads_destroy(&ads);
642 return ADS_ERR_OK(rc) ? 0 : -1;
645 return net_run_function(c, argc, argv, func, net_ads_user_usage);
648 static int net_ads_group_usage(struct net_context *c, int argc, const char **argv)
650 return net_group_usage(c, argc, argv);
653 static int ads_group_add(struct net_context *c, int argc, const char **argv)
655 ADS_STRUCT *ads;
656 ADS_STATUS status;
657 LDAPMessage *res=NULL;
658 int rc = -1;
659 char *ou_str = NULL;
661 if (argc < 1) {
662 return net_ads_group_usage(c, argc, argv);
665 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
666 return -1;
669 status = ads_find_user_acct(ads, &res, argv[0]);
671 if (!ADS_ERR_OK(status)) {
672 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
673 goto done;
676 if (ads_count_replies(ads, res)) {
677 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
678 goto done;
681 if (c->opt_container) {
682 ou_str = SMB_STRDUP(c->opt_container);
683 } else {
684 ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
687 status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment);
689 if (ADS_ERR_OK(status)) {
690 d_printf("Group %s added\n", argv[0]);
691 rc = 0;
692 } else {
693 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
694 ads_errstr(status));
697 done:
698 if (res)
699 ads_msgfree(ads, res);
700 ads_destroy(&ads);
701 SAFE_FREE(ou_str);
702 return rc;
705 static int ads_group_delete(struct net_context *c, int argc, const char **argv)
707 ADS_STRUCT *ads;
708 ADS_STATUS rc;
709 LDAPMessage *res = NULL;
710 char *groupdn;
712 if (argc < 1) {
713 return net_ads_group_usage(c, argc, argv);
716 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
717 return -1;
720 rc = ads_find_user_acct(ads, &res, argv[0]);
721 if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
722 d_printf("Group %s does not exist.\n", argv[0]);
723 ads_msgfree(ads, res);
724 ads_destroy(&ads);
725 return -1;
727 groupdn = ads_get_dn(ads, res);
728 ads_msgfree(ads, res);
729 rc = ads_del_dn(ads, groupdn);
730 ads_memfree(ads, groupdn);
731 if (ADS_ERR_OK(rc)) {
732 d_printf("Group %s deleted\n", argv[0]);
733 ads_destroy(&ads);
734 return 0;
736 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
737 ads_errstr(rc));
738 ads_destroy(&ads);
739 return -1;
742 int net_ads_group(struct net_context *c, int argc, const char **argv)
744 struct functable func[] = {
745 {"ADD", ads_group_add},
746 {"DELETE", ads_group_delete},
747 {NULL, NULL}
749 ADS_STRUCT *ads;
750 ADS_STATUS rc;
751 const char *shortattrs[] = {"sAMAccountName", NULL};
752 const char *longattrs[] = {"sAMAccountName", "description", NULL};
753 char *disp_fields[2] = {NULL, NULL};
755 if (argc == 0) {
756 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
757 return -1;
760 if (c->opt_long_list_entries)
761 d_printf("\nGroup name Comment"\
762 "\n-----------------------------\n");
763 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
764 LDAP_SCOPE_SUBTREE,
765 "(objectCategory=group)",
766 c->opt_long_list_entries ? longattrs :
767 shortattrs, usergrp_display,
768 disp_fields);
770 ads_destroy(&ads);
771 return ADS_ERR_OK(rc) ? 0 : -1;
773 return net_run_function(c, argc, argv, func, net_ads_group_usage);
776 static int net_ads_status(struct net_context *c, int argc, const char **argv)
778 ADS_STRUCT *ads;
779 ADS_STATUS rc;
780 LDAPMessage *res;
782 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
783 return -1;
786 rc = ads_find_machine_acct(ads, &res, global_myname());
787 if (!ADS_ERR_OK(rc)) {
788 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
789 ads_destroy(&ads);
790 return -1;
793 if (ads_count_replies(ads, res) == 0) {
794 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
795 ads_destroy(&ads);
796 return -1;
799 ads_dump(ads, res);
800 ads_destroy(&ads);
801 return 0;
804 /*******************************************************************
805 Leave an AD domain. Windows XP disables the machine account.
806 We'll try the same. The old code would do an LDAP delete.
807 That only worked using the machine creds because added the machine
808 with full control to the computer object's ACL.
809 *******************************************************************/
811 static int net_ads_leave(struct net_context *c, int argc, const char **argv)
813 TALLOC_CTX *ctx;
814 struct libnet_UnjoinCtx *r = NULL;
815 WERROR werr;
817 if (!*lp_realm()) {
818 d_fprintf(stderr, "No realm set, are we joined ?\n");
819 return -1;
822 if (!(ctx = talloc_init("net_ads_leave"))) {
823 d_fprintf(stderr, "Could not initialise talloc context.\n");
824 return -1;
827 use_in_memory_ccache();
829 werr = libnet_init_UnjoinCtx(ctx, &r);
830 if (!W_ERROR_IS_OK(werr)) {
831 d_fprintf(stderr, "Could not initialise unjoin context.\n");
832 return -1;
835 r->in.debug = true;
836 r->in.dc_name = c->opt_host;
837 r->in.domain_name = lp_realm();
838 r->in.admin_account = c->opt_user_name;
839 r->in.admin_password = net_prompt_pass(c, c->opt_user_name);
840 r->in.modify_config = lp_config_backend_is_registry();
841 r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
842 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
844 werr = libnet_Unjoin(ctx, r);
845 if (!W_ERROR_IS_OK(werr)) {
846 d_printf("Failed to leave domain: %s\n",
847 r->out.error_string ? r->out.error_string :
848 get_friendly_werror_msg(werr));
849 goto done;
852 if (W_ERROR_IS_OK(werr)) {
853 d_printf("Deleted account for '%s' in realm '%s'\n",
854 r->in.machine_name, r->out.dns_domain_name);
855 goto done;
858 /* We couldn't delete it - see if the disable succeeded. */
859 if (r->out.disabled_machine_account) {
860 d_printf("Disabled account for '%s' in realm '%s'\n",
861 r->in.machine_name, r->out.dns_domain_name);
862 werr = WERR_OK;
863 goto done;
866 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
867 r->in.machine_name, r->out.dns_domain_name);
869 done:
870 TALLOC_FREE(r);
871 TALLOC_FREE(ctx);
873 if (W_ERROR_IS_OK(werr)) {
874 return 0;
877 return -1;
880 static NTSTATUS net_ads_join_ok(struct net_context *c)
882 ADS_STRUCT *ads = NULL;
883 ADS_STATUS status;
885 if (!secrets_init()) {
886 DEBUG(1,("Failed to initialise secrets database\n"));
887 return NT_STATUS_ACCESS_DENIED;
890 net_use_krb_machine_account(c);
892 status = ads_startup(c, true, &ads);
893 if (!ADS_ERR_OK(status)) {
894 return ads_ntstatus(status);
897 ads_destroy(&ads);
898 return NT_STATUS_OK;
902 check that an existing join is OK
904 int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
906 NTSTATUS status;
907 use_in_memory_ccache();
909 /* Display success or failure */
910 status = net_ads_join_ok(c);
911 if (!NT_STATUS_IS_OK(status)) {
912 fprintf(stderr,"Join to domain is not valid: %s\n",
913 get_friendly_nt_error_msg(status));
914 return -1;
917 printf("Join is OK\n");
918 return 0;
921 /*******************************************************************
922 Simple configu checks before beginning the join
923 ********************************************************************/
925 static WERROR check_ads_config( void )
927 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
928 d_printf("Host is not configured as a member server.\n");
929 return WERR_INVALID_DOMAIN_ROLE;
932 if (strlen(global_myname()) > 15) {
933 d_printf("Our netbios name can be at most 15 chars long, "
934 "\"%s\" is %u chars long\n", global_myname(),
935 (unsigned int)strlen(global_myname()));
936 return WERR_INVALID_COMPUTER_NAME;
939 if ( lp_security() == SEC_ADS && !*lp_realm()) {
940 d_fprintf(stderr, "realm must be set in in %s for ADS "
941 "join to succeed.\n", get_dyn_CONFIGFILE());
942 return WERR_INVALID_PARAM;
945 return WERR_OK;
948 /*******************************************************************
949 Send a DNS update request
950 *******************************************************************/
952 #if defined(WITH_DNS_UPDATES)
953 #include "dns.h"
954 DNS_ERROR DoDNSUpdate(char *pszServerName,
955 const char *pszDomainName, const char *pszHostName,
956 const struct sockaddr_storage *sslist,
957 size_t num_addrs );
959 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
960 const char *machine_name,
961 const struct sockaddr_storage *addrs,
962 int num_addrs)
964 struct dns_rr_ns *nameservers = NULL;
965 int ns_count = 0;
966 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
967 DNS_ERROR dns_err;
968 fstring dns_server;
969 const char *dnsdomain = NULL;
970 char *root_domain = NULL;
972 if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
973 d_printf("No DNS domain configured for %s. "
974 "Unable to perform DNS Update.\n", machine_name);
975 status = NT_STATUS_INVALID_PARAMETER;
976 goto done;
978 dnsdomain++;
980 status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
981 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
982 /* Child domains often do not have NS records. Look
983 for the NS record for the forest root domain
984 (rootDomainNamingContext in therootDSE) */
986 const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
987 LDAPMessage *msg = NULL;
988 char *root_dn;
989 ADS_STATUS ads_status;
991 if ( !ads->ldap.ld ) {
992 ads_status = ads_connect( ads );
993 if ( !ADS_ERR_OK(ads_status) ) {
994 DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
995 goto done;
999 ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
1000 "(objectclass=*)", rootname_attrs, &msg);
1001 if (!ADS_ERR_OK(ads_status)) {
1002 goto done;
1005 root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
1006 if ( !root_dn ) {
1007 ads_msgfree( ads, msg );
1008 goto done;
1011 root_domain = ads_build_domain( root_dn );
1013 /* cleanup */
1014 ads_msgfree( ads, msg );
1016 /* try again for NS servers */
1018 status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
1020 if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
1021 DEBUG(3,("net_ads_join: Failed to find name server for the %s "
1022 "realm\n", ads->config.realm));
1023 goto done;
1026 dnsdomain = root_domain;
1030 /* Now perform the dns update - we'll try non-secure and if we fail,
1031 we'll follow it up with a secure update */
1033 fstrcpy( dns_server, nameservers[0].hostname );
1035 dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
1036 if (!ERR_DNS_IS_OK(dns_err)) {
1037 status = NT_STATUS_UNSUCCESSFUL;
1040 done:
1042 SAFE_FREE( root_domain );
1044 return status;
1047 static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
1049 int num_addrs;
1050 struct sockaddr_storage *iplist = NULL;
1051 fstring machine_name;
1052 NTSTATUS status;
1054 name_to_fqdn( machine_name, global_myname() );
1055 strlower_m( machine_name );
1057 /* Get our ip address (not the 127.0.0.x address but a real ip
1058 * address) */
1060 num_addrs = get_my_ip_address( &iplist );
1061 if ( num_addrs <= 0 ) {
1062 DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
1063 "addresses!\n"));
1064 return NT_STATUS_INVALID_PARAMETER;
1067 status = net_update_dns_internal(mem_ctx, ads, machine_name,
1068 iplist, num_addrs);
1069 SAFE_FREE( iplist );
1070 return status;
1072 #endif
1075 /*******************************************************************
1076 ********************************************************************/
1078 static int net_ads_join_usage(struct net_context *c, int argc, const char **argv)
1080 d_printf("net ads join [options]\n");
1081 d_printf("Valid options:\n");
1082 d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
1083 d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
1084 d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
1085 d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
1086 d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
1087 d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
1088 d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
1089 d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
1090 d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
1091 d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
1092 d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
1093 d_printf(" the two other attributes.\n");
1095 return -1;
1098 /*******************************************************************
1099 ********************************************************************/
1101 int net_ads_join(struct net_context *c, int argc, const char **argv)
1103 TALLOC_CTX *ctx = NULL;
1104 struct libnet_JoinCtx *r = NULL;
1105 const char *domain = lp_realm();
1106 WERROR werr = WERR_SETUP_NOT_JOINED;
1107 bool createupn = false;
1108 const char *machineupn = NULL;
1109 const char *create_in_ou = NULL;
1110 int i;
1111 const char *os_name = NULL;
1112 const char *os_version = NULL;
1113 bool modify_config = lp_config_backend_is_registry();
1115 if (!modify_config) {
1117 werr = check_ads_config();
1118 if (!W_ERROR_IS_OK(werr)) {
1119 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1120 goto fail;
1124 if (!(ctx = talloc_init("net_ads_join"))) {
1125 d_fprintf(stderr, "Could not initialise talloc context.\n");
1126 werr = WERR_NOMEM;
1127 goto fail;
1130 use_in_memory_ccache();
1132 werr = libnet_init_JoinCtx(ctx, &r);
1133 if (!W_ERROR_IS_OK(werr)) {
1134 goto fail;
1137 /* process additional command line args */
1139 for ( i=0; i<argc; i++ ) {
1140 if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
1141 createupn = true;
1142 machineupn = get_string_param(argv[i]);
1144 else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
1145 if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
1146 d_fprintf(stderr, "Please supply a valid OU path.\n");
1147 werr = WERR_INVALID_PARAM;
1148 goto fail;
1151 else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
1152 if ( (os_name = get_string_param(argv[i])) == NULL ) {
1153 d_fprintf(stderr, "Please supply a operating system name.\n");
1154 werr = WERR_INVALID_PARAM;
1155 goto fail;
1158 else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
1159 if ( (os_version = get_string_param(argv[i])) == NULL ) {
1160 d_fprintf(stderr, "Please supply a valid operating system version.\n");
1161 werr = WERR_INVALID_PARAM;
1162 goto fail;
1165 else {
1166 domain = argv[i];
1170 if (!*domain) {
1171 d_fprintf(stderr, "Please supply a valid domain name\n");
1172 werr = WERR_INVALID_PARAM;
1173 goto fail;
1176 /* Do the domain join here */
1178 r->in.domain_name = domain;
1179 r->in.create_upn = createupn;
1180 r->in.upn = machineupn;
1181 r->in.account_ou = create_in_ou;
1182 r->in.os_name = os_name;
1183 r->in.os_version = os_version;
1184 r->in.dc_name = c->opt_host;
1185 r->in.admin_account = c->opt_user_name;
1186 r->in.admin_password = net_prompt_pass(c, c->opt_user_name);
1187 r->in.debug = true;
1188 r->in.modify_config = modify_config;
1189 r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
1190 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
1191 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
1193 werr = libnet_Join(ctx, r);
1194 if (!W_ERROR_IS_OK(werr)) {
1195 goto fail;
1198 /* Check the short name of the domain */
1200 if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
1201 d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
1202 d_printf("domain name obtained from the server.\n");
1203 d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
1204 d_printf("You should set \"workgroup = %s\" in %s.\n",
1205 r->out.netbios_domain_name, get_dyn_CONFIGFILE());
1208 d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
1210 if (r->out.dns_domain_name) {
1211 d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
1212 r->out.dns_domain_name);
1213 } else {
1214 d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
1215 r->out.netbios_domain_name);
1218 #if defined(WITH_DNS_UPDATES)
1219 if (r->out.domain_is_ad) {
1220 /* We enter this block with user creds */
1221 ADS_STRUCT *ads_dns = NULL;
1223 if ( (ads_dns = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
1224 /* kinit with the machine password */
1226 use_in_memory_ccache();
1227 asprintf( &ads_dns->auth.user_name, "%s$", global_myname() );
1228 ads_dns->auth.password = secrets_fetch_machine_password(
1229 r->out.netbios_domain_name, NULL, NULL );
1230 ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
1231 strupper_m(ads_dns->auth.realm );
1232 ads_kinit_password( ads_dns );
1235 if ( !ads_dns || !NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns )) ) {
1236 d_fprintf( stderr, "DNS update failed!\n" );
1239 /* exit from this block using machine creds */
1240 ads_destroy(&ads_dns);
1242 #endif
1243 TALLOC_FREE(r);
1244 TALLOC_FREE( ctx );
1246 return 0;
1248 fail:
1249 /* issue an overall failure message at the end. */
1250 d_printf("Failed to join domain: %s\n",
1251 r && r->out.error_string ? r->out.error_string :
1252 get_friendly_werror_msg(werr));
1253 TALLOC_FREE( ctx );
1255 return -1;
1258 /*******************************************************************
1259 ********************************************************************/
1261 static int net_ads_dns_usage(struct net_context *c, int argc, const char **argv)
1263 #if defined(WITH_DNS_UPDATES)
1264 d_printf("net ads dns <command>\n");
1265 d_printf("Valid commands:\n");
1266 d_printf(" register Issue a dynamic DNS update request for our hostname\n");
1268 return 0;
1269 #else
1270 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1271 return -1;
1272 #endif
1275 /*******************************************************************
1276 ********************************************************************/
1278 static int net_ads_dns_register(struct net_context *c, int argc, const char **argv)
1280 #if defined(WITH_DNS_UPDATES)
1281 ADS_STRUCT *ads;
1282 ADS_STATUS status;
1283 TALLOC_CTX *ctx;
1285 #ifdef DEVELOPER
1286 talloc_enable_leak_report();
1287 #endif
1289 if (argc > 0) {
1290 d_fprintf(stderr, "net ads dns register\n");
1291 return -1;
1294 if (!(ctx = talloc_init("net_ads_dns"))) {
1295 d_fprintf(stderr, "Could not initialise talloc context\n");
1296 return -1;
1299 status = ads_startup(c, true, &ads);
1300 if ( !ADS_ERR_OK(status) ) {
1301 DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
1302 TALLOC_FREE(ctx);
1303 return -1;
1306 if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
1307 d_fprintf( stderr, "DNS update failed!\n" );
1308 ads_destroy( &ads );
1309 TALLOC_FREE( ctx );
1310 return -1;
1313 d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
1315 ads_destroy(&ads);
1316 TALLOC_FREE( ctx );
1318 return 0;
1319 #else
1320 d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
1321 return -1;
1322 #endif
1325 #if defined(WITH_DNS_UPDATES)
1326 DNS_ERROR do_gethostbyname(const char *server, const char *host);
1327 #endif
1329 static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char **argv)
1331 #if defined(WITH_DNS_UPDATES)
1332 DNS_ERROR err;
1334 #ifdef DEVELOPER
1335 talloc_enable_leak_report();
1336 #endif
1338 if (argc != 2) {
1339 d_fprintf(stderr, "net ads dns gethostbyname <server> "
1340 "<name>\n");
1341 return -1;
1344 err = do_gethostbyname(argv[0], argv[1]);
1346 d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
1347 #endif
1348 return 0;
1351 static int net_ads_dns(struct net_context *c, int argc, const char *argv[])
1353 struct functable func[] = {
1354 {"REGISTER", net_ads_dns_register},
1355 {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
1356 {NULL, NULL}
1359 return net_run_function(c, argc, argv, func, net_ads_dns_usage);
1362 /*******************************************************************
1363 ********************************************************************/
1365 int net_ads_printer_usage(struct net_context *c, int argc, const char **argv)
1367 d_printf(
1368 "\nnet ads printer search <printer>"
1369 "\n\tsearch for a printer in the directory\n"
1370 "\nnet ads printer info <printer> <server>"
1371 "\n\tlookup info in directory for printer on server"
1372 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1373 "\nnet ads printer publish <printername>"
1374 "\n\tpublish printer in directory"
1375 "\n\t(note: printer name is required)\n"
1376 "\nnet ads printer remove <printername>"
1377 "\n\tremove printer from directory"
1378 "\n\t(note: printer name is required)\n");
1379 return -1;
1382 /*******************************************************************
1383 ********************************************************************/
1385 static int net_ads_printer_search(struct net_context *c, int argc, const char **argv)
1387 ADS_STRUCT *ads;
1388 ADS_STATUS rc;
1389 LDAPMessage *res = NULL;
1391 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
1392 return -1;
1395 rc = ads_find_printers(ads, &res);
1397 if (!ADS_ERR_OK(rc)) {
1398 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1399 ads_msgfree(ads, res);
1400 ads_destroy(&ads);
1401 return -1;
1404 if (ads_count_replies(ads, res) == 0) {
1405 d_fprintf(stderr, "No results found\n");
1406 ads_msgfree(ads, res);
1407 ads_destroy(&ads);
1408 return -1;
1411 ads_dump(ads, res);
1412 ads_msgfree(ads, res);
1413 ads_destroy(&ads);
1414 return 0;
1417 static int net_ads_printer_info(struct net_context *c, int argc, const char **argv)
1419 ADS_STRUCT *ads;
1420 ADS_STATUS rc;
1421 const char *servername, *printername;
1422 LDAPMessage *res = NULL;
1424 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
1425 return -1;
1428 if (argc > 0) {
1429 printername = argv[0];
1430 } else {
1431 printername = "*";
1434 if (argc > 1) {
1435 servername = argv[1];
1436 } else {
1437 servername = global_myname();
1440 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1442 if (!ADS_ERR_OK(rc)) {
1443 d_fprintf(stderr, "Server '%s' not found: %s\n",
1444 servername, ads_errstr(rc));
1445 ads_msgfree(ads, res);
1446 ads_destroy(&ads);
1447 return -1;
1450 if (ads_count_replies(ads, res) == 0) {
1451 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1452 ads_msgfree(ads, res);
1453 ads_destroy(&ads);
1454 return -1;
1457 ads_dump(ads, res);
1458 ads_msgfree(ads, res);
1459 ads_destroy(&ads);
1461 return 0;
1464 static int net_ads_printer_publish(struct net_context *c, int argc, const char **argv)
1466 ADS_STRUCT *ads;
1467 ADS_STATUS rc;
1468 const char *servername, *printername;
1469 struct cli_state *cli;
1470 struct rpc_pipe_client *pipe_hnd;
1471 struct sockaddr_storage server_ss;
1472 NTSTATUS nt_status;
1473 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1474 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1475 char *prt_dn, *srv_dn, **srv_cn;
1476 char *srv_cn_escaped = NULL, *printername_escaped = NULL;
1477 LDAPMessage *res = NULL;
1479 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
1480 talloc_destroy(mem_ctx);
1481 return -1;
1484 if (argc < 1) {
1485 talloc_destroy(mem_ctx);
1486 return net_ads_printer_usage(c, argc, argv);
1489 printername = argv[0];
1491 if (argc == 2) {
1492 servername = argv[1];
1493 } else {
1494 servername = global_myname();
1497 /* Get printer data from SPOOLSS */
1499 resolve_name(servername, &server_ss, 0x20);
1501 nt_status = cli_full_connection(&cli, global_myname(), servername,
1502 &server_ss, 0,
1503 "IPC$", "IPC",
1504 c->opt_user_name, c->opt_workgroup,
1505 c->opt_password ? c->opt_password : "",
1506 CLI_FULL_CONNECTION_USE_KERBEROS,
1507 Undefined, NULL);
1509 if (NT_STATUS_IS_ERR(nt_status)) {
1510 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1511 "for %s\n", servername, printername);
1512 ads_destroy(&ads);
1513 talloc_destroy(mem_ctx);
1514 return -1;
1517 /* Publish on AD server */
1519 ads_find_machine_acct(ads, &res, servername);
1521 if (ads_count_replies(ads, res) == 0) {
1522 d_fprintf(stderr, "Could not find machine account for server %s\n",
1523 servername);
1524 ads_destroy(&ads);
1525 talloc_destroy(mem_ctx);
1526 return -1;
1529 srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
1530 srv_cn = ldap_explode_dn(srv_dn, 1);
1532 srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
1533 printername_escaped = escape_rdn_val_string_alloc(printername);
1534 if (!srv_cn_escaped || !printername_escaped) {
1535 SAFE_FREE(srv_cn_escaped);
1536 SAFE_FREE(printername_escaped);
1537 d_fprintf(stderr, "Internal error, out of memory!");
1538 ads_destroy(&ads);
1539 talloc_destroy(mem_ctx);
1540 return -1;
1543 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
1545 SAFE_FREE(srv_cn_escaped);
1546 SAFE_FREE(printername_escaped);
1548 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1549 if (!pipe_hnd) {
1550 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1551 servername);
1552 SAFE_FREE(prt_dn);
1553 ads_destroy(&ads);
1554 talloc_destroy(mem_ctx);
1555 return -1;
1558 if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1559 printername))) {
1560 SAFE_FREE(prt_dn);
1561 ads_destroy(&ads);
1562 talloc_destroy(mem_ctx);
1563 return -1;
1566 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1567 if (!ADS_ERR_OK(rc)) {
1568 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1569 SAFE_FREE(prt_dn);
1570 ads_destroy(&ads);
1571 talloc_destroy(mem_ctx);
1572 return -1;
1575 d_printf("published printer\n");
1576 SAFE_FREE(prt_dn);
1577 ads_destroy(&ads);
1578 talloc_destroy(mem_ctx);
1580 return 0;
1583 static int net_ads_printer_remove(struct net_context *c, int argc, const char **argv)
1585 ADS_STRUCT *ads;
1586 ADS_STATUS rc;
1587 const char *servername;
1588 char *prt_dn;
1589 LDAPMessage *res = NULL;
1591 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
1592 return -1;
1595 if (argc < 1) {
1596 return net_ads_printer_usage(c, argc, argv);
1599 if (argc > 1) {
1600 servername = argv[1];
1601 } else {
1602 servername = global_myname();
1605 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1607 if (!ADS_ERR_OK(rc)) {
1608 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1609 ads_msgfree(ads, res);
1610 ads_destroy(&ads);
1611 return -1;
1614 if (ads_count_replies(ads, res) == 0) {
1615 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1616 ads_msgfree(ads, res);
1617 ads_destroy(&ads);
1618 return -1;
1621 prt_dn = ads_get_dn(ads, res);
1622 ads_msgfree(ads, res);
1623 rc = ads_del_dn(ads, prt_dn);
1624 ads_memfree(ads, prt_dn);
1626 if (!ADS_ERR_OK(rc)) {
1627 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1628 ads_destroy(&ads);
1629 return -1;
1632 ads_destroy(&ads);
1633 return 0;
1636 static int net_ads_printer(struct net_context *c, int argc, const char **argv)
1638 struct functable func[] = {
1639 {"SEARCH", net_ads_printer_search},
1640 {"INFO", net_ads_printer_info},
1641 {"PUBLISH", net_ads_printer_publish},
1642 {"REMOVE", net_ads_printer_remove},
1643 {NULL, NULL}
1646 return net_run_function(c, argc, argv, func, net_ads_printer_usage);
1650 static int net_ads_password(struct net_context *c, int argc, const char **argv)
1652 ADS_STRUCT *ads;
1653 const char *auth_principal = c->opt_user_name;
1654 const char *auth_password = c->opt_password;
1655 char *realm = NULL;
1656 char *new_password = NULL;
1657 char *chr, *prompt;
1658 const char *user;
1659 ADS_STATUS ret;
1661 if (c->opt_user_name == NULL || c->opt_password == NULL) {
1662 d_fprintf(stderr, "You must supply an administrator username/password\n");
1663 return -1;
1666 if (argc < 1) {
1667 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1668 return -1;
1671 user = argv[0];
1672 if (!strchr_m(user, '@')) {
1673 asprintf(&chr, "%s@%s", argv[0], lp_realm());
1674 user = chr;
1677 use_in_memory_ccache();
1678 chr = strchr_m(auth_principal, '@');
1679 if (chr) {
1680 realm = ++chr;
1681 } else {
1682 realm = lp_realm();
1685 /* use the realm so we can eventually change passwords for users
1686 in realms other than default */
1687 if (!(ads = ads_init(realm, c->opt_workgroup, c->opt_host))) {
1688 return -1;
1691 /* we don't actually need a full connect, but it's the easy way to
1692 fill in the KDC's addresss */
1693 ads_connect(ads);
1695 if (!ads->config.realm) {
1696 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1697 return -1;
1700 if (argv[1]) {
1701 new_password = (char *)argv[1];
1702 } else {
1703 asprintf(&prompt, "Enter new password for %s:", user);
1704 new_password = getpass(prompt);
1705 free(prompt);
1708 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1709 auth_password, user, new_password, ads->auth.time_offset);
1710 if (!ADS_ERR_OK(ret)) {
1711 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1712 ads_destroy(&ads);
1713 return -1;
1716 d_printf("Password change for %s completed.\n", user);
1717 ads_destroy(&ads);
1719 return 0;
1722 int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
1724 ADS_STRUCT *ads;
1725 char *host_principal;
1726 fstring my_name;
1727 ADS_STATUS ret;
1729 if (!secrets_init()) {
1730 DEBUG(1,("Failed to initialise secrets database\n"));
1731 return -1;
1734 net_use_krb_machine_account(c);
1736 use_in_memory_ccache();
1738 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
1739 return -1;
1742 fstrcpy(my_name, global_myname());
1743 strlower_m(my_name);
1744 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1745 d_printf("Changing password for principal: %s\n", host_principal);
1747 ret = ads_change_trust_account_password(ads, host_principal);
1749 if (!ADS_ERR_OK(ret)) {
1750 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1751 ads_destroy(&ads);
1752 SAFE_FREE(host_principal);
1753 return -1;
1756 d_printf("Password change for principal %s succeeded.\n", host_principal);
1758 if (lp_use_kerberos_keytab()) {
1759 d_printf("Attempting to update system keytab with new password.\n");
1760 if (ads_keytab_create_default(ads)) {
1761 d_printf("Failed to update system keytab.\n");
1765 ads_destroy(&ads);
1766 SAFE_FREE(host_principal);
1768 return 0;
1772 help for net ads search
1774 static int net_ads_search_usage(struct net_context *c, int argc, const char **argv)
1776 d_printf(
1777 "\nnet ads search <expression> <attributes...>\n"\
1778 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1779 "The expression is a standard LDAP search expression, and the\n"\
1780 "attributes are a list of LDAP fields to show in the results\n\n"\
1781 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1783 net_common_flags_usage(c, argc, argv);
1784 return -1;
1789 general ADS search function. Useful in diagnosing problems in ADS
1791 static int net_ads_search(struct net_context *c, int argc, const char **argv)
1793 ADS_STRUCT *ads;
1794 ADS_STATUS rc;
1795 const char *ldap_exp;
1796 const char **attrs;
1797 LDAPMessage *res = NULL;
1799 if (argc < 1) {
1800 return net_ads_search_usage(c, argc, argv);
1803 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
1804 return -1;
1807 ldap_exp = argv[0];
1808 attrs = (argv + 1);
1810 rc = ads_do_search_all(ads, ads->config.bind_path,
1811 LDAP_SCOPE_SUBTREE,
1812 ldap_exp, attrs, &res);
1813 if (!ADS_ERR_OK(rc)) {
1814 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1815 ads_destroy(&ads);
1816 return -1;
1819 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1821 /* dump the results */
1822 ads_dump(ads, res);
1824 ads_msgfree(ads, res);
1825 ads_destroy(&ads);
1827 return 0;
1832 help for net ads search
1834 static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv)
1836 d_printf(
1837 "\nnet ads dn <dn> <attributes...>\n"\
1838 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1839 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1840 "to show in the results\n\n"\
1841 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1842 "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
1844 net_common_flags_usage(c, argc, argv);
1845 return -1;
1850 general ADS search function. Useful in diagnosing problems in ADS
1852 static int net_ads_dn(struct net_context *c, int argc, const char **argv)
1854 ADS_STRUCT *ads;
1855 ADS_STATUS rc;
1856 const char *dn;
1857 const char **attrs;
1858 LDAPMessage *res = NULL;
1860 if (argc < 1) {
1861 return net_ads_dn_usage(c, argc, argv);
1864 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
1865 return -1;
1868 dn = argv[0];
1869 attrs = (argv + 1);
1871 rc = ads_do_search_all(ads, dn,
1872 LDAP_SCOPE_BASE,
1873 "(objectclass=*)", attrs, &res);
1874 if (!ADS_ERR_OK(rc)) {
1875 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1876 ads_destroy(&ads);
1877 return -1;
1880 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1882 /* dump the results */
1883 ads_dump(ads, res);
1885 ads_msgfree(ads, res);
1886 ads_destroy(&ads);
1888 return 0;
1892 help for net ads sid search
1894 static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv)
1896 d_printf(
1897 "\nnet ads sid <sid> <attributes...>\n"\
1898 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1899 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1900 "to show in the results\n\n"\
1901 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1903 net_common_flags_usage(c, argc, argv);
1904 return -1;
1909 general ADS search function. Useful in diagnosing problems in ADS
1911 static int net_ads_sid(struct net_context *c, int argc, const char **argv)
1913 ADS_STRUCT *ads;
1914 ADS_STATUS rc;
1915 const char *sid_string;
1916 const char **attrs;
1917 LDAPMessage *res = NULL;
1918 DOM_SID sid;
1920 if (argc < 1) {
1921 return net_ads_sid_usage(c, argc, argv);
1924 if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
1925 return -1;
1928 sid_string = argv[0];
1929 attrs = (argv + 1);
1931 if (!string_to_sid(&sid, sid_string)) {
1932 d_fprintf(stderr, "could not convert sid\n");
1933 ads_destroy(&ads);
1934 return -1;
1937 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1938 if (!ADS_ERR_OK(rc)) {
1939 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1940 ads_destroy(&ads);
1941 return -1;
1944 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1946 /* dump the results */
1947 ads_dump(ads, res);
1949 ads_msgfree(ads, res);
1950 ads_destroy(&ads);
1952 return 0;
1956 static int net_ads_keytab_usage(struct net_context *c, int argc, const char **argv)
1958 d_printf(
1959 "net ads keytab <COMMAND>\n"\
1960 "<COMMAND> can be either:\n"\
1961 " ADD Adds new service principal\n"\
1962 " CREATE Creates a fresh keytab\n"\
1963 " FLUSH Flushes out all keytab entries\n"\
1964 " HELP Prints this help message\n"\
1965 " LIST List the keytab\n"\
1966 "The ADD and LIST command will take arguments, the other commands\n"\
1967 "will not take any arguments. The arguments given to ADD\n"\
1968 "should be a list of principals to add. For example, \n"\
1969 " net ads keytab add srv1 srv2\n"\
1970 "will add principals for the services srv1 and srv2 to the\n"\
1971 "system's keytab.\n"\
1972 "The LIST command takes a keytabname.\n"\
1973 "\n"
1975 return -1;
1978 static int net_ads_keytab_flush(struct net_context *c, int argc, const char **argv)
1980 int ret;
1981 ADS_STRUCT *ads;
1983 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
1984 return -1;
1986 ret = ads_keytab_flush(ads);
1987 ads_destroy(&ads);
1988 return ret;
1991 static int net_ads_keytab_add(struct net_context *c, int argc, const char **argv)
1993 int i;
1994 int ret = 0;
1995 ADS_STRUCT *ads;
1997 d_printf("Processing principals to add...\n");
1998 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
1999 return -1;
2001 for (i = 0; i < argc; i++) {
2002 ret |= ads_keytab_add_entry(ads, argv[i]);
2004 ads_destroy(&ads);
2005 return ret;
2008 static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv)
2010 ADS_STRUCT *ads;
2011 int ret;
2013 if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
2014 return -1;
2016 ret = ads_keytab_create_default(ads);
2017 ads_destroy(&ads);
2018 return ret;
2021 static int net_ads_keytab_list(struct net_context *c, int argc, const char **argv)
2023 const char *keytab = NULL;
2025 if (argc >= 1) {
2026 keytab = argv[0];
2029 return ads_keytab_list(keytab);
2033 int net_ads_keytab(struct net_context *c, int argc, const char **argv)
2035 struct functable func[] = {
2036 {"ADD", net_ads_keytab_add},
2037 {"CREATE", net_ads_keytab_create},
2038 {"FLUSH", net_ads_keytab_flush},
2039 {"HELP", net_ads_keytab_usage},
2040 {"LIST", net_ads_keytab_list},
2041 {NULL, NULL}
2044 if (!lp_use_kerberos_keytab()) {
2045 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
2046 use keytab functions.\n");
2049 return net_run_function(c, argc, argv, func, net_ads_keytab_usage);
2052 static int net_ads_kerberos_usage(struct net_context *c, int argc, const char **argv)
2054 d_printf(
2055 "net ads kerberos <COMMAND>\n"\
2056 "<COMMAND> can be either:\n"\
2057 " RENEW Renew TGT from existing credential cache\n"\
2058 " PAC Dumps the Kerberos PAC\n"\
2059 " KINIT Retrieve Ticket Granting Ticket (TGT)\n"\
2060 "\n"
2063 return -1;
2066 static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv)
2068 int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
2069 if (ret) {
2070 d_printf("failed to renew kerberos ticket: %s\n",
2071 error_message(ret));
2073 return ret;
2076 static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv)
2078 struct PAC_DATA *pac = NULL;
2079 struct PAC_LOGON_INFO *info = NULL;
2080 TALLOC_CTX *mem_ctx = NULL;
2081 NTSTATUS status;
2082 int ret = -1;
2084 mem_ctx = talloc_init("net_ads_kerberos_pac");
2085 if (!mem_ctx) {
2086 goto out;
2089 c->opt_password = net_prompt_pass(c, c->opt_user_name);
2091 status = kerberos_return_pac(mem_ctx,
2092 c->opt_user_name,
2093 c->opt_password,
2095 NULL,
2096 NULL,
2097 NULL,
2098 true,
2099 true,
2100 2592000, /* one month */
2101 &pac);
2102 if (!NT_STATUS_IS_OK(status)) {
2103 d_printf("failed to query kerberos PAC: %s\n",
2104 nt_errstr(status));
2105 goto out;
2108 info = get_logon_info_from_pac(pac);
2109 if (info) {
2110 const char *s;
2111 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
2112 d_printf("The Pac: %s\n", s);
2115 ret = 0;
2116 out:
2117 TALLOC_FREE(mem_ctx);
2118 return ret;
2121 static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv)
2123 TALLOC_CTX *mem_ctx = NULL;
2124 int ret = -1;
2125 NTSTATUS status;
2127 mem_ctx = talloc_init("net_ads_kerberos_kinit");
2128 if (!mem_ctx) {
2129 goto out;
2132 c->opt_password = net_prompt_pass(c, c->opt_user_name);
2134 ret = kerberos_kinit_password_ext(c->opt_user_name,
2135 c->opt_password,
2137 NULL,
2138 NULL,
2139 NULL,
2140 true,
2141 true,
2142 2592000, /* one month */
2143 &status);
2144 if (ret) {
2145 d_printf("failed to kinit password: %s\n",
2146 nt_errstr(status));
2148 out:
2149 return ret;
2152 int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
2154 struct functable func[] = {
2155 {"KINIT", net_ads_kerberos_kinit},
2156 {"RENEW", net_ads_kerberos_renew},
2157 {"PAC", net_ads_kerberos_pac},
2158 {"HELP", net_ads_kerberos_usage},
2159 {NULL, NULL}
2162 return net_run_function(c, argc, argv, func, net_ads_kerberos_usage);
2166 int net_ads_usage(struct net_context *c, int argc, const char **argv)
2168 struct functable func[] = {
2169 {"USER", net_ads_user_usage},
2170 {"GROUP", net_ads_group_usage},
2171 {"PRINTER", net_ads_printer_usage},
2172 {"SEARCH", net_ads_search_usage},
2173 {"INFO", net_ads_info},
2174 {"JOIN", net_ads_join_usage},
2175 {"DNS", net_ads_dns_usage},
2176 {"LEAVE", net_ads_leave},
2177 {"STATUS", net_ads_status},
2178 {"PASSWORD", net_ads_password},
2179 {"CHANGETRUSTPW", net_ads_changetrustpw},
2180 {NULL, NULL}
2183 return net_run_function(c, argc, argv, func, net_ads_help);
2186 int net_ads(struct net_context *c, int argc, const char **argv)
2188 struct functable func[] = {
2189 {"INFO", net_ads_info},
2190 {"JOIN", net_ads_join},
2191 {"TESTJOIN", net_ads_testjoin},
2192 {"LEAVE", net_ads_leave},
2193 {"STATUS", net_ads_status},
2194 {"USER", net_ads_user},
2195 {"GROUP", net_ads_group},
2196 {"DNS", net_ads_dns},
2197 {"PASSWORD", net_ads_password},
2198 {"CHANGETRUSTPW", net_ads_changetrustpw},
2199 {"PRINTER", net_ads_printer},
2200 {"SEARCH", net_ads_search},
2201 {"DN", net_ads_dn},
2202 {"SID", net_ads_sid},
2203 {"WORKGROUP", net_ads_workgroup},
2204 {"LOOKUP", net_ads_lookup},
2205 {"KEYTAB", net_ads_keytab},
2206 {"GPO", net_ads_gpo},
2207 {"KERBEROS", net_ads_kerberos},
2208 {"HELP", net_ads_help},
2209 {NULL, NULL}
2212 return net_run_function(c, argc, argv, func, net_ads_help);
2215 #else
2217 static int net_ads_noads(void)
2219 d_fprintf(stderr, "ADS support not compiled in\n");
2220 return -1;
2223 int net_ads_keytab(struct net_context *c, int argc, const char **argv)
2225 return net_ads_noads();
2228 int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
2230 return net_ads_noads();
2233 int net_ads_usage(struct net_context *c, int argc, const char **argv)
2235 return net_ads_noads();
2238 int net_ads_help(struct net_context *c, int argc, const char **argv)
2240 return net_ads_noads();
2243 int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
2245 return net_ads_noads();
2248 int net_ads_join(struct net_context *c, int argc, const char **argv)
2250 return net_ads_noads();
2253 int net_ads_user(struct net_context *c, int argc, const char **argv)
2255 return net_ads_noads();
2258 int net_ads_group(struct net_context *c, int argc, const char **argv)
2260 return net_ads_noads();
2263 /* this one shouldn't display a message */
2264 int net_ads_check(struct net_context *c)
2266 return -1;
2269 int net_ads_check_our_domain(struct net_context *c)
2271 return -1;
2274 int net_ads(struct net_context *c, int argc, const char **argv)
2276 return net_ads_usage(c, argc, argv);
2279 #endif /* WITH_ADS */