r16945: Sync trunk -> 3.0 for 3.0.24 code. Still need
[vlendec/samba-autobuild/.git] / source3 / utils / net_ads.c
index 650f9922cb185c99c56c32e25e7955d15047eae4..bfbc80759aa9ae0f495c63c66a625f1f8046ff0c 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
    Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
    Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+   Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org)
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 
 #include "includes.h"
-#include "../utils/net.h"
+#include "utils/net.h"
 
+/* Macro for checking RPC error codes to make things more readable */
+
+#if 0
+#define CHECK_RPC_ERR(rpc, msg) \
+        if (!NT_STATUS_IS_OK(result = rpc)) { \
+                DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+                goto done; \
+        }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+        if (!NT_STATUS_IS_OK(result = rpc)) { \
+                DEBUG(0, debug_args); \
+                goto done; \
+        }
+
+#endif
 #ifdef HAVE_ADS
 
 int net_ads_usage(int argc, const char **argv)
@@ -55,6 +72,8 @@ int net_ads_usage(int argc, const char **argv)
 "\n\tperform a raw LDAP search and dump the results\n"
 "\nnet ads dn"\
 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+"\nnet ads sid"\
+"\n\tperform a raw LDAP search and dump attributes of a particular SID\n"
 "\nnet ads keytab"\
 "\n\tcreates and updates the kerberos system keytab file\n"
                );
@@ -62,6 +81,78 @@ int net_ads_usage(int argc, const char **argv)
 }
 
 
+/*
+  do a cldap netlogon query
+*/
+static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
+{
+       struct cldap_netlogon_reply reply;
+
+       if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
+               d_fprintf(stderr, "CLDAP query failed!\n");
+               return -1;
+       }
+
+       d_printf("Information for Domain Controller: %s\n\n", 
+               inet_ntoa(ads->ldap_ip));
+
+       d_printf("Response Type: ");
+       switch (reply.type) {
+       case SAMLOGON_AD_UNK_R:
+               d_printf("SAMLOGON\n");
+               break;
+       case SAMLOGON_AD_R:
+               d_printf("SAMLOGON_USER\n");
+               break;
+       default:
+               d_printf("0x%x\n", reply.type);
+               break;
+       }
+       d_printf("GUID: %s\n", 
+                smb_uuid_string_static(smb_uuid_unpack_static(reply.guid))); 
+       d_printf("Flags:\n"
+                "\tIs a PDC:                                   %s\n"
+                "\tIs a GC of the forest:                      %s\n"
+                "\tIs an LDAP server:                          %s\n"
+                "\tSupports DS:                                %s\n"
+                "\tIs running a KDC:                           %s\n"
+                "\tIs running time services:                   %s\n"
+                "\tIs the closest DC:                          %s\n"
+                "\tIs writable:                                %s\n"
+                "\tHas a hardware clock:                       %s\n"
+                "\tIs a non-domain NC serviced by LDAP server: %s\n",
+                (reply.flags & ADS_PDC) ? "yes" : "no",
+                (reply.flags & ADS_GC) ? "yes" : "no",
+                (reply.flags & ADS_LDAP) ? "yes" : "no",
+                (reply.flags & ADS_DS) ? "yes" : "no",
+                (reply.flags & ADS_KDC) ? "yes" : "no",
+                (reply.flags & ADS_TIMESERV) ? "yes" : "no",
+                (reply.flags & ADS_CLOSEST) ? "yes" : "no",
+                (reply.flags & ADS_WRITABLE) ? "yes" : "no",
+                (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no",
+                (reply.flags & ADS_NDNC) ? "yes" : "no");
+
+       printf("Forest:\t\t\t%s\n", reply.forest);
+       printf("Domain:\t\t\t%s\n", reply.domain);
+       printf("Domain Controller:\t%s\n", reply.hostname);
+
+       printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain);
+       printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname);
+
+       if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk);
+       if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
+
+       printf("Site Name:\t\t%s\n", reply.site_name);
+       printf("Site Name (2):\t\t%s\n", reply.site_name_2);
+
+       d_printf("NT Version: %d\n", reply.version);
+       d_printf("LMNT Token: %.2x\n", reply.lmnt_token);
+       d_printf("LM20 Token: %.2x\n", reply.lm20_token);
+
+       return 0;
+}
+
+
 /*
   this implements the CLDAP based netlogon lookup requests
   for finding the domain controller of a ADS domain
@@ -69,20 +160,29 @@ int net_ads_usage(int argc, const char **argv)
 static int net_ads_lookup(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
+       ADS_STATUS status;
+       const char *realm = NULL;
 
-       ads = ads_init(NULL, opt_target_workgroup, opt_host);
+       if ( strequal(lp_workgroup(), opt_target_workgroup ) )
+               realm = lp_realm();
+
+       ads = ads_init(realm, opt_target_workgroup, opt_host);
        if (ads) {
                ads->auth.flags |= ADS_AUTH_NO_BIND;
        }
 
-       ads_connect(ads);
-
-       if (!ads || !ads->config.realm) {
-               d_printf("Didn't find the cldap server!\n");
+       status = ads_connect(ads);
+       if (!ADS_ERR_OK(status) || !ads) {
+               d_fprintf(stderr, "Didn't find the cldap server!\n");
                return -1;
        }
+       
+       if (!ads->config.realm) {
+               ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+               ads->ldap_port = 389;
+       }
 
-       return ads_cldap_netlogon(ads);
+       return net_ads_cldap_netlogon(ads);
 }
 
 
@@ -91,19 +191,24 @@ static int net_ads_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
 
-       ads = ads_init(NULL, opt_target_workgroup, opt_host);
-
-       if (ads) {
+       if ( (ads = ads_init(lp_realm(), opt_target_workgroup, opt_host)) != NULL ) {
                ads->auth.flags |= ADS_AUTH_NO_BIND;
        }
 
        ads_connect(ads);
 
        if (!ads || !ads->config.realm) {
-               d_printf("Didn't find the ldap server!\n");
+               d_fprintf(stderr, "Didn't find the ldap server!\n");
                return -1;
        }
 
+       /* Try to set the server's current time since we didn't do a full
+          TCP LDAP session initially */
+
+       if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
+               d_fprintf( stderr, "Failed to get server's current time!\n");
+       }
+
        d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip));
        d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
        d_printf("Realm: %s\n", ads->config.realm);
