2 Samba Unix/Linux SMB client library
4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
5 Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
6 Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
7 Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "utils/net.h"
27 /* Macro for checking RPC error codes to make things more readable */
30 #define CHECK_RPC_ERR(rpc, msg) \
31 if (!NT_STATUS_IS_OK(result = rpc)) { \
32 DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
36 #define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
37 if (!NT_STATUS_IS_OK(result = rpc)) { \
38 DEBUG(0, debug_args); \
45 int net_ads_usage(int argc, const char **argv)
48 "\nnet ads join <org_unit>"\
49 "\n\tjoins the local machine to a ADS realm\n"\
51 "\n\tremoves the local machine from a ADS realm\n"\
53 "\n\ttests that an exiting join is OK\n"\
55 "\n\tlist, add, or delete users in the realm\n"\
57 "\n\tlist, add, or delete groups in the realm\n"\
59 "\n\tshows some info on the server\n"\
61 "\n\tdump the machine account details to stdout\n"
63 "\n\tperform a CLDAP search on the server\n"
64 "\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
65 "\n\tchange a user's password using an admin account"\
66 "\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
67 "\nnet ads changetrustpw"\
68 "\n\tchange the trust account password of this machine in the AD tree\n"\
69 "\nnet ads printer [info | publish | remove] <printername> <servername>"\
70 "\n\t lookup, add, or remove directory entry for a printer\n"\
72 "\n\tperform a raw LDAP search and dump the results\n"
74 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
76 "\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
78 "\n\tcreates and updates the kerberos system keytab file\n"
83 /* when we do not have sufficient input parameters to contact a remote domain
84 * we always fall back to our own realm - Guenther*/
86 static const char *assume_own_realm(void)
88 if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
96 do a cldap netlogon query
98 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
100 struct cldap_netlogon_reply reply;
102 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
103 d_fprintf(stderr, "CLDAP query failed!\n");
107 d_printf("Information for Domain Controller: %s\n\n",
108 inet_ntoa(ads->ldap_ip));
110 d_printf("Response Type: ");
111 switch (reply.type) {
112 case SAMLOGON_AD_UNK_R:
113 d_printf("SAMLOGON\n");
116 d_printf("SAMLOGON_USER\n");
119 d_printf("0x%x\n", reply.type);
122 d_printf("GUID: %s\n",
123 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid)));
126 "\tIs a GC of the forest: %s\n"
127 "\tIs an LDAP server: %s\n"
128 "\tSupports DS: %s\n"
129 "\tIs running a KDC: %s\n"
130 "\tIs running time services: %s\n"
131 "\tIs the closest DC: %s\n"
132 "\tIs writable: %s\n"
133 "\tHas a hardware clock: %s\n"
134 "\tIs a non-domain NC serviced by LDAP server: %s\n",
135 (reply.flags & ADS_PDC) ? "yes" : "no",
136 (reply.flags & ADS_GC) ? "yes" : "no",
137 (reply.flags & ADS_LDAP) ? "yes" : "no",
138 (reply.flags & ADS_DS) ? "yes" : "no",
139 (reply.flags & ADS_KDC) ? "yes" : "no",
140 (reply.flags & ADS_TIMESERV) ? "yes" : "no",
141 (reply.flags & ADS_CLOSEST) ? "yes" : "no",
142 (reply.flags & ADS_WRITABLE) ? "yes" : "no",
143 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
144 (reply.flags & ADS_NDNC) ? "yes" : "no");
146 printf("Forest:\t\t\t%s\n", reply.forest);
147 printf("Domain:\t\t\t%s\n", reply.domain);
148 printf("Domain Controller:\t%s\n", reply.hostname);
150 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
151 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
153 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
154 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
156 printf("Site Name:\t\t%s\n", reply.site_name);
157 printf("Site Name (2):\t\t%s\n", reply.site_name_2);
159 d_printf("NT Version: %d\n", reply.version);
160 d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
161 d_printf("LM20 Token: %.2x\n", reply.lm20_token);
168 this implements the CLDAP based netlogon lookup requests
169 for finding the domain controller of a ADS domain
171 static int net_ads_lookup(int argc, const char **argv)
175 const char *realm = assume_own_realm();
177 ads = ads_init(realm, opt_target_workgroup, opt_host);
179 ads->auth.flags |= ADS_AUTH_NO_BIND;
182 status = ads_connect(ads);
183 if (!ADS_ERR_OK(status) || !ads) {
184 d_fprintf(stderr, "Didn't find the cldap server!\n");
188 if (!ads->config.realm) {
189 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
190 ads->ldap_port = 389;
193 return net_ads_cldap_netlogon(ads);
198 static int net_ads_info(int argc, const char **argv)
201 const char *realm = assume_own_realm();
203 if ( (ads = ads_init(realm, opt_target_workgroup, opt_host)) != NULL ) {
204 ads->auth.flags |= ADS_AUTH_NO_BIND;
209 if (!ads || !ads->config.realm) {
210 d_fprintf(stderr, "Didn't find the ldap server!\n");
214 /* Try to set the server's current time since we didn't do a full
215 TCP LDAP session initially */
217 if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
218 d_fprintf( stderr, "Failed to get server's current time!\n");
221 d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
222 d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
223 d_printf("Realm: %s\n", ads->config.realm);
224 d_printf("Bind Path: %s\n", ads->config.bind_path);
225 d_printf("LDAP port: %d\n", ads->ldap_port);
226 d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
228 d_printf("KDC server: %s\n", ads->auth.kdc_server );
229 d_printf("Server time offset: %d\n", ads->auth.time_offset );
234 static void use_in_memory_ccache(void) {
235 /* Use in-memory credentials cache so we do not interfere with
236 * existing credentials */
237 setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
240 static ADS_STRUCT *ads_startup(BOOL only_own_domain)
244 BOOL need_password = False;
245 BOOL second_time = False;
247 const char *realm = NULL;
249 /* lp_realm() should be handled by a command line param,
250 However, the join requires that realm be set in smb.conf
251 and compares our realm with the remote server's so this is
252 ok until someone needs more flexibility */
254 if (only_own_domain) {
258 ads = ads_init(realm, opt_target_workgroup, opt_host);
260 if (!opt_user_name) {
261 opt_user_name = "administrator";
264 if (opt_user_specified) {
265 need_password = True;
269 if (!opt_password && need_password && !opt_machine_pass) {
271 asprintf(&prompt,"%s's password: ", opt_user_name);
272 opt_password = getpass(prompt);
277 use_in_memory_ccache();
278 ads->auth.password = smb_xstrdup(opt_password);
281 ads->auth.user_name = smb_xstrdup(opt_user_name);
284 * If the username is of the form "name@realm",
285 * extract the realm and convert to upper case.
286 * This is only used to establish the connection.
288 if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
290 ads->auth.realm = smb_xstrdup(cp);
291 strupper_m(ads->auth.realm);
294 status = ads_connect(ads);
296 if (!ADS_ERR_OK(status)) {
297 if (!need_password && !second_time) {
298 need_password = True;
302 DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
312 Check to see if connection can be made via ads.
313 ads_startup() stores the password in opt_password if it needs to so
314 that rpc or rap can use it without re-prompting.
316 static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
321 if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
325 ads->auth.flags |= ADS_AUTH_NO_BIND;
327 status = ads_connect(ads);
328 if ( !ADS_ERR_OK(status) ) {
336 int net_ads_check_our_domain(void)
338 return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
341 int net_ads_check(void)
343 return net_ads_check_int(NULL, opt_workgroup, opt_host);
346 determine the netbios workgroup name for a domain
348 static int net_ads_workgroup(int argc, const char **argv)
352 const char *realm = assume_own_realm();
353 struct cldap_netlogon_reply reply;
355 ads = ads_init(realm, opt_target_workgroup, opt_host);
357 ads->auth.flags |= ADS_AUTH_NO_BIND;
360 status = ads_connect(ads);
361 if (!ADS_ERR_OK(status) || !ads) {
362 d_fprintf(stderr, "Didn't find the cldap server!\n");
366 if (!ads->config.realm) {
367 ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
368 ads->ldap_port = 389;
371 if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
372 d_fprintf(stderr, "CLDAP query failed!\n");
376 d_printf("Workgroup: %s\n", reply.netbios_domain);
385 static BOOL usergrp_display(char *field, void **values, void *data_area)
387 char **disp_fields = (char **) data_area;
389 if (!field) { /* must be end of record */
390 if (disp_fields[0]) {
391 if (!strchr_m(disp_fields[0], '$')) {
393 d_printf("%-21.21s %s\n",
394 disp_fields[0], disp_fields[1]);
396 d_printf("%s\n", disp_fields[0]);
399 SAFE_FREE(disp_fields[0]);
400 SAFE_FREE(disp_fields[1]);
403 if (!values) /* must be new field, indicate string field */
405 if (StrCaseCmp(field, "sAMAccountName") == 0) {
406 disp_fields[0] = SMB_STRDUP((char *) values[0]);
408 if (StrCaseCmp(field, "description") == 0)
409 disp_fields[1] = SMB_STRDUP((char *) values[0]);
413 static int net_ads_user_usage(int argc, const char **argv)
415 return net_help_user(argc, argv);
418 static int ads_user_add(int argc, const char **argv)
426 if (argc < 1) return net_ads_user_usage(argc, argv);
428 if (!(ads = ads_startup(False))) {
432 status = ads_find_user_acct(ads, &res, argv[0]);
434 if (!ADS_ERR_OK(status)) {
435 d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
439 if (ads_count_replies(ads, res)) {
440 d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
444 if (opt_container == NULL) {
445 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
448 status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
450 if (!ADS_ERR_OK(status)) {
451 d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
456 /* if no password is to be set, we're done */
458 d_printf("User %s added\n", argv[0]);
463 /* try setting the password */
464 asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
465 status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
466 ads->auth.time_offset);
468 if (ADS_ERR_OK(status)) {
469 d_printf("User %s added\n", argv[0]);
474 /* password didn't set, delete account */
475 d_fprintf(stderr, "Could not add user %s. Error setting password %s\n",
476 argv[0], ads_errstr(status));
477 ads_msgfree(ads, res);
478 status=ads_find_user_acct(ads, &res, argv[0]);
479 if (ADS_ERR_OK(status)) {
480 userdn = ads_get_dn(ads, res);
481 ads_del_dn(ads, userdn);
482 ads_memfree(ads, userdn);
487 ads_msgfree(ads, res);
492 static int ads_user_info(int argc, const char **argv)
497 const char *attrs[] = {"memberOf", NULL};
498 char *searchstring=NULL;
503 return net_ads_user_usage(argc, argv);
506 escaped_user = escape_ldap_string_alloc(argv[0]);
509 d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
513 if (!(ads = ads_startup(False))) {
514 SAFE_FREE(escaped_user);
518 asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
519 rc = ads_search(ads, &res, searchstring, attrs);
520 safe_free(searchstring);
522 if (!ADS_ERR_OK(rc)) {
523 d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
525 SAFE_FREE(escaped_user);
529 grouplist = ldap_get_values((LDAP *)ads->ld,
530 (LDAPMessage *)res, "memberOf");
535 for (i=0;grouplist[i];i++) {
536 groupname = ldap_explode_dn(grouplist[i], 1);
537 d_printf("%s\n", groupname[0]);
538 ldap_value_free(groupname);
540 ldap_value_free(grouplist);
543 ads_msgfree(ads, res);
545 SAFE_FREE(escaped_user);
549 static int ads_user_delete(int argc, const char **argv)
557 return net_ads_user_usage(argc, argv);
560 if (!(ads = ads_startup(False))) {
564 rc = ads_find_user_acct(ads, &res, argv[0]);
565 if (!ADS_ERR_OK(rc)) {
566 DEBUG(0, ("User %s does not exist\n", argv[0]));
570 userdn = ads_get_dn(ads, res);
571 ads_msgfree(ads, res);
572 rc = ads_del_dn(ads, userdn);
573 ads_memfree(ads, userdn);
574 if (!ADS_ERR_OK(rc)) {
575 d_printf("User %s deleted\n", argv[0]);
579 d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
585 int net_ads_user(int argc, const char **argv)
587 struct functable func[] = {
588 {"ADD", ads_user_add},
589 {"INFO", ads_user_info},
590 {"DELETE", ads_user_delete},
595 const char *shortattrs[] = {"sAMAccountName", NULL};
596 const char *longattrs[] = {"sAMAccountName", "description", NULL};
597 char *disp_fields[2] = {NULL, NULL};
600 if (!(ads = ads_startup(False))) {
604 if (opt_long_list_entries)
605 d_printf("\nUser name Comment"\
606 "\n-----------------------------\n");
608 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
610 "(objectCategory=user)",
611 opt_long_list_entries ? longattrs :
612 shortattrs, usergrp_display,
615 return ADS_ERR_OK(rc) ? 0 : -1;
618 return net_run_function(argc, argv, func, net_ads_user_usage);
621 static int net_ads_group_usage(int argc, const char **argv)
623 return net_help_group(argc, argv);
626 static int ads_group_add(int argc, const char **argv)
634 return net_ads_group_usage(argc, argv);
637 if (!(ads = ads_startup(False))) {
641 status = ads_find_user_acct(ads, &res, argv[0]);
643 if (!ADS_ERR_OK(status)) {
644 d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
648 if (ads_count_replies(ads, res)) {
649 d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
650 ads_msgfree(ads, res);
654 if (opt_container == NULL) {
655 opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
658 status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
660 if (ADS_ERR_OK(status)) {
661 d_printf("Group %s added\n", argv[0]);
664 d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
670 ads_msgfree(ads, res);
675 static int ads_group_delete(int argc, const char **argv)
683 return net_ads_group_usage(argc, argv);
686 if (!(ads = ads_startup(False))) {
690 rc = ads_find_user_acct(ads, &res, argv[0]);
691 if (!ADS_ERR_OK(rc)) {
692 DEBUG(0, ("Group %s does not exist\n", argv[0]));
696 groupdn = ads_get_dn(ads, res);
697 ads_msgfree(ads, res);
698 rc = ads_del_dn(ads, groupdn);
699 ads_memfree(ads, groupdn);
700 if (!ADS_ERR_OK(rc)) {
701 d_printf("Group %s deleted\n", argv[0]);
705 d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
711 int net_ads_group(int argc, const char **argv)
713 struct functable func[] = {
714 {"ADD", ads_group_add},
715 {"DELETE", ads_group_delete},
720 const char *shortattrs[] = {"sAMAccountName", NULL};
721 const char *longattrs[] = {"sAMAccountName", "description", NULL};
722 char *disp_fields[2] = {NULL, NULL};
725 if (!(ads = ads_startup(False))) {
729 if (opt_long_list_entries)
730 d_printf("\nGroup name Comment"\
731 "\n-----------------------------\n");
732 rc = ads_do_search_all_fn(ads, ads->config.bind_path,
734 "(objectCategory=group)",
735 opt_long_list_entries ? longattrs :
736 shortattrs, usergrp_display,
740 return ADS_ERR_OK(rc) ? 0 : -1;
742 return net_run_function(argc, argv, func, net_ads_group_usage);
745 static int net_ads_status(int argc, const char **argv)
751 if (!(ads = ads_startup(True))) {
755 rc = ads_find_machine_acct(ads, &res, global_myname());
756 if (!ADS_ERR_OK(rc)) {
757 d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
762 if (ads_count_replies(ads, res) == 0) {
763 d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
773 /*******************************************************************
774 Leave an AD domain. Windows XP disables the machine account.
775 We'll try the same. The old code would do an LDAP delete.
776 That only worked using the machine creds because added the machine
777 with full control to the computer object's ACL.
778 *******************************************************************/
779 static int net_ads_leave(int argc, const char **argv)
781 ADS_STRUCT *ads = NULL;
783 struct cli_state *cli = NULL;
785 DOM_SID *dom_sid = NULL;
787 if (!secrets_init()) {
788 DEBUG(1,("Failed to initialise secrets database\n"));
792 if (!(ctx = talloc_init("net_ads_leave"))) {
793 DEBUG(0, ("Could not initialise talloc context\n"));
797 /* The finds a DC and takes care of getting the
798 user creds if necessary */
800 if (!(ads = ads_startup(True))) {
804 /* make RPC calls here */
806 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip,
807 ads->config.ldap_server_name)) )
812 saf_store( cli->server_domain, cli->desthost );
814 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
818 if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
819 d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
820 global_myname(), ads->config.realm);
824 d_printf("Disabled account for '%s' in realm '%s'\n",
825 global_myname(), ads->config.realm);
839 static int net_ads_join_ok(void)
841 ADS_STRUCT *ads = NULL;
843 if (!secrets_init()) {
844 DEBUG(1,("Failed to initialise secrets database\n"));
848 net_use_machine_password();
850 if (!(ads = ads_startup(True))) {
859 check that an existing join is OK
861 int net_ads_testjoin(int argc, const char **argv)
863 use_in_memory_ccache();
865 /* Display success or failure */
866 if (net_ads_join_ok() != 0) {
867 fprintf(stderr,"Join to domain is not valid\n");
871 printf("Join is OK\n");
875 /*******************************************************************
876 Simple configu checks before beginning the join
877 ********************************************************************/
879 static int check_ads_config( void )
881 if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
882 d_printf("Host is not configured as a member server.\n");
886 if (strlen(global_myname()) > 15) {
887 d_printf("Our netbios name can be at most 15 chars long, "
888 "\"%s\" is %u chars long\n",
889 global_myname(), (unsigned int)strlen(global_myname()));
893 if ( lp_security() == SEC_ADS && !*lp_realm()) {
894 d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
895 "join to succeed.\n");
899 if (!secrets_init()) {
900 DEBUG(1,("Failed to initialise secrets database\n"));
907 /*******************************************************************
909 ********************************************************************/
911 static int net_join_domain( TALLOC_CTX *ctx, const char *servername,
912 struct in_addr *ip, DOM_SID **dom_sid, const char *password )
915 struct cli_state *cli = NULL;
917 if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
920 saf_store( cli->server_domain, cli->desthost );
922 if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
925 if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid,
926 password, ND_TYPE_AD )) )
940 /*******************************************************************
941 Set a machines dNSHostName and servicePrincipalName attributes
942 ********************************************************************/
944 static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
946 ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
947 char *host_upn, *new_dn;
949 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
952 LDAPMessage *res = NULL;
953 char *dn_string = NULL;
954 const char *machine_name = global_myname();
957 if ( !machine_name ) {
958 return ADS_ERROR(LDAP_NO_MEMORY);
963 status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
964 if (!ADS_ERR_OK(status))
967 if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
968 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
969 return ADS_ERROR(LDAP_NO_MEMORY);
972 if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
973 DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
977 new_dn = talloc_strdup(ctx, dn_string);
978 ads_memfree(ads_s, dn_string);
980 return ADS_ERROR(LDAP_NO_MEMORY);
983 /* Windows only creates HOST/shortname & HOST/fqdn. We create
984 the UPN as well so that 'kinit -k' will work. You can only
985 request a TGT for entries with a UPN in AD. */
987 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) )
990 servicePrincipalName[0] = psp;
992 name_to_fqdn(my_fqdn, machine_name);
994 if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) )
996 servicePrincipalName[1] = psp;
998 if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
1001 /* now do the mods */
1003 if (!(mods = ads_init_mods(ctx))) {
1007 /* fields of primary importance */
1009 ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
1010 ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1012 status = ads_gen_mod(ads_s, new_dn, mods);
1015 ads_msgfree(ads_s, res);
1021 /*******************************************************************
1022 join a domain using ADS (LDAP mods)
1023 ********************************************************************/
1025 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
1027 ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
1029 LDAPMessage *res = NULL;
1031 ou_str = ads_ou_string(ads, ou);
1032 asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
1035 rc = ads_search_dn(ads, &res, dn, NULL);
1036 ads_msgfree(ads, res);
1038 if (ADS_ERR_OK(rc)) {
1039 /* Attempt to create the machine account and bail if this fails.
1040 Assume that the admin wants exactly what they requested */
1042 rc = ads_create_machine_acct( ads, global_myname(), dn );
1043 if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
1053 /************************************************************************
1054 ************************************************************************/
1056 static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
1062 LDAPMessage *res = NULL;
1063 const char *machine_name = global_myname();
1065 status = ads_domain_func_level( ads, &domain_func );
1066 if ( !ADS_ERR_OK(status) ) {
1067 DEBUG(2,("Failed to determine domain functional level!\n"));
1071 /* go ahead and setup the default salt */
1073 if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
1074 DEBUG(0,("net_derive_salting_principal: failed to obtain stanard DES salt\n"));
1078 fstrcpy( salt, std_salt );
1079 SAFE_FREE( std_salt );
1081 /* if it's a Windows functional domain, we have to look for the UPN */
1083 if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
1087 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
1088 if (!ADS_ERR_OK(status)) {
1092 if ( (count = ads_count_replies(ads, res)) != 1 ) {
1093 DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
1097 upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
1099 fstrcpy( salt, upn );
1102 ads_msgfree(ads, res);
1105 return kerberos_secrets_store_des_salt( salt );
1108 /*******************************************************************
1109 join a domain using ADS (LDAP mods)
1110 ********************************************************************/
1112 int net_ads_join(int argc, const char **argv)
1116 char *machine_account = NULL;
1117 const char *short_domain_name = NULL;
1118 char *tmp_password, *password;
1119 struct cldap_netlogon_reply cldap_reply;
1121 DOM_SID *domain_sid = NULL;
1123 if ( check_ads_config() != 0 ) {
1124 d_fprintf(stderr, "Invalid configuration. Exiting....\n");
1128 if ( (ads = ads_startup(True)) == NULL ) {
1132 if (strcmp(ads->config.realm, lp_realm()) != 0) {
1133 d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
1134 "(%s) DO NOT match. Aborting join\n", ads->config.realm,
1140 if (!(ctx = talloc_init("net_ads_join"))) {
1141 DEBUG(0, ("Could not initialise talloc context\n"));
1145 /* If we were given an OU, try to create the machine in the OU account
1146 first and then do the normal RPC join */
1149 status = net_precreate_machine_acct( ads, argv[0] );
1150 if ( !ADS_ERR_OK(status) ) {
1151 d_fprintf( stderr, "Failed to pre-create the machine object "
1152 "in OU %s.\n", argv[0]);
1153 ads_destroy( &ads );
1158 /* Do the domain join here */
1160 tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
1161 password = talloc_strdup(ctx, tmp_password);
1163 if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
1164 d_fprintf(stderr, "Failed to join domain!\n");
1168 /* Check the short name of the domain */
1170 ZERO_STRUCT( cldap_reply );
1172 if ( ads_cldap_netlogon( ads->config.ldap_server_name,
1173 ads->server.realm, &cldap_reply ) )
1175 short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
1176 if ( !strequal(lp_workgroup(), short_domain_name) ) {
1177 d_printf("The workgroup in smb.conf does not match the short\n");
1178 d_printf("domain name obtained from the server.\n");
1179 d_printf("Using the name [%s] from the server.\n", short_domain_name);
1180 d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
1183 short_domain_name = lp_workgroup();
1186 d_printf("Using short domain name -- %s\n", short_domain_name);
1188 /* HACK ALERT! Store the sid and password under both the lp_workgroup()
1189 value from smb.conf and the string returned from the server. The former is
1190 neede to bootstrap winbindd's first connection to the DC to get the real
1191 short domain name --jerry */
1193 if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
1194 || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
1200 /* Verify that everything is ok */
1202 if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
1203 d_fprintf(stderr, "Failed to verify membership in domain!\n");
1207 /* create the dNSHostName & servicePrincipalName values */
1209 status = net_set_machine_spn( ctx, ads );
1210 if ( !ADS_ERR_OK(status) ) {
1211 d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
1212 d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
1213 d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
1218 if ( !net_derive_salting_principal( ctx, ads ) ) {
1219 DEBUG(1,("Failed to determine salting principal\n"));
1224 /* Now build the keytab, using the same ADS connection */
1225 if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
1226 DEBUG(1,("Error creating host keytab!\n"));
1229 d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
1231 SAFE_FREE(machine_account);
1238 /*******************************************************************
1239 ********************************************************************/
1241 int net_ads_printer_usage(int argc, const char **argv)
1244 "\nnet ads printer search <printer>"
1245 "\n\tsearch for a printer in the directory\n"
1246 "\nnet ads printer info <printer> <server>"
1247 "\n\tlookup info in directory for printer on server"
1248 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
1249 "\nnet ads printer publish <printername>"
1250 "\n\tpublish printer in directory"
1251 "\n\t(note: printer name is required)\n"
1252 "\nnet ads printer remove <printername>"
1253 "\n\tremove printer from directory"
1254 "\n\t(note: printer name is required)\n");
1258 /*******************************************************************
1259 ********************************************************************/
1261 static int net_ads_printer_search(int argc, const char **argv)
1267 if (!(ads = ads_startup(False))) {
1271 rc = ads_find_printers(ads, &res);
1273 if (!ADS_ERR_OK(rc)) {
1274 d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
1275 ads_msgfree(ads, res);
1280 if (ads_count_replies(ads, res) == 0) {
1281 d_fprintf(stderr, "No results found\n");
1282 ads_msgfree(ads, res);
1288 ads_msgfree(ads, res);
1293 static int net_ads_printer_info(int argc, const char **argv)
1297 const char *servername, *printername;
1300 if (!(ads = ads_startup(False))) {
1305 printername = argv[0];
1311 servername = argv[1];
1313 servername = global_myname();
1316 rc = ads_find_printer_on_server(ads, &res, printername, servername);
1318 if (!ADS_ERR_OK(rc)) {
1319 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1320 ads_msgfree(ads, res);
1325 if (ads_count_replies(ads, res) == 0) {
1326 d_fprintf(stderr, "Printer '%s' not found\n", printername);
1327 ads_msgfree(ads, res);
1333 ads_msgfree(ads, res);
1339 void do_drv_upgrade_printer(int msg_type, struct process_id src,
1340 void *buf, size_t len)
1345 static int net_ads_printer_publish(int argc, const char **argv)
1349 const char *servername, *printername;
1350 struct cli_state *cli;
1351 struct rpc_pipe_client *pipe_hnd;
1352 struct in_addr server_ip;
1354 TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
1355 ADS_MODLIST mods = ads_init_mods(mem_ctx);
1356 char *prt_dn, *srv_dn, **srv_cn;
1359 if (!(ads = ads_startup(True))) {
1364 return net_ads_printer_usage(argc, argv);
1367 printername = argv[0];
1370 servername = argv[1];
1372 servername = global_myname();
1375 /* Get printer data from SPOOLSS */
1377 resolve_name(servername, &server_ip, 0x20);
1379 nt_status = cli_full_connection(&cli, global_myname(), servername,
1382 opt_user_name, opt_workgroup,
1383 opt_password ? opt_password : "",
1384 CLI_FULL_CONNECTION_USE_KERBEROS,
1387 if (NT_STATUS_IS_ERR(nt_status)) {
1388 d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
1389 "for %s\n", servername, printername);
1394 /* Publish on AD server */
1396 ads_find_machine_acct(ads, &res, servername);
1398 if (ads_count_replies(ads, res) == 0) {
1399 d_fprintf(stderr, "Could not find machine account for server %s\n",
1405 srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
1406 srv_cn = ldap_explode_dn(srv_dn, 1);
1408 asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
1410 pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
1412 d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
1418 get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
1421 rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
1422 if (!ADS_ERR_OK(rc)) {
1423 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
1428 d_printf("published printer\n");
1434 static int net_ads_printer_remove(int argc, const char **argv)
1438 const char *servername;
1442 if (!(ads = ads_startup(True))) {
1447 return net_ads_printer_usage(argc, argv);
1451 servername = argv[1];
1453 servername = global_myname();
1456 rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
1458 if (!ADS_ERR_OK(rc)) {
1459 d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
1460 ads_msgfree(ads, res);
1465 if (ads_count_replies(ads, res) == 0) {
1466 d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
1467 ads_msgfree(ads, res);
1472 prt_dn = ads_get_dn(ads, res);
1473 ads_msgfree(ads, res);
1474 rc = ads_del_dn(ads, prt_dn);
1475 ads_memfree(ads, prt_dn);
1477 if (!ADS_ERR_OK(rc)) {
1478 d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
1487 static int net_ads_printer(int argc, const char **argv)
1489 struct functable func[] = {
1490 {"SEARCH", net_ads_printer_search},
1491 {"INFO", net_ads_printer_info},
1492 {"PUBLISH", net_ads_printer_publish},
1493 {"REMOVE", net_ads_printer_remove},
1497 return net_run_function(argc, argv, func, net_ads_printer_usage);
1501 static int net_ads_password(int argc, const char **argv)
1504 const char *auth_principal = opt_user_name;
1505 const char *auth_password = opt_password;
1507 char *new_password = NULL;
1512 if (opt_user_name == NULL || opt_password == NULL) {
1513 d_fprintf(stderr, "You must supply an administrator username/password\n");
1518 d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
1523 if (!strchr_m(user, '@')) {
1524 asprintf(&c, "%s@%s", argv[0], lp_realm());
1528 use_in_memory_ccache();
1529 c = strchr_m(auth_principal, '@');
1536 /* use the realm so we can eventually change passwords for users
1537 in realms other than default */
1538 if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
1542 /* we don't actually need a full connect, but it's the easy way to
1543 fill in the KDC's addresss */
1546 if (!ads || !ads->config.realm) {
1547 d_fprintf(stderr, "Didn't find the kerberos server!\n");
1552 new_password = (char *)argv[1];
1554 asprintf(&prompt, "Enter new password for %s:", user);
1555 new_password = getpass(prompt);
1559 ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
1560 auth_password, user, new_password, ads->auth.time_offset);
1561 if (!ADS_ERR_OK(ret)) {
1562 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1567 d_printf("Password change for %s completed.\n", user);
1573 int net_ads_changetrustpw(int argc, const char **argv)
1576 char *host_principal;
1580 if (!secrets_init()) {
1581 DEBUG(1,("Failed to initialise secrets database\n"));
1585 net_use_machine_password();
1587 use_in_memory_ccache();
1589 if (!(ads = ads_startup(True))) {
1593 fstrcpy(my_name, global_myname());
1594 strlower_m(my_name);
1595 asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
1596 d_printf("Changing password for principal: %s\n", host_principal);
1598 ret = ads_change_trust_account_password(ads, host_principal);
1600 if (!ADS_ERR_OK(ret)) {
1601 d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
1603 SAFE_FREE(host_principal);
1607 d_printf("Password change for principal %s succeeded.\n", host_principal);
1609 if (lp_use_kerberos_keytab()) {
1610 d_printf("Attempting to update system keytab with new password.\n");
1611 if (ads_keytab_create_default(ads)) {
1612 d_printf("Failed to update system keytab.\n");
1617 SAFE_FREE(host_principal);
1623 help for net ads search
1625 static int net_ads_search_usage(int argc, const char **argv)
1628 "\nnet ads search <expression> <attributes...>\n"\
1629 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1630 "The expression is a standard LDAP search expression, and the\n"\
1631 "attributes are a list of LDAP fields to show in the results\n\n"\
1632 "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
1634 net_common_flags_usage(argc, argv);
1640 general ADS search function. Useful in diagnosing problems in ADS
1642 static int net_ads_search(int argc, const char **argv)
1646 const char *ldap_exp;
1651 return net_ads_search_usage(argc, argv);
1654 if (!(ads = ads_startup(False))) {
1661 rc = ads_do_search_all(ads, ads->config.bind_path,
1663 ldap_exp, attrs, &res);
1664 if (!ADS_ERR_OK(rc)) {
1665 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1670 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1672 /* dump the results */
1675 ads_msgfree(ads, res);
1683 help for net ads search
1685 static int net_ads_dn_usage(int argc, const char **argv)
1688 "\nnet ads dn <dn> <attributes...>\n"\
1689 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1690 "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
1691 "to show in the results\n\n"\
1692 "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
1694 net_common_flags_usage(argc, argv);
1700 general ADS search function. Useful in diagnosing problems in ADS
1702 static int net_ads_dn(int argc, const char **argv)
1711 return net_ads_dn_usage(argc, argv);
1714 if (!(ads = ads_startup(False))) {
1721 rc = ads_do_search_all(ads, dn,
1723 "(objectclass=*)", attrs, &res);
1724 if (!ADS_ERR_OK(rc)) {
1725 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1730 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1732 /* dump the results */
1735 ads_msgfree(ads, res);
1742 help for net ads sid search
1744 static int net_ads_sid_usage(int argc, const char **argv)
1747 "\nnet ads sid <sid> <attributes...>\n"\
1748 "\nperform a raw LDAP search on a ADS server and dump the results\n"\
1749 "The SID is in string format, and the attributes are a list of LDAP fields \n"\
1750 "to show in the results\n\n"\
1751 "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
1753 net_common_flags_usage(argc, argv);
1759 general ADS search function. Useful in diagnosing problems in ADS
1761 static int net_ads_sid(int argc, const char **argv)
1765 const char *sid_string;
1771 return net_ads_sid_usage(argc, argv);
1774 if (!(ads = ads_startup(False))) {
1778 sid_string = argv[0];
1781 if (!string_to_sid(&sid, sid_string)) {
1782 d_fprintf(stderr, "could not convert sid\n");
1787 rc = ads_search_retry_sid(ads, &res, &sid, attrs);
1788 if (!ADS_ERR_OK(rc)) {
1789 d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
1794 d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
1796 /* dump the results */
1799 ads_msgfree(ads, res);
1806 static int net_ads_keytab_usage(int argc, const char **argv)
1809 "net ads keytab <COMMAND>\n"\
1810 "<COMMAND> can be either:\n"\
1811 " CREATE Creates a fresh keytab\n"\
1812 " ADD Adds new service principal\n"\
1813 " FLUSH Flushes out all keytab entries\n"\
1814 " HELP Prints this help message\n"\
1815 "The ADD command will take arguments, the other commands\n"\
1816 "will not take any arguments. The arguments given to ADD\n"\
1817 "should be a list of principals to add. For example, \n"\
1818 " net ads keytab add srv1 srv2\n"\
1819 "will add principals for the services srv1 and srv2 to the\n"\
1820 "system's keytab.\n"\
1826 static int net_ads_keytab_flush(int argc, const char **argv)
1831 if (!(ads = ads_startup(True))) {
1834 ret = ads_keytab_flush(ads);
1839 static int net_ads_keytab_add(int argc, const char **argv)
1845 d_printf("Processing principals to add...\n");
1846 if (!(ads = ads_startup(True))) {
1849 for (i = 0; i < argc; i++) {
1850 ret |= ads_keytab_add_entry(ads, argv[i]);
1856 static int net_ads_keytab_create(int argc, const char **argv)
1861 if (!(ads = ads_startup(True))) {
1864 ret = ads_keytab_create_default(ads);
1869 int net_ads_keytab(int argc, const char **argv)
1871 struct functable func[] = {
1872 {"CREATE", net_ads_keytab_create},
1873 {"ADD", net_ads_keytab_add},
1874 {"FLUSH", net_ads_keytab_flush},
1875 {"HELP", net_ads_keytab_usage},
1879 if (!lp_use_kerberos_keytab()) {
1880 d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order to \
1881 use keytab functions.\n");
1884 return net_run_function(argc, argv, func, net_ads_keytab_usage);
1887 int net_ads_help(int argc, const char **argv)
1889 struct functable func[] = {
1890 {"USER", net_ads_user_usage},
1891 {"GROUP", net_ads_group_usage},
1892 {"PRINTER", net_ads_printer_usage},
1893 {"SEARCH", net_ads_search_usage},
1895 {"INFO", net_ads_info},
1896 {"JOIN", net_ads_join},
1897 {"JOIN2", net_ads_join2},
1898 {"LEAVE", net_ads_leave},
1899 {"STATUS", net_ads_status},
1900 {"PASSWORD", net_ads_password},
1901 {"CHANGETRUSTPW", net_ads_changetrustpw},
1906 return net_run_function(argc, argv, func, net_ads_usage);
1909 int net_ads(int argc, const char **argv)
1911 struct functable func[] = {
1912 {"INFO", net_ads_info},
1913 {"JOIN", net_ads_join},
1914 {"TESTJOIN", net_ads_testjoin},
1915 {"LEAVE", net_ads_leave},
1916 {"STATUS", net_ads_status},
1917 {"USER", net_ads_user},
1918 {"GROUP", net_ads_group},
1919 {"PASSWORD", net_ads_password},
1920 {"CHANGETRUSTPW", net_ads_changetrustpw},
1921 {"PRINTER", net_ads_printer},
1922 {"SEARCH", net_ads_search},
1924 {"SID", net_ads_sid},
1925 {"WORKGROUP", net_ads_workgroup},
1926 {"LOOKUP", net_ads_lookup},
1927 {"KEYTAB", net_ads_keytab},
1928 {"HELP", net_ads_help},
1932 return net_run_function(argc, argv, func, net_ads_usage);
1937 static int net_ads_noads(void)
1939 d_fprintf(stderr, "ADS support not compiled in\n");
1943 int net_ads_keytab(int argc, const char **argv)
1945 return net_ads_noads();
1948 int net_ads_usage(int argc, const char **argv)
1950 return net_ads_noads();
1953 int net_ads_help(int argc, const char **argv)
1955 return net_ads_noads();
1958 int net_ads_changetrustpw(int argc, const char **argv)
1960 return net_ads_noads();
1963 int net_ads_join(int argc, const char **argv)
1965 return net_ads_noads();
1968 int net_ads_user(int argc, const char **argv)
1970 return net_ads_noads();
1973 int net_ads_group(int argc, const char **argv)
1975 return net_ads_noads();
1978 /* this one shouldn't display a message */
1979 int net_ads_check(void)
1984 int net_ads_check_our_domain(void)
1989 int net_ads(int argc, const char **argv)
1991 return net_ads_usage(argc, argv);