@@ -111,8 +216,8 @@ static int net_ads_info(int argc, const char **argv)
        d_printf("LDAP port: %d\n", ads->ldap_port);
        d_printf("Server time: %s\n", http_timestring(ads->config.current_time));
 
-       d_printf("KDC server: %s\n", ads->auth.kdc_server );
-       d_printf("Server time offset: %d\n", ads->auth.time_offset );
+       d_printf("KDC server: %s\n", ads->auth.kdc_server );
+       d_printf("Server time offset: %d\n", ads->auth.time_offset );
 
        return 0;
 }
@@ -166,7 +271,7 @@ retry:
         * extract the realm and convert to upper case.
         * This is only used to establish the connection.
         */
-       if ((cp = strchr(ads->auth.user_name, '@'))!=0) {
+       if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
                *cp++ = '\0';
                ads->auth.realm = smb_xstrdup(cp);
                strupper_m(ads->auth.realm);
@@ -180,7 +285,8 @@ retry:
                        second_time = True;
                        goto retry;
                } else {
-                       DEBUG(1,("ads_connect: %s\n", ads_errstr(status)));
+                       DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
+                       ads_destroy(&ads);
                        return NULL;
                }
        }
@@ -196,10 +302,19 @@ retry:
 int net_ads_check(void)
 {
        ADS_STRUCT *ads;
+       ADS_STATUS status;
 
-       ads = ads_startup();
-       if (!ads)
+       if ( (ads = ads_init( lp_realm(), lp_workgroup(), NULL )) == NULL ) {
                return -1;
+       }
+
+       ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+        status = ads_connect(ads);
+        if ( !ADS_ERR_OK(status) ) {
+                return -1;
+        }
+
        ads_destroy(&ads);
        return 0;
 }
@@ -210,26 +325,38 @@ int net_ads_check(void)
 static int net_ads_workgroup(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
-       TALLOC_CTX *ctx;
-       const char *workgroup;
+       ADS_STATUS status;
+       const char *realm = NULL;
+       struct cldap_netlogon_reply reply;
 
-       if (!(ads = ads_startup())) return -1;
+       if ( strequal(lp_workgroup(), opt_target_workgroup ) )
+               realm = lp_realm();
 
-       if (!(ctx = talloc_init("net_ads_workgroup"))) {
-               return -1;
+       ads = ads_init(realm, opt_target_workgroup, opt_host);
+       if (ads) {
+               ads->auth.flags |= ADS_AUTH_NO_BIND;
        }
 
-       if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
-               d_printf("Failed to find workgroup for realm '%s'\n", 
-                        ads->config.realm);
-               talloc_destroy(ctx);
+       status = ads_connect(ads);
+       if (!ADS_ERR_OK(status) || !ads) {
+               d_fprintf(stderr, "Didn't find the cldap server!\n");
+               return -1;
+       }
+       
+       if (!ads->config.realm) {
+               ads->config.realm = CONST_DISCARD(char *, opt_target_workgroup);
+               ads->ldap_port = 389;
+       }
+       
+       if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap_ip), ads->server.realm, &reply ) ) {
+               d_fprintf(stderr, "CLDAP query failed!\n");
                return -1;
        }
 
-       d_printf("Workgroup: %s\n", workgroup);
-
-       talloc_destroy(ctx);
+       d_printf("Workgroup: %s\n", reply.netbios_domain);
 
+       ads_destroy(&ads);
+       
        return 0;
 }
 
@@ -240,12 +367,14 @@ static BOOL usergrp_display(char *field, void **values, void *data_area)
        char **disp_fields = (char **) data_area;
 
        if (!field) { /* must be end of record */
-               if (!strchr_m(disp_fields[0], '$')) {
-                       if (disp_fields[1])
-                               d_printf("%-21.21s %s\n", 
-                                      disp_fields[0], disp_fields[1]);
-                       else
-                               d_printf("%s\n", disp_fields[0]);
+               if (disp_fields[0]) {
+                       if (!strchr_m(disp_fields[0], '$')) {
+                               if (disp_fields[1])
+                                       d_printf("%-21.21s %s\n", 
+                                              disp_fields[0], disp_fields[1]);
+                               else
+                                       d_printf("%s\n", disp_fields[0]);
+                       }
                }
                SAFE_FREE(disp_fields[0]);
                SAFE_FREE(disp_fields[1]);
@@ -254,10 +383,10 @@ static BOOL usergrp_display(char *field, void **values, void *data_area)
        if (!values) /* must be new field, indicate string field */
                return True;
        if (StrCaseCmp(field, "sAMAccountName") == 0) {
-               disp_fields[0] = strdup((char *) values[0]);
+               disp_fields[0] = SMB_STRDUP((char *) values[0]);
        }
        if (StrCaseCmp(field, "description") == 0)
-               disp_fields[1] = strdup((char *) values[0]);
+               disp_fields[1] = SMB_STRDUP((char *) values[0]);
        return True;
 }
 
@@ -276,24 +405,30 @@ static int ads_user_add(int argc, const char **argv)
 
        if (argc < 1) return net_ads_user_usage(argc, argv);
        
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
        status = ads_find_user_acct(ads, &res, argv[0]);
 
        if (!ADS_ERR_OK(status)) {
-               d_printf("ads_user_add: %s\n", ads_errstr(status));
+               d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
                goto done;
        }
        
        if (ads_count_replies(ads, res)) {
-               d_printf("ads_user_add: User %s already exists\n", argv[0]);
+               d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
                goto done;
        }
 
+       if (opt_container == NULL) {
+               opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
+       }
+
        status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
 
        if (!ADS_ERR_OK(status)) {
-               d_printf("Could not add user %s: %s\n", argv[0],
+               d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
                         ads_errstr(status));
                goto done;
        }
@@ -317,7 +452,7 @@ static int ads_user_add(int argc, const char **argv)
        }
 
        /* password didn't set, delete account */
-       d_printf("Could not add user %s.  Error setting password %s\n",
+       d_fprintf(stderr, "Could not add user %s.  Error setting password %s\n",
                 argv[0], ads_errstr(status));
        ads_msgfree(ads, res);
        status=ads_find_user_acct(ads, &res, argv[0]);
@@ -342,14 +477,21 @@ static int ads_user_info(int argc, const char **argv)
        const char *attrs[] = {"memberOf", NULL};
        char *searchstring=NULL;
        char **grouplist;
-       char *escaped_user = escape_ldap_string_alloc(argv[0]);
+       char *escaped_user;
 
-       if (argc < 1) return net_ads_user_usage(argc, argv);
-       
-       if (!(ads = ads_startup())) return -1;
+       if (argc < 1) {
+               return net_ads_user_usage(argc, argv);
+       }
+
+       escaped_user = escape_ldap_string_alloc(argv[0]);
 
        if (!escaped_user) {
-               d_printf("ads_user_info: failed to escape user %s\n", argv[0]);
+               d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
+               return -1;
+       }
+
+       if (!(ads = ads_startup())) {
+               SAFE_FREE(escaped_user);
                return -1;
        }
 
@@ -358,11 +500,14 @@ static int ads_user_info(int argc, const char **argv)
        safe_free(searchstring);
 
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_search: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
+               SAFE_FREE(escaped_user);
                return -1;
        }
        
-       grouplist = ldap_get_values(ads->ld, res, "memberOf");
+       grouplist = ldap_get_values((LDAP *)ads->ld,
+                                   (LDAPMessage *)res, "memberOf");
 
        if (grouplist) {
                int i;
@@ -376,8 +521,8 @@ static int ads_user_info(int argc, const char **argv)
        }
        
        ads_msgfree(ads, res);
-
        ads_destroy(&ads);
+       SAFE_FREE(escaped_user);
        return 0;
 }
 
@@ -388,13 +533,18 @@ static int ads_user_delete(int argc, const char **argv)
        void *res;
        char *userdn;
 
-       if (argc < 1) return net_ads_user_usage(argc, argv);
+       if (argc < 1) {
+               return net_ads_user_usage(argc, argv);
+       }
        
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
        rc = ads_find_user_acct(ads, &res, argv[0]);
        if (!ADS_ERR_OK(rc)) {
                DEBUG(0, ("User %s does not exist\n", argv[0]));
+               ads_destroy(&ads);
                return -1;
        }
        userdn = ads_get_dn(ads, res);
@@ -403,10 +553,12 @@ static int ads_user_delete(int argc, const char **argv)
        ads_memfree(ads, userdn);
        if (!ADS_ERR_OK(rc)) {
                d_printf("User %s deleted\n", argv[0]);
+               ads_destroy(&ads);
                return 0;
        }
-       d_printf("Error deleting user %s: %s\n", argv[0], 
+       d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0], 
                 ads_errstr(rc));
+       ads_destroy(&ads);
        return -1;
 }
 
@@ -425,7 +577,9 @@ int net_ads_user(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
        
        if (argc == 0) {
-               if (!(ads = ads_startup())) return -1;
+               if (!(ads = ads_startup())) {
+                       return -1;
+               }
 
                if (opt_long_list_entries)
                        d_printf("\nUser name             Comment"\
@@ -433,12 +587,12 @@ int net_ads_user(int argc, const char **argv)
 
                rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
                                          LDAP_SCOPE_SUBTREE,
-                                         "(objectclass=user)", 
+                                         "(objectCategory=user)", 
                                          opt_long_list_entries ? longattrs :
                                          shortattrs, usergrp_display, 
                                          disp_fields);
                ads_destroy(&ads);
-               return 0;
+               return ADS_ERR_OK(rc) ? 0 : -1;
        }
 
        return net_run_function(argc, argv, func, net_ads_user_usage);
@@ -456,30 +610,38 @@ static int ads_group_add(int argc, const char **argv)
        void *res=NULL;
        int rc = -1;
 
-       if (argc < 1) return net_ads_group_usage(argc, argv);
+       if (argc < 1) {
+               return net_ads_group_usage(argc, argv);
+       }
        
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
        status = ads_find_user_acct(ads, &res, argv[0]);
 
        if (!ADS_ERR_OK(status)) {
-               d_printf("ads_group_add: %s\n", ads_errstr(status));
+               d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
                goto done;
        }
        
        if (ads_count_replies(ads, res)) {
-               d_printf("ads_group_add: Group %s already exists\n", argv[0]);
+               d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
                ads_msgfree(ads, res);
                goto done;
        }
 
+       if (opt_container == NULL) {
+               opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
+       }
+
        status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
 
        if (ADS_ERR_OK(status)) {
                d_printf("Group %s added\n", argv[0]);
                rc = 0;
        } else {
-               d_printf("Could not add group %s: %s\n", argv[0],
+               d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
                         ads_errstr(status));
        }
 
@@ -497,13 +659,18 @@ static int ads_group_delete(int argc, const char **argv)
        void *res;
        char *groupdn;
 
-       if (argc < 1) return net_ads_group_usage(argc, argv);
+       if (argc < 1) {
+               return net_ads_group_usage(argc, argv);
+       }
        
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
        rc = ads_find_user_acct(ads, &res, argv[0]);
        if (!ADS_ERR_OK(rc)) {
                DEBUG(0, ("Group %s does not exist\n", argv[0]));
+               ads_destroy(&ads);
                return -1;
        }
        groupdn = ads_get_dn(ads, res);
@@ -512,10 +679,12 @@ static int ads_group_delete(int argc, const char **argv)
        ads_memfree(ads, groupdn);
        if (!ADS_ERR_OK(rc)) {
                d_printf("Group %s deleted\n", argv[0]);
+               ads_destroy(&ads);
                return 0;
        }
-       d_printf("Error deleting group %s: %s\n", argv[0], 
+       d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0], 
                 ads_errstr(rc));
+       ads_destroy(&ads);
        return -1;
 }
 
@@ -533,20 +702,22 @@ int net_ads_group(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
-               if (!(ads = ads_startup())) return -1;
+               if (!(ads = ads_startup())) {
+                       return -1;
+               }
 
                if (opt_long_list_entries)
                        d_printf("\nGroup name            Comment"\
                                 "\n-----------------------------\n");
                rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
                                          LDAP_SCOPE_SUBTREE, 
-                                         "(objectclass=group)", 
+                                         "(objectCategory=group)", 
                                          opt_long_list_entries ? longattrs : 
                                          shortattrs, usergrp_display, 
                                          disp_fields);
 
                ads_destroy(&ads);
-               return 0;
+               return ADS_ERR_OK(rc) ? 0 : -1;
        }
        return net_run_function(argc, argv, func, net_ads_group_usage);
 }
@@ -557,52 +728,92 @@ static int net_ads_status(int argc, const char **argv)
        ADS_STATUS rc;
        void *res;
 
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
        rc = ads_find_machine_acct(ads, &res, global_myname());
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
                return -1;
        }
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("No machine account for '%s' found\n", global_myname());
+               d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
+               ads_destroy(&ads);
                return -1;
        }
 
        ads_dump(ads, res);
-
+       ads_destroy(&ads);
        return 0;
 }
 
+/*******************************************************************
+ Leave an AD domain.  Windows XP disables the machine account.
+ We'll try the same.  The old code would do an LDAP delete.
+ That only worked using the machine creds because added the machine
+ with full control to the computer object's ACL.
+*******************************************************************/
 static int net_ads_leave(int argc, const char **argv)
 {
        ADS_STRUCT *ads = NULL;
-       ADS_STATUS rc;
+       int ret = -1;
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *ctx;
+       DOM_SID *dom_sid = NULL;
 
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
                return -1;
        }
 
-       if (!opt_password) {
-               net_use_machine_password();
+       if (!(ctx = talloc_init("net_ads_leave"))) {
+               DEBUG(0, ("Could not initialise talloc context\n"));
+               return -1;
        }
 
+       /* The finds a DC and takes care of getting the 
+          user creds if necessary */
+
        if (!(ads = ads_startup())) {
                return -1;
        }
 
-       rc = ads_leave_realm(ads, global_myname());
-       if (!ADS_ERR_OK(rc)) {
-           d_printf("Failed to delete host '%s' from the '%s' realm.\n", 
-                    global_myname(), ads->config.realm);
-           return -1;
+       /* make RPC calls here */
+
+       if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip, 
+               ads->config.ldap_server_name)) )
+       {
+               goto done;
        }
+       
+       saf_store( cli->server_domain, cli->desthost );
 
-       d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
+               goto done;
+       }
 
-       return 0;
+       if ( !NT_STATUS_IS_OK(netdom_leave_domain( ctx, cli, dom_sid )) ) {
+               d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+                       global_myname(), ads->config.realm);
+               goto done;
+       }
+       
+       d_printf("Disabled account for '%s' in realm '%s'\n", 
+               global_myname(), ads->config.realm);
+               
+       ret = 0;
+
+done:
+       if ( cli ) 
+               cli_shutdown(cli);
+
+       ads_destroy(&ads);
+       TALLOC_FREE( ctx );
+
+       return ret;
 }
 
 static int net_ads_join_ok(void)
@@ -641,99 +852,252 @@ int net_ads_testjoin(int argc, const char **argv)
        return 0;
 }
 
-/*
-  join a domain using ADS
- */
-int net_ads_join(int argc, const char **argv)
+/*******************************************************************
+  Simple configu checks before beginning the join
+ ********************************************************************/
+
+static int check_ads_config( void )
 {
-       ADS_STRUCT *ads;
-       ADS_STATUS rc;
-       char *password;
-       char *machine_account = NULL;
-       char *tmp_password;
-       const char *org_unit = "Computers";
-       char *dn;
-       void *res;
-       DOM_SID dom_sid;
-       char *ou_str;
-       uint32 sec_channel_type = SEC_CHAN_WKSTA;
-       uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT;
-       const char *short_domain_name = NULL;
-       TALLOC_CTX *ctx = NULL;
+       if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+               d_printf("Host is not configured as a member server.\n");
+               return -1;
+       }
 
-       if (argc > 0) org_unit = argv[0];
+       if (strlen(global_myname()) > 15) {
+               d_printf("Our netbios name can be at most 15 chars long, "
+                        "\"%s\" is %u chars long\n",
+                        global_myname(), (unsigned int)strlen(global_myname()));
+               return -1;
+       }
+
+       if ( lp_security() == SEC_ADS && !*lp_realm()) {
+               d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
+                       "join to succeed.\n");
+               return -1;
+       }
 
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
                return -1;
        }
+       
+       return 0;
+}
 
-       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-       password = strdup(tmp_password);
+/*******************************************************************
+ Do the domain join
+ ********************************************************************/
 
-       if (!(ads = ads_startup())) return -1;
+static int net_join_domain( TALLOC_CTX *ctx, const char *servername, 
+                            struct in_addr *ip, DOM_SID **dom_sid, const char *password )
+{
+       int ret = -1;
+       struct cli_state *cli = NULL;
 
-       if (!*lp_realm()) {
-               d_printf("realm must be set in in smb.conf for ADS join to succeed.\n");
-               return -1;
+       if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, ip, servername)) )
+               goto done;
+       
+       saf_store( cli->server_domain, cli->desthost );
+
+       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, dom_sid )) )
+               goto done;
+
+       if ( !NT_STATUS_IS_OK(netdom_join_domain( ctx, cli, *dom_sid, 
+               password, ND_TYPE_AD )) )
+       {
+               goto done;
        }
+       
+       ret = 0;
 
-       if (strcmp(ads->config.realm, lp_realm()) != 0) {
-               d_printf("realm of remote server (%s) and realm in smb.conf (%s) DO NOT match.  Aborting join\n", ads->config.realm, lp_realm());
-               return -1;
+done:
+       if ( cli ) 
+               cli_shutdown(cli);
+
+       return ret;
+}
+
+/*******************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+ ********************************************************************/
+
+static ADS_STATUS net_set_machine_spn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s )
+{
+       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
+       char *host_upn, *new_dn;
+       ADS_MODLIST mods;
+       const char *servicePrincipalName[3] = {NULL, NULL, NULL};
+       char *psp;
+       fstring my_fqdn;
+       LDAPMessage *res = NULL;
+       char *dn_string = NULL;
+       const char *machine_name = global_myname();
+       int count;
+       
+       if ( !machine_name ) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+       
+       /* Find our DN */
+       
+       status = ads_find_machine_acct(ads_s, (void **)(void *)&res, machine_name);
+       if (!ADS_ERR_OK(status)) 
+               return status;
+               
+       if ( (count = ads_count_replies(ads_s, res)) != 1 ) {
+               DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
+               return ADS_ERROR(LDAP_NO_MEMORY);       
+       }
+       
+       if ( (dn_string = ads_get_dn(ads_s, res)) == NULL ) {
+               DEBUG(1, ("ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?)\n"));
+               goto done;
        }
+       
+       new_dn = talloc_strdup(ctx, dn_string);
+       ads_memfree(ads_s, dn_string);
+       if (!new_dn) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+
+       /* Windows only creates HOST/shortname & HOST/fqdn.  We create 
+          the UPN as well so that 'kinit -k' will work.  You can only 
+          request a TGT for entries with a UPN in AD. */
+          
+       if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) ) 
+               goto done;
+       strupper_m(psp);
+       servicePrincipalName[0] = psp;
 
-       ou_str = ads_ou_string(org_unit);
+       name_to_fqdn(my_fqdn, machine_name);
+       strlower_m(my_fqdn);
+       if ( !(psp = talloc_asprintf(ctx, "HOST/%s", my_fqdn)) ) 
+               goto done;
+       servicePrincipalName[1] = psp;
+       
+       if (!(host_upn = talloc_asprintf(ctx, "%s@%s", servicePrincipalName[0], ads_s->config.realm)))
+               goto done;
+
+       /* now do the mods */
+       
+       if (!(mods = ads_init_mods(ctx))) {
+               goto done;
+       }
+       
+       /* fields of primary importance */
+       
+       ads_mod_str(ctx, &mods, "dNSHostName", my_fqdn);
+       ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+
+       status = ads_gen_mod(ads_s, new_dn, mods);
+
+done:
+       ads_msgfree(ads_s, res);
+       
+       return status;
+}
+
+
+/*******************************************************************
+  join a domain using ADS (LDAP mods)
+ ********************************************************************/
+
+static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
+{
+       ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
+       char *dn, *ou_str;
+       LDAPMessage *res = NULL;
+
+       ou_str = ads_ou_string(ads, ou);
        asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
        free(ou_str);
 
        rc = ads_search_dn(ads, &res, dn, NULL);
        ads_msgfree(ads, res);
 
-       if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
-               d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n", 
-                        org_unit, dn);
-               return -1;
+       if (ADS_ERR_OK(rc)) {
+               /* Attempt to create the machine account and bail if this fails.
+                  Assume that the admin wants exactly what they requested */
+
+               rc = ads_create_machine_acct( ads, global_myname(), dn );
+               if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
+                       rc = ADS_SUCCESS;
+               }
        }
-       free(dn);
 
-       if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_join_realm: %s\n", ads_errstr(rc));
-               return -1;
-       }       
+       SAFE_FREE( dn );
 
-       rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
-       if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_join_realm: %s\n", ads_errstr(rc));
+       return rc;
+}
+
+/*******************************************************************
+  join a domain using ADS (LDAP mods)
+ ********************************************************************/
+int net_ads_join(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       char *machine_account = NULL;
+       const char *short_domain_name = NULL;
+       char *tmp_password, *password;
+       struct cldap_netlogon_reply cldap_reply;
+       TALLOC_CTX *ctx;
+       DOM_SID *domain_sid = NULL;
+       
+       if ( check_ads_config() != 0 ) {
+               d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
                return -1;
        }
 
-       rc = ads_domain_sid(ads, &dom_sid);
-       if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_domain_sid: %s\n", ads_errstr(rc));       
-       return -1;
+       if ( (ads = ads_startup()) == NULL ) {
+               return -1;
        }
 
-       if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
-               d_printf("asprintf failed\n");
+       if (strcmp(ads->config.realm, lp_realm()) != 0) {
+               d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
+                       "(%s) DO NOT match.  Aborting join\n", ads->config.realm, 
+                       lp_realm());
+               ads_destroy(&ads);
                return -1;
        }
 
-       rc = ads_set_machine_password(ads, machine_account, password);
-       if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_set_machine_password: %s\n", ads_errstr(rc));
+       if (!(ctx = talloc_init("net_ads_join"))) {
+               DEBUG(0, ("Could not initialise talloc context\n"));
                return -1;
        }
+
+       /* If we were given an OU, try to create the machine in the OU account 
+          first and then do the normal RPC join */
+
+       if ( argc > 0 ) {
+               status = net_precreate_machine_acct( ads, argv[0] );
+               if ( !ADS_ERR_OK(status) ) {
+                       d_fprintf( stderr, "Failed to pre-create the machine object "
+                               "in OU %s.\n", argv[0]);
+                       ads_destroy( &ads );
+                       return -1;
+               }
+       }
+
+       /* Do the domain join here */
+
+       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+       password = talloc_strdup(ctx, tmp_password);
        
-       /* make sure we get the right workgroup */
-       
-       if ( !(ctx = talloc_init("net ads join")) ) {
-               d_printf("talloc_init() failed!\n");
+       if ( net_join_domain( ctx, ads->config.ldap_server_name, &ads->ldap_ip, &domain_sid, password ) != 0 ) {
+               d_fprintf(stderr, "Failed to join domain!\n");
                return -1;
        }
        
-       rc = ads_workgroup_name(ads, ctx, &short_domain_name);
-       if ( ADS_ERR_OK(rc) ) {
+       /* Check the short name of the domain */
+       
+       ZERO_STRUCT( cldap_reply );
+       
+       if ( ads_cldap_netlogon( ads->config.ldap_server_name, 
+               ads->server.realm, &cldap_reply ) ) 
+       {
+               short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
                if ( !strequal(lp_workgroup(), short_domain_name) ) {
                        d_printf("The workgroup in smb.conf does not match the short\n");
                        d_printf("domain name obtained from the server.\n");
@@ -745,29 +1109,53 @@ int net_ads_join(int argc, const char **argv)
        }
        
        d_printf("Using short domain name -- %s\n", short_domain_name);
-       
-       /*  HACK ALRET!  Store the sid and password under bother the lp_workgroup() 
+
+       /*  HACK ALERT!  Store the sid and password under both the lp_workgroup() 
            value from smb.conf and the string returned from the server.  The former is
            neede to bootstrap winbindd's first connection to the DC to get the real 
            short domain name   --jerry */
-           
-       if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) {
-               DEBUG(1,("Failed to save domain sid\n"));
+          
+       if ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
+               || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
+       {
+               ads_destroy(&ads);
                return -1;
        }
 
-       if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
-               DEBUG(1,("Failed to save machine password\n"));
+       /* Verify that everything is ok */
+
+       if ( net_rpc_join_ok(short_domain_name, ads->config.ldap_server_name, &ads->ldap_ip) != 0 ) {
+               d_fprintf(stderr, "Failed to verify membership in domain!\n");
+               return -1;
+       }       
+
+       /* create the dNSHostName & servicePrincipalName values */
+       
+       status = net_set_machine_spn( ctx, ads );
+       if ( !ADS_ERR_OK(status) )  {
+               d_fprintf(stderr, "Failed to set servicePrincipalNames. Only NTLM authentication will be possible.\n");
+               d_fprintf(stderr, "Please ensure that the DNS domain of this server matches the AD domain,\n");
+               d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
+
+               /* don't fail */
+       }
+
+#if defined(HAVE_KRB5) 
+       if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
+               d_fprintf(stderr, "asprintf failed\n");
+               ads_destroy(&ads);
                return -1;
        }
 
-       if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
-               DEBUG(1,("Failed to save domain sid\n"));
+       if (!kerberos_derive_salting_principal(machine_account)) {
+               DEBUG(1,("Failed to determine salting principal\n"));
+               ads_destroy(&ads);
                return -1;
        }
 
-       if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
-               DEBUG(1,("Failed to save machine password\n"));
+       if (!kerberos_derive_cifs_salting_principals()) {
+               DEBUG(1,("Failed to determine salting principals\n"));
+               ads_destroy(&ads);
                return -1;
        }
        
@@ -775,22 +1163,25 @@ int net_ads_join(int argc, const char **argv)
        if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
                DEBUG(1,("Error creating host keytab!\n"));
        }
+#endif
 
        d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
 
-       SAFE_FREE(password);
        SAFE_FREE(machine_account);
-       if ( ctx ) {
-               talloc_destroy(ctx);
-       }
+       TALLOC_FREE( ctx );
+       ads_destroy(&ads);
+       
        return 0;
 }
 
+/*******************************************************************
+ ********************************************************************/
+
 int net_ads_printer_usage(int argc, const char **argv)
 {
        d_printf(
 "\nnet ads printer search <printer>"
-"\n\tsearch for a printer in the directory"
+"\n\tsearch for a printer in the directory\n"
 "\nnet ads printer info <printer> <server>"
 "\n\tlookup info in directory for printer on server"
 "\n\t(note: printer defaults to \"*\", server defaults to local)\n"
@@ -803,32 +1194,38 @@ int net_ads_printer_usage(int argc, const char **argv)
        return -1;
 }
 
+/*******************************************************************
+ ********************************************************************/
+
 static int net_ads_printer_search(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        void *res = NULL;
 
-       if (!(ads = ads_startup())) 
+       if (!(ads = ads_startup())) {
                return -1;
+       }
 
        rc = ads_find_printers(ads, &res);
 
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_find_printer: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
                ads_msgfree(ads, res);
-               return -1;
+               ads_destroy(&ads);
+               return -1;
        }
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("No results found\n");
+               d_fprintf(stderr, "No results found\n");
                ads_msgfree(ads, res);
+               ads_destroy(&ads);
                return -1;
        }
 
        ads_dump(ads, res);
        ads_msgfree(ads, res);
-
+       ads_destroy(&ads);
        return 0;
 }
 
@@ -839,39 +1236,47 @@ static int net_ads_printer_info(int argc, const char **argv)
        const char *servername, *printername;
        void *res = NULL;
 
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
-       if (argc > 0)
+       if (argc > 0) {
                printername = argv[0];
-       else
+       } else {
                printername = "*";
+       }
 
-       if (argc > 1)
+       if (argc > 1) {
                servername =  argv[1];
-       else
+       } else {
                servername = global_myname();
+       }
 
        rc = ads_find_printer_on_server(ads, &res, printername, servername);
 
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
                ads_msgfree(ads, res);
+               ads_destroy(&ads);
                return -1;
        }
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("Printer '%s' not found\n", printername);
+               d_fprintf(stderr, "Printer '%s' not found\n", printername);
                ads_msgfree(ads, res);
+               ads_destroy(&ads);
                return -1;
        }
 
        ads_dump(ads, res);
        ads_msgfree(ads, res);
+       ads_destroy(&ads);
 
        return 0;
 }
 
-void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+void do_drv_upgrade_printer(int msg_type, struct process_id src,
+                           void *buf, size_t len)
 {
        return;
 }
@@ -882,6 +1287,7 @@ static int net_ads_printer_publish(int argc, const char **argv)
         ADS_STATUS rc;
        const char *servername, *printername;
        struct cli_state *cli;
+       struct rpc_pipe_client *pipe_hnd;
        struct in_addr          server_ip;
        NTSTATUS nt_status;
        TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
@@ -889,17 +1295,21 @@ static int net_ads_printer_publish(int argc, const char **argv)
        char *prt_dn, *srv_dn, **srv_cn;
        void *res = NULL;
 
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
-       if (argc < 1)
+       if (argc < 1) {
                return net_ads_printer_usage(argc, argv);
+       }
        
        printername = argv[0];
 
-       if (argc == 2)
+       if (argc == 2) {
                servername = argv[1];
-       else
+       } else {
                servername = global_myname();
+       }
                
        /* Get printer data from SPOOLSS */
 
@@ -914,8 +1324,9 @@ static int net_ads_printer_publish(int argc, const char **argv)
                                        Undefined, NULL);
 
        if (NT_STATUS_IS_ERR(nt_status)) {
-               d_printf("Unable to open a connnection to %s to obtain data "
+               d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
                         "for %s\n", servername, printername);
+               ads_destroy(&ads);
                return -1;
        }
 
@@ -924,26 +1335,37 @@ static int net_ads_printer_publish(int argc, const char **argv)
        ads_find_machine_acct(ads, &res, servername);
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("Could not find machine account for server %s\n", 
+               d_fprintf(stderr, "Could not find machine account for server %s\n", 
                         servername);
+               ads_destroy(&ads);
                return -1;
        }
 
-       srv_dn = ldap_get_dn(ads->ld, res);
+       srv_dn = ldap_get_dn((LDAP *)ads->ld, (LDAPMessage *)res);
        srv_cn = ldap_explode_dn(srv_dn, 1);
 
        asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], printername, srv_dn);
 
-       cli_nt_session_open(cli, PI_SPOOLSS);
-       get_remote_printer_publishing_data(cli, mem_ctx, &mods, printername);
+       pipe_hnd = cli_rpc_pipe_open_noauth(cli, PI_SPOOLSS, &nt_status);
+       if (!pipe_hnd) {
+               d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
+                        servername);
+               ads_destroy(&ads);
+               return -1;
+       }
+
+       get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
+                                          printername);
 
         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
         if (!ADS_ERR_OK(rc)) {
-                d_printf("ads_publish_printer: %s\n", ads_errstr(rc));
+                d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
                 return -1;
         }
  
         d_printf("published printer\n");
+       ads_destroy(&ads);
  
        return 0;
 }
@@ -956,27 +1378,33 @@ static int net_ads_printer_remove(int argc, const char **argv)
        char *prt_dn;
        void *res = NULL;
 
-       if (!(ads = ads_startup())) return -1;
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
 
-       if (argc < 1)
+       if (argc < 1) {
                return net_ads_printer_usage(argc, argv);
+       }
 
-       if (argc > 1)
+       if (argc > 1) {
                servername = argv[1];
-       else
+       } else {
                servername = global_myname();
+       }
 
        rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
 
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
                ads_msgfree(ads, res);
+               ads_destroy(&ads);
                return -1;
        }
 
        if (ads_count_replies(ads, res) == 0) {
-               d_printf("Printer '%s' not found\n", argv[1]);
+               d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
                ads_msgfree(ads, res);
+               ads_destroy(&ads);
                return -1;
        }
 
@@ -986,10 +1414,12 @@ static int net_ads_printer_remove(int argc, const char **argv)
        ads_memfree(ads, prt_dn);
 
        if (!ADS_ERR_OK(rc)) {
-               d_printf("ads_del_dn: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
                return -1;
        }
 
+       ads_destroy(&ads);
        return 0;
 }
 
@@ -1019,12 +1449,12 @@ static int net_ads_password(int argc, const char **argv)
        ADS_STATUS ret;
 
        if (opt_user_name == NULL || opt_password == NULL) {
-               d_printf("You must supply an administrator username/password\n");
+               d_fprintf(stderr, "You must supply an administrator username/password\n");
                return -1;
        }
 
        if (argc < 1) {
-               d_printf("ERROR: You must say which username to change password for\n");
+               d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
                return -1;
        }
 
@@ -1035,7 +1465,7 @@ static int net_ads_password(int argc, const char **argv)
        }
 
        use_in_memory_ccache();    
-       c = strchr(auth_principal, '@');
+       c = strchr_m(auth_principal, '@');
        if (c) {
                realm = ++c;
        } else {
@@ -1044,7 +1474,7 @@ static int net_ads_password(int argc, const char **argv)
 
        /* use the realm so we can eventually change passwords for users 
        in realms other than default */
-       if (!(ads = ads_init(realm, NULL, NULL))) {
+       if (!(ads = ads_init(realm, opt_workgroup, NULL))) {
                return -1;
        }
 
@@ -1053,7 +1483,7 @@ static int net_ads_password(int argc, const char **argv)
        ads_connect(ads);
     
        if (!ads || !ads->config.realm) {
-               d_printf("Didn't find the kerberos server!\n");
+               d_fprintf(stderr, "Didn't find the kerberos server!\n");
                return -1;
        }
 
@@ -1068,7 +1498,7 @@ static int net_ads_password(int argc, const char **argv)
        ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
                                auth_password, user, new_password, ads->auth.time_offset);
        if (!ADS_ERR_OK(ret)) {
-               d_printf("Password change failed :-( ...\n");
+               d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
                ads_destroy(&ads);
                return -1;
        }
@@ -1083,7 +1513,7 @@ int net_ads_changetrustpw(int argc, const char **argv)
 {    
        ADS_STRUCT *ads;
        char *host_principal;
-       fstring my_fqdn;
+       fstring my_name;
        ADS_STATUS ret;
 
        if (!secrets_init()) {
@@ -1099,21 +1529,21 @@ int net_ads_changetrustpw(int argc, const char **argv)
                return -1;
        }
 
-       name_to_fqdn(my_fqdn, global_myname());
-       strlower_m(my_fqdn);
-       asprintf(&host_principal, "%s@%s", my_fqdn, ads->config.realm);
-       d_printf("Changing password for principal: HOST/%s\n", host_principal);
+       fstrcpy(my_name, global_myname());
+       strlower_m(my_name);
+       asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
+       d_printf("Changing password for principal: %s\n", host_principal);
 
        ret = ads_change_trust_account_password(ads, host_principal);
 
        if (!ADS_ERR_OK(ret)) {
-               d_printf("Password change failed :-( ...\n");
+               d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
                ads_destroy(&ads);
                SAFE_FREE(host_principal);
                return -1;
        }
     
-       d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
+       d_printf("Password change for principal %s succeeded.\n", host_principal);
 
        if (lp_use_kerberos_keytab()) {
                d_printf("Attempting to update system keytab with new password.\n");
@@ -1171,7 +1601,8 @@ static int net_ads_search(int argc, const char **argv)
                               LDAP_SCOPE_SUBTREE,
                               ldap_exp, attrs, &res);
        if (!ADS_ERR_OK(rc)) {
-               d_printf("search failed: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
                return -1;
        }       
 
@@ -1230,7 +1661,72 @@ static int net_ads_dn(int argc, const char **argv)
                               LDAP_SCOPE_BASE,
                               "(objectclass=*)", attrs, &res);
        if (!ADS_ERR_OK(rc)) {
-               d_printf("search failed: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
+               return -1;
+       }       
+
+       d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+       /* dump the results */
+       ads_dump(ads, res);
+
+       ads_msgfree(ads, res);
+       ads_destroy(&ads);
+
+       return 0;
+}
+
+/*
+  help for net ads sid search
+*/
+static int net_ads_sid_usage(int argc, const char **argv)
+{
+       d_printf(
+               "\nnet ads sid <sid> <attributes...>\n"\
+               "\nperform a raw LDAP search on a ADS server and dump the results\n"\
+               "The SID is in string format, and the attributes are a list of LDAP fields \n"\
+               "to show in the results\n\n"\
+               "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
+               );
+       net_common_flags_usage(argc, argv);
+       return -1;
+}
+
+
+/*
+  general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_sid(int argc, const char **argv)
+{
+       ADS_STRUCT *ads;
+       ADS_STATUS rc;
+       const char *sid_string;
+       const char **attrs;
+       void *res = NULL;
+       DOM_SID sid;
+
+       if (argc < 1) {
+               return net_ads_sid_usage(argc, argv);
+       }
+
+       if (!(ads = ads_startup())) {
+               return -1;
+       }
+
+       sid_string = argv[0];
+       attrs = (argv + 1);
+
+       if (!string_to_sid(&sid, sid_string)) {
+               d_fprintf(stderr, "could not convert sid\n");
+               ads_destroy(&ads);
+               return -1;
+       }
+
+       rc = ads_search_retry_sid(ads, &res, &sid, attrs);
+       if (!ADS_ERR_OK(rc)) {
+               d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+               ads_destroy(&ads);
                return -1;
        }       
 
@@ -1245,6 +1741,7 @@ static int net_ads_dn(int argc, const char **argv)
        return 0;
 }
 
+
 static int net_ads_keytab_usage(int argc, const char **argv)
 {
        d_printf(
@@ -1336,6 +1833,7 @@ int net_ads_help(int argc, const char **argv)
 #if 0
                {"INFO", net_ads_info},
                {"JOIN", net_ads_join},
+               {"JOIN2", net_ads_join2},
                {"LEAVE", net_ads_leave},
                {"STATUS", net_ads_status},
                {"PASSWORD", net_ads_password},
@@ -1362,6 +1860,7 @@ int net_ads(int argc, const char **argv)
                {"PRINTER", net_ads_printer},
                {"SEARCH", net_ads_search},
                {"DN", net_ads_dn},
+               {"SID", net_ads_sid},
                {"WORKGROUP", net_ads_workgroup},
                {"LOOKUP", net_ads_lookup},
                {"KEYTAB", net_ads_keytab},
@@ -1374,9 +1873,9 @@ int net_ads(int argc, const char **argv)
 
 #else
 
-static int net_ads_noads(int argc, const char **argv)
+static int net_ads_noads(void)
 {
-       d_printf("ADS support not compiled in\n");
+       d_fprintf(stderr, "ADS support not compiled in\n");
        return -1;
 }