r23779: Change from v2 or later to v3 or later.
[kai/samba.git] / source3 / utils / net_ads.c
index 6cd332fd3dfc48522debd7a806309af4779c8bfc..888c5a58d9f7d839b79f7fa9f0d66d2ac3d460a0 100644 (file)
@@ -4,10 +4,11 @@
    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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
 
 int net_ads_usage(int argc, const char **argv)
 {
-       d_printf(
-"\nnet ads join <org_unit>"\
-"\n\tjoins the local machine to a ADS realm\n"\
-"\nnet ads leave"\
-"\n\tremoves the local machine from a ADS realm\n"\
-"\nnet ads testjoin"\
-"\n\ttests that an exiting join is OK\n"\
-"\nnet ads user"\
-"\n\tlist, add, or delete users in the realm\n"\
-"\nnet ads group"\
-"\n\tlist, add, or delete groups in the realm\n"\
-"\nnet ads info"\
-"\n\tshows some info on the server\n"\
-"\nnet ads status"\
-"\n\tdump the machine account details to stdout\n"
-"\nnet ads lookup"\
-"\n\tperform a CLDAP search on the server\n"
-"\nnet ads password <username@realm> <password> -Uadmin_username@realm%%admin_pass"\
-"\n\tchange a user's password using an admin account"\
-"\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\
-"\nnet ads changetrustpw"\
-"\n\tchange the trust account password of this machine in the AD tree\n"\
-"\nnet ads printer [info | publish | remove] <printername> <servername>"\
-"\n\t lookup, add, or remove directory entry for a printer\n"\
-"\nnet ads search"\
-"\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 keytab"\
-"\n\tcreates and updates the kerberos system keytab file\n"
-               );
+       d_printf("join [createupn[=principal]] [createcomputer=<org_unit>]\n");
+       d_printf("    Join the local machine to a ADS realm\n");
+       d_printf("leave\n");
+       d_printf("    Remove the local machine from a ADS realm\n");
+       d_printf("testjoin\n");
+       d_printf("    Validates the machine account in the domain\n");
+       d_printf("user\n");
+       d_printf("    List, add, or delete users in the realm\n");
+       d_printf("group\n");
+       d_printf("    List, add, or delete groups in the realm\n");
+       d_printf("info\n");
+       d_printf("    Displays details regarding a specific AD server\n");
+       d_printf("status\n");
+       d_printf("    Display details regarding the machine's account in AD\n");
+       d_printf("lookup\n");
+       d_printf("    Performs CLDAP query of AD domain controllers\n");
+       d_printf("password <username@realm> <password> -Uadmin_username@realm%%admin_pass\n");
+       d_printf("    Change a user's password using an admin account\n");
+       d_printf("    (note: use realm in UPPERCASE, prompts if password is obmitted)\n");
+       d_printf("changetrustpw\n");
+       d_printf("    Change the trust account password of this machine in the AD tree\n");
+       d_printf("printer [info | publish | remove] <printername> <servername>\n");
+       d_printf("    Lookup, add, or remove directory entry for a printer\n");
+       d_printf("{search,dn,sid}\n");
+       d_printf("    Issue LDAP search queries using a general filter, by DN, or by SID\n");
+       d_printf("keytab\n");
+       d_printf("    Manage a local keytab file based on the machine account in AD\n");
+       d_printf("dns\n");
+       d_printf("    Issue a dynamic DNS update request the server's hostname\n");
+       d_printf("    (using the machine credentials)\n");
+       
        return -1;
 }
 
+/* when we do not have sufficient input parameters to contact a remote domain
+ * we always fall back to our own realm - Guenther*/
+
+static const char *assume_own_realm(void)
+{
+       if (!opt_host && strequal(lp_workgroup(), opt_target_workgroup)) {
+               return lp_realm();
+       }
+
+       return NULL;
+}
+
+/*
+  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("Server Site Name :\t\t%s\n", reply.server_site_name);
+       printf("Client Site Name :\t\t%s\n", reply.client_site_name);
+
+       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
@@ -69,25 +153,18 @@ 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;
 
-       ads = ads_init(NULL, opt_target_workgroup, opt_host);
-       if (ads) {
-               ads->auth.flags |= ADS_AUTH_NO_BIND;
-       }
-
-       status = ads_connect(ads);
-       if (!ADS_ERR_OK(status) || !ads) {
+       if (!ADS_ERR_OK(ads_startup_nobind(False, &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);
 }
 
 
@@ -96,24 +173,23 @@ static int net_ads_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
 
-       /* if netbios is disabled we have to default to the realm from smb.conf */
-
-       if ( lp_disable_netbios() && *lp_realm() )
-               ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
-       else
-               ads = ads_init(NULL, opt_target_workgroup, opt_host);
-
-       if (ads) {
-               ads->auth.flags |= ADS_AUTH_NO_BIND;
+       if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
+               d_fprintf(stderr, "Didn't find the ldap server!\n");
+               return -1;
        }
 
-       ads_connect(ads);
-
        if (!ads || !ads->config.realm) {
                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);
@@ -133,20 +209,31 @@ static void use_in_memory_ccache(void) {
        setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
 }
 
-static ADS_STRUCT *ads_startup(void)
+static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
 {
-       ADS_STRUCT *ads;
+       ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
        BOOL need_password = False;
        BOOL second_time = False;
        char *cp;
-       
+       const char *realm = NULL;
+       BOOL tried_closest_dc = False;
+
        /* lp_realm() should be handled by a command line param, 
           However, the join requires that realm be set in smb.conf
           and compares our realm with the remote server's so this is
           ok until someone needs more flexibility */
-          
-       ads = ads_init(lp_realm(), opt_target_workgroup, opt_host);
+
+       *ads_ret = NULL;
+
+retry_connect:
+       if (only_own_domain) {
+               realm = lp_realm();
+       } else {
+               realm = assume_own_realm();
+       }
+
+       ads = ads_init(realm, opt_target_workgroup, opt_host);
 
        if (!opt_user_name) {
                opt_user_name = "administrator";
@@ -158,17 +245,24 @@ static ADS_STRUCT *ads_startup(void)
 
 retry:
        if (!opt_password && need_password && !opt_machine_pass) {
-               char *prompt;
+               char *prompt = NULL;
                asprintf(&prompt,"%s's password: ", opt_user_name);
+               if (!prompt) {
+                       ads_destroy(&ads);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
                opt_password = getpass(prompt);
                free(prompt);
        }
 
        if (opt_password) {
                use_in_memory_ccache();
+               SAFE_FREE(ads->auth.password);
                ads->auth.password = smb_xstrdup(opt_password);
        }
 
+       ads->auth.flags |= auth_flags;
+       SAFE_FREE(ads->auth.user_name);
        ads->auth.user_name = smb_xstrdup(opt_user_name);
 
        /*
@@ -177,71 +271,128 @@ retry:
         * This is only used to establish the connection.
         */
        if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
-               *cp++ = '\0';
-               ads->auth.realm = smb_xstrdup(cp);
-               strupper_m(ads->auth.realm);
+               *cp++ = '\0';
+               SAFE_FREE(ads->auth.realm);
+               ads->auth.realm = smb_xstrdup(cp);
+               strupper_m(ads->auth.realm);
        }
 
        status = ads_connect(ads);
 
        if (!ADS_ERR_OK(status)) {
-               if (!need_password && !second_time) {
+
+               if (NT_STATUS_EQUAL(ads_ntstatus(status), 
+                                   NT_STATUS_NO_LOGON_SERVERS)) {
+                       DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
+                       ads_destroy(&ads);
+                       return status;
+               }
+       
+               if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
                        need_password = True;
                        second_time = True;
                        goto retry;
                } else {
-                       DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
-                       return NULL;
+                       ads_destroy(&ads);
+                       return status;
+               }
+       }
+
+       /* when contacting our own domain, make sure we use the closest DC.
+        * This is done by reconnecting to ADS because only the first call to
+        * ads_connect will give us our own sitename */
+
+       if ((only_own_domain || !opt_host) && !tried_closest_dc) {
+
+               tried_closest_dc = True; /* avoid loop */
+
+               if (!ads->config.tried_closest_dc) {
+
+                       namecache_delete(ads->server.realm, 0x1C);
+                       namecache_delete(ads->server.workgroup, 0x1C);
+
+                       ads_destroy(&ads);
+                       ads = NULL;
+
+                       goto retry_connect;
                }
        }
-       return ads;
+
+       *ads_ret = ads;
+       return status;
 }
 
+ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
+{
+       return ads_startup_int(only_own_domain, 0, ads);
+}
+
+ADS_STATUS ads_startup_nobind(BOOL only_own_domain, ADS_STRUCT **ads)
+{
+       return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
+}
 
 /*
   Check to see if connection can be made via ads.
   ads_startup() stores the password in opt_password if it needs to so
   that rpc or rap can use it without re-prompting.
 */
-int net_ads_check(void)
+static int net_ads_check_int(const char *realm, const char *workgroup, const char *host)
 {
        ADS_STRUCT *ads;
+       ADS_STATUS status;
 
-       ads = ads_startup();
-       if (!ads)
+       if ( (ads = ads_init( realm, workgroup, host )) == 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;
 }
 
+int net_ads_check_our_domain(void)
+{
+       return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
+}
+
+int net_ads_check(void)
+{
+       return net_ads_check_int(NULL, opt_workgroup, opt_host);
+}
 /* 
    determine the netbios workgroup name for a domain
  */
 static int net_ads_workgroup(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
-       TALLOC_CTX *ctx;
-       const char *workgroup;
-
-       if (!(ads = ads_startup())) return -1;
+       struct cldap_netlogon_reply reply;
 
-       if (!(ctx = talloc_init("net_ads_workgroup"))) {
-               ads_destroy(&ads);
+       if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
+               d_fprintf(stderr, "Didn't find the cldap server!\n");
                return -1;
        }
-
-       if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) {
-               d_fprintf(stderr, "Failed to find workgroup for realm '%s'\n", 
-                        ads->config.realm);
-               talloc_destroy(ctx);
-               ads_destroy(&ads);
+       
+       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);
+       d_printf("Workgroup: %s\n", reply.netbios_domain);
 
-       talloc_destroy(ctx);
        ads_destroy(&ads);
+       
        return 0;
 }
 
@@ -285,12 +436,13 @@ static int ads_user_add(int argc, const char **argv)
        ADS_STRUCT *ads;
        ADS_STATUS status;
        char *upn, *userdn;
-       void *res=NULL;
+       LDAPMessage *res=NULL;
        int rc = -1;
+       char *ou_str = NULL;
 
        if (argc < 1) return net_ads_user_usage(argc, argv);
        
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -306,11 +458,13 @@ static int ads_user_add(int argc, const char **argv)
                goto done;
        }
 
-       if (opt_container == NULL) {
-               opt_container = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
+       if (opt_container) {
+               ou_str = SMB_STRDUP(opt_container);
+       } else {
+               ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
        }
 
-       status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment);
+       status = ads_add_user_acct(ads, argv[0], ou_str, opt_comment);
 
        if (!ADS_ERR_OK(status)) {
                d_fprintf(stderr, "Could not add user %s: %s\n", argv[0],
@@ -351,6 +505,7 @@ static int ads_user_add(int argc, const char **argv)
        if (res)
                ads_msgfree(ads, res);
        ads_destroy(&ads);
+       SAFE_FREE(ou_str);
        return rc;
 }
 
@@ -358,7 +513,7 @@ static int ads_user_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       void *res;
+       LDAPMessage *res;
        const char *attrs[] = {"memberOf", NULL};
        char *searchstring=NULL;
        char **grouplist;
@@ -375,7 +530,7 @@ static int ads_user_info(int argc, const char **argv)
                return -1;
        }
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                SAFE_FREE(escaped_user);
                return -1;
        }
@@ -391,7 +546,8 @@ static int ads_user_info(int argc, const char **argv)
                return -1;
        }
        
-       grouplist = ldap_get_values(ads->ld, res, "memberOf");
+       grouplist = ldap_get_values((LDAP *)ads->ld,
+                                   (LDAPMessage *)res, "memberOf");
 
        if (grouplist) {
                int i;
@@ -414,20 +570,21 @@ static int ads_user_delete(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       void *res;
+       LDAPMessage *res = NULL;
        char *userdn;
 
        if (argc < 1) {
                return net_ads_user_usage(argc, argv);
        }
        
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                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]));
+       if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
+               d_printf("User %s does not exist.\n", argv[0]);
+               ads_msgfree(ads, res);
                ads_destroy(&ads);
                return -1;
        }
@@ -435,7 +592,7 @@ static int ads_user_delete(int argc, const char **argv)
        ads_msgfree(ads, res);
        rc = ads_del_dn(ads, userdn);
        ads_memfree(ads, userdn);
-       if (!ADS_ERR_OK(rc)) {
+       if (ADS_ERR_OK(rc)) {
                d_printf("User %s deleted\n", argv[0]);
                ads_destroy(&ads);
                return 0;
@@ -461,7 +618,7 @@ int net_ads_user(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
        
        if (argc == 0) {
-               if (!(ads = ads_startup())) {
+               if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                        return -1;
                }
 
@@ -471,12 +628,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);
@@ -491,14 +648,15 @@ static int ads_group_add(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS status;
-       void *res=NULL;
+       LDAPMessage *res=NULL;
        int rc = -1;
+       char *ou_str = NULL;
 
        if (argc < 1) {
                return net_ads_group_usage(argc, argv);
        }
        
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -511,15 +669,16 @@ static int ads_group_add(int argc, const char **argv)
        
        if (ads_count_replies(ads, res)) {
                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);
+       if (opt_container) {
+               ou_str = SMB_STRDUP(opt_container);
+       } else {
+               ou_str = ads_default_ou_string(ads, WELL_KNOWN_GUID_USERS);
        }
 
-       status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment);
+       status = ads_add_group_acct(ads, argv[0], ou_str, opt_comment);
 
        if (ADS_ERR_OK(status)) {
                d_printf("Group %s added\n", argv[0]);
@@ -533,6 +692,7 @@ static int ads_group_add(int argc, const char **argv)
        if (res)
                ads_msgfree(ads, res);
        ads_destroy(&ads);
+       SAFE_FREE(ou_str);
        return rc;
 }
 
@@ -540,20 +700,21 @@ static int ads_group_delete(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       void *res;
+       LDAPMessage *res = NULL;
        char *groupdn;
 
        if (argc < 1) {
                return net_ads_group_usage(argc, argv);
        }
        
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                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]));
+       if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
+               d_printf("Group %s does not exist.\n", argv[0]);
+               ads_msgfree(ads, res);
                ads_destroy(&ads);
                return -1;
        }
@@ -561,7 +722,7 @@ static int ads_group_delete(int argc, const char **argv)
        ads_msgfree(ads, res);
        rc = ads_del_dn(ads, groupdn);
        ads_memfree(ads, groupdn);
-       if (!ADS_ERR_OK(rc)) {
+       if (ADS_ERR_OK(rc)) {
                d_printf("Group %s deleted\n", argv[0]);
                ads_destroy(&ads);
                return 0;
@@ -586,7 +747,7 @@ int net_ads_group(int argc, const char **argv)
        char *disp_fields[2] = {NULL, NULL};
 
        if (argc == 0) {
-               if (!(ads = ads_startup())) {
+               if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                        return -1;
                }
 
@@ -595,13 +756,13 @@ int net_ads_group(int argc, const char **argv)
                                 "\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);
 }
@@ -610,9 +771,9 @@ static int net_ads_status(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
        ADS_STATUS rc;
-       void *res;
+       LDAPMessage *res;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
 
@@ -634,54 +795,106 @@ static int net_ads_status(int argc, const char **argv)
        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;
+       ADS_STATUS adsret;
+       NTSTATUS status;
+       int ret = -1;
+       struct cli_state *cli = NULL;
+       TALLOC_CTX *ctx;
+       DOM_SID *dom_sid = NULL;
+       char *short_domain_name = 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"))) {
+               d_fprintf(stderr, "Could not initialise talloc context.\n");
+               return -1;
        }
 
-       if (!(ads = ads_startup())) {
+       /* The finds a DC and takes care of getting the 
+          user creds if necessary */
+
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
 
-       rc = ads_leave_realm(ads, global_myname());
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "Failed to delete host '%s' from the '%s' realm.\n", 
+       /* make RPC calls here */
+
+       if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap_ip, 
+               ads->config.ldap_server_name)) )
+       {
+               goto done;
+       }
+       
+       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
+               goto done;
+       }
+
+       saf_delete( short_domain_name );
+
+       status = netdom_leave_domain(ctx, cli, dom_sid);
+
+       /* Try and delete it via LDAP - the old way we used to. */
+
+       adsret = ads_leave_realm(ads, global_myname());
+       if (ADS_ERR_OK(adsret)) {
+               d_printf("Deleted account for '%s' in realm '%s'\n",
                        global_myname(), ads->config.realm);
-               ads_destroy(&ads);
-               return -1;
+               ret = 0;
+       } else {
+               /* We couldn't delete it - see if the disable succeeded. */
+               if (NT_STATUS_IS_OK(status)) {
+                       d_printf("Disabled account for '%s' in realm '%s'\n",
+                               global_myname(), ads->config.realm);
+                       ret = 0;
+               } else {
+                       d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+                               global_myname(), ads->config.realm);
+               }
        }
 
-       d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm);
+done:
+
+       if ( cli ) 
+               cli_shutdown(cli);
+
        ads_destroy(&ads);
-       return 0;
+       TALLOC_FREE( ctx );
+
+       return ret;
 }
 
-static int net_ads_join_ok(void)
+static NTSTATUS net_ads_join_ok(void)
 {
        ADS_STRUCT *ads = NULL;
+       ADS_STATUS status;
 
        if (!secrets_init()) {
                DEBUG(1,("Failed to initialise secrets database\n"));
-               return -1;
+               return NT_STATUS_ACCESS_DENIED;
        }
 
        net_use_machine_password();
 
-       if (!(ads = ads_startup())) {
-               return -1;
+       status = ads_startup(True, &ads);
+       if (!ADS_ERR_OK(status)) {
+               return ads_ntstatus(status);
        }
 
        ads_destroy(&ads);
-       return 0;
+       return NT_STATUS_OK;
 }
 
 /*
@@ -689,202 +902,926 @@ static int net_ads_join_ok(void)
  */
 int net_ads_testjoin(int argc, const char **argv)
 {
+       NTSTATUS status;
        use_in_memory_ccache();
 
        /* Display success or failure */
-       if (net_ads_join_ok() != 0) {
-               fprintf(stderr,"Join to domain is not valid\n");
+       status = net_ads_join_ok();
+       if (!NT_STATUS_IS_OK(status)) {
+               fprintf(stderr,"Join to domain is not valid: %s\n", 
+                       get_friendly_nt_error_msg(status));
                return -1;
        }
 
-       printf("Join is OK\n");
-       return 0;
+       printf("Join is OK\n");
+       return 0;
+}
+
+/*******************************************************************
+  Simple configu checks before beginning the join
+ ********************************************************************/
+
+static NTSTATUS check_ads_config( void )
+{
+       if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+               d_printf("Host is not configured as a member server.\n");
+               return NT_STATUS_INVALID_DOMAIN_ROLE;
+       }
+
+       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 NT_STATUS_NAME_TOO_LONG;
+       }
+
+       if ( lp_security() == SEC_ADS && !*lp_realm()) {
+               d_fprintf(stderr, "realm must be set in in %s for ADS "
+                       "join to succeed.\n", dyn_CONFIGFILE);
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (!secrets_init()) {
+               DEBUG(1,("Failed to initialise secrets database\n"));
+               /* This is a good bet for failure of secrets_init ... */
+               return NT_STATUS_ACCESS_DENIED;
+       }
+       
+       return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Do the domain join
+ ********************************************************************/
+
+static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername, 
+                               struct in_addr *ip, char **domain, 
+                               DOM_SID **dom_sid, 
+                               const char *password)
+{
+       NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+       struct cli_state *cli = NULL;
+
+       ret = connect_to_ipc_krb5(&cli, ip, servername);
+       if ( !NT_STATUS_IS_OK(ret) ) {
+               goto done;
+       }
+       
+       ret = netdom_get_domain_sid( ctx, cli, domain, dom_sid );
+       if ( !NT_STATUS_IS_OK(ret) ) {
+               goto done;
+       }
+
+       /* cli->server_domain is not filled in when using krb5 
+          session setups */
+
+       saf_store( *domain, cli->desthost );
+
+       ret = netdom_join_domain( ctx, cli, *dom_sid, password, ND_TYPE_AD );
+
+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 *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, &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. */
+          
+       if ( !(psp = talloc_asprintf(ctx, "HOST/%s", machine_name)) ) 
+               goto done;
+       strupper_m(psp);
+       servicePrincipalName[0] = psp;
+
+       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 (!(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;
+}
+
+/*******************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+ ********************************************************************/
+
+static ADS_STATUS net_set_machine_upn(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, const char *upn )
+{
+       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
+       char *new_dn;
+       ADS_MODLIST mods;
+       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, &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);
+       }
+       
+       /* now do the mods */
+       
+       if (!(mods = ads_init_mods(ctx))) {
+               goto done;
+       }
+       
+       /* fields of primary importance */
+       
+       ads_mod_str(ctx, &mods, "userPrincipalName", upn);
+
+       status = ads_gen_mod(ads_s, new_dn, mods);
+
+done:
+       ads_msgfree(ads_s, res);
+       
+       return status;
+}
+
+/*******************************************************************
+ Set a machines dNSHostName and servicePrincipalName attributes
+ ********************************************************************/
+
+static ADS_STATUS net_set_os_attributes(TALLOC_CTX *ctx, ADS_STRUCT *ads_s, 
+                                       const char *os_name, const char *os_version )
+{
+       ADS_STATUS status = ADS_ERROR(LDAP_SERVER_DOWN);
+       char *new_dn;
+       ADS_MODLIST mods;
+       LDAPMessage *res = NULL;
+       char *dn_string = NULL;
+       const char *machine_name = global_myname();
+       int count;
+       char *os_sp = NULL;
+       
+       if ( !os_name || !os_version ) {
+               return ADS_ERROR(LDAP_NO_MEMORY);
+       }
+       
+       /* Find our DN */
+       
+       status = ads_find_machine_acct(ads_s, &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);
+       }
+       
+       /* now do the mods */
+       
+       if (!(mods = ads_init_mods(ctx))) {
+               goto done;
+       }
+
+       os_sp = talloc_asprintf( ctx, "Samba %s", SAMBA_VERSION_STRING );
+       
+       /* fields of primary importance */
+       
+       ads_mod_str(ctx, &mods, "operatingSystem", os_name);
+       ads_mod_str(ctx, &mods, "operatingSystemVersion", os_version);
+       if ( os_sp )
+               ads_mod_str(ctx, &mods, "operatingSystemServicePack", os_sp);
+
+       status = ads_gen_mod(ads_s, new_dn, mods);
+
+done:
+       ads_msgfree(ads_s, res);
+       TALLOC_FREE( os_sp );   
+       
+       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 *ou_str = NULL;
+       char *dn = NULL;
+       LDAPMessage *res = NULL;
+       BOOL moved;
+
+       ou_str = ads_ou_string(ads, ou);
+       if (asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path) == -1) {
+               rc = ADS_ERROR(LDAP_NO_MEMORY);
+               goto done;
+       }
+
+       rc = ads_search_dn(ads, &res, dn, NULL);
+       if (!ADS_ERR_OK(rc)) {
+               d_fprintf(stderr, "The specified OU does not exist.\n");
+               goto done;
+       }
+
+               /* 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 (ADS_ERR_OK(rc)) {
+               DEBUG(1, ("machine account created\n"));
+               goto done;
+               }
+       if ( !(rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS) ) {
+               DEBUG(1, ("machine account creation failed\n"));
+               goto done;
+       }
+
+       rc = ads_move_machine_acct(ads, global_myname(), dn, &moved);
+       if (!ADS_ERR_OK(rc)) {
+               DEBUG(1, ("failure to locate/move pre-existing machine account\n"));
+               goto done;
+       }
+
+       if (moved) {
+               d_printf("The machine account was moved into the specified OU.\n");
+       } else {
+               d_printf("The machine account already exists in the specified OU.\n");
+       }
+
+done:
+       ads_msgfree(ads, res);
+       SAFE_FREE( ou_str );
+       SAFE_FREE( dn );
+
+       return rc;
+}
+
+/************************************************************************
+ ************************************************************************/
+
+static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
+{
+       uint32 domain_func;
+       ADS_STATUS status;
+       fstring salt;
+       char *std_salt;
+       LDAPMessage *res = NULL;
+       const char *machine_name = global_myname();
+
+       status = ads_domain_func_level( ads, &domain_func );
+       if ( !ADS_ERR_OK(status) ) {
+               DEBUG(2,("Failed to determine domain functional level!\n"));
+               return False;
+       }
+
+       /* go ahead and setup the default salt */
+
+       if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
+               d_fprintf(stderr, "net_derive_salting_principal: failed to obtain stanard DES salt\n");
+               return False;
+       }
+
+       fstrcpy( salt, std_salt );
+       SAFE_FREE( std_salt );
+       
+       /* if it's a Windows functional domain, we have to look for the UPN */
+          
+       if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) { 
+               char *upn;
+               int count;
+               
+               status = ads_find_machine_acct(ads, &res, machine_name);
+               if (!ADS_ERR_OK(status)) {
+                       return False;
+               }
+               
+               if ( (count = ads_count_replies(ads, res)) != 1 ) {
+                       DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
+                       return False;
+               }
+               
+               upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
+               if ( upn ) {
+                       fstrcpy( salt, upn );
+               }
+               
+               ads_msgfree(ads, res);
+       }
+
+       return kerberos_secrets_store_des_salt( salt );
+}
+
+/*******************************************************************
+ Send a DNS update request
+*******************************************************************/
+
+#if defined(WITH_DNS_UPDATES)
+#include "dns.h"
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+                     const char *pszDomainName,
+                     const char *pszHostName,
+                     const struct in_addr *iplist, int num_addrs );
+
+
+static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
+                                       const char *machine_name,
+                                       const struct in_addr *addrs,
+                                       int num_addrs)
+{
+       struct dns_rr_ns *nameservers = NULL;
+       int ns_count = 0;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       DNS_ERROR dns_err;
+       fstring dns_server;
+       const char *dnsdomain = NULL;   
+       char *root_domain = NULL;       
+
+       if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
+               d_printf("No DNS domain configured for %s. "
+                        "Unable to perform DNS Update.\n", machine_name);
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto done;
+       }
+       dnsdomain++;
+
+       status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
+       if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
+               /* Child domains often do not have NS records.  Look
+                  for the NS record for the forest root domain 
+                  (rootDomainNamingContext in therootDSE) */
+
+               const char *rootname_attrs[] =  { "rootDomainNamingContext", NULL };
+               LDAPMessage *msg = NULL;
+               char *root_dn;
+               ADS_STATUS ads_status;
+               
+               if ( !ads->ld ) {
+                       ads_status = ads_connect( ads );
+                       if ( !ADS_ERR_OK(ads_status) ) {
+                               DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
+                               goto done;                              
+                       }                       
+               }
+               
+               ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
+                                      "(objectclass=*)", rootname_attrs, &msg);
+               if (!ADS_ERR_OK(ads_status)) {
+                       goto done;
+               }
+
+               root_dn = ads_pull_string(ads, ctx, msg,  "rootDomainNamingContext");
+               if ( !root_dn ) {
+                       ads_msgfree( ads, msg );                        
+                       goto done;
+               }
+
+               root_domain = ads_build_domain( root_dn );
+
+               /* cleanup */
+               ads_msgfree( ads, msg );
+
+               /* try again for NS servers */
+
+               status = ads_dns_lookup_ns( ctx, root_domain, &nameservers, &ns_count );
+               
+               if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {                     
+                       DEBUG(3,("net_ads_join: Failed to find name server for the %s "
+                        "realm\n", ads->config.realm));
+                       goto done;
+               }
+
+               dnsdomain = root_domain;                
+               
+       }
+
+       /* Now perform the dns update - we'll try non-secure and if we fail,
+          we'll follow it up with a secure update */
+
+       fstrcpy( dns_server, nameservers[0].hostname );
+
+       dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
+       if (!ERR_DNS_IS_OK(dns_err)) {
+               status = NT_STATUS_UNSUCCESSFUL;
+       }
+
+done:
+
+       SAFE_FREE( root_domain );
+       
+       return status;
+}
+
+static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
+{
+       int num_addrs;
+       struct in_addr *iplist = NULL;
+       fstring machine_name;
+       NTSTATUS status;
+
+       name_to_fqdn( machine_name, global_myname() );
+       strlower_m( machine_name );
+
+       /* Get our ip address (not the 127.0.0.x address but a real ip
+        * address) */
+
+       num_addrs = get_my_ip_address( &iplist );
+       if ( num_addrs <= 0 ) {
+               DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
+                        "addresses!\n"));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       status = net_update_dns_internal(mem_ctx, ads, machine_name,
+                                        iplist, num_addrs);
+       SAFE_FREE( iplist );
+       return status;
+}
+#endif
+
+
+/*******************************************************************
+ utility function to parse an integer parameter from 
+ "parameter = value"
+**********************************************************/
+static char* get_string_param( const char* param )
+{
+       char *p;
+       
+       if ( (p = strchr( param, '=' )) == NULL )
+               return NULL;
+               
+       return (p+1);
+}
+
+/*******************************************************************
+ ********************************************************************/
+static int net_ads_join_usage(int argc, const char **argv)
+{
+       d_printf("net ads join [options]\n");
+       d_printf("Valid options:\n");
+       d_printf("   createupn[=UPN]    Set the userPrincipalName attribute during the join.\n");
+       d_printf("                      The deault UPN is in the form host/netbiosname@REALM.\n");
+       d_printf("   createcomputer=OU  Precreate the computer account in a specific OU.\n");
+       d_printf("                      The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
+       d_printf("                      E.g. \"createcomputer=Computers/Servers/Unix\"\n");
+       d_printf("                      NB: A backslash '\\' is used as escape at multiple levels and may\n");
+       d_printf("                          need to be doubled or even quadrupled.  It is not used as a separator.\n");
+       d_printf("   osName=string      Set the operatingSystem attribute during the join.\n");
+       d_printf("   osVer=string       Set the operatingSystemVersion attribute during the join.\n");
+       d_printf("                      NB: osName and osVer must be specified together for either to take effect.\n");
+       d_printf("                          Also, the operatingSystemService attribute is also set when along with\n");
+       d_printf("                          the two other attributes.\n");
+
+       return -1;
 }
 
-/*
-  join a domain using ADS
- */
+/*******************************************************************
+ ********************************************************************/
 int net_ads_join(int argc, const char **argv)
 {
-       ADS_STRUCT *ads;
-       ADS_STATUS rc;
-       char *password;
+       ADS_STRUCT *ads = NULL;
+       ADS_STATUS status;
+       NTSTATUS nt_status;
        char *machine_account = NULL;
-       char *tmp_password;
-       const char *org_unit = NULL;
-       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;
+       char *short_domain_name = NULL;
+       char *tmp_password, *password;
        TALLOC_CTX *ctx = NULL;
-
-       if ((lp_server_role() != ROLE_DOMAIN_MEMBER) || 
-           (lp_server_role() != ROLE_DOMAIN_BDC)) {
-               d_printf("can only join as domain member or as BDC\n");
-               return -1;
+       DOM_SID *domain_sid = NULL;
+       BOOL createupn = False;
+       const char *machineupn = NULL;
+       const char *create_in_ou = NULL;
+       int i;
+       fstring dc_name;
+       struct in_addr dcip;
+       const char *os_name = NULL;
+       const char *os_version = NULL;
+       
+       nt_status = check_ads_config();
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
+               goto fail;
        }
 
+       /* find a DC to initialize the server affinity cache */
 
-       if (argc > 0) {
-               org_unit = argv[0];
-       }
+       get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
 
-       if (!secrets_init()) {
-               DEBUG(1,("Failed to initialise secrets database\n"));
-               return -1;
+       status = ads_startup(True, &ads);
+       if (!ADS_ERR_OK(status)) {
+               DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+               nt_status = ads_ntstatus(status);
+               goto fail;
        }
 
-       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-       password = SMB_STRDUP(tmp_password);
+       if (strcmp(ads->config.realm, lp_realm()) != 0) {
+               d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
+                       "(%s) DO NOT match.  Aborting join\n", ads->config.realm, 
+                       dyn_CONFIGFILE, lp_realm());
+               nt_status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
 
-       if (!(ads = ads_startup())) {
-               return -1;
+       if (!(ctx = talloc_init("net_ads_join"))) {
+               d_fprintf(stderr, "Could not initialise talloc context.\n");
+               nt_status = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
 
-       if (!*lp_realm()) {
-               d_fprintf(stderr, "realm must be set in in smb.conf for ADS join to succeed.\n");
-               ads_destroy(&ads);
-               return -1;
+       /* process additional command line args */
+       
+       for ( i=0; i<argc; i++ ) {
+               if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
+                       createupn = True;
+                       machineupn = get_string_param(argv[i]);
+               }
+               else if ( !StrnCaseCmp(argv[i], "createcomputer", strlen("createcomputer")) ) {
+                       if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, "Please supply a valid OU path.\n");
+                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               goto fail;
+                       }               
+               }
+               else if ( !StrnCaseCmp(argv[i], "osName", strlen("osName")) ) {
+                       if ( (os_name = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, "Please supply a operating system name.\n");
+                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               goto fail;
+                       }               
+               }
+               else if ( !StrnCaseCmp(argv[i], "osVer", strlen("osVer")) ) {
+                       if ( (os_version = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, "Please supply a valid operating system version.\n");
+                               nt_status = NT_STATUS_INVALID_PARAMETER;
+                               goto fail;
+                       }               
+               }
+               else {
+                       d_fprintf(stderr, "Bad option: %s\n", argv[i]);
+                       nt_status = NT_STATUS_INVALID_PARAMETER;
+                       goto fail;
+               }
        }
 
-       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;
+       /* If we were given an OU, try to create the machine in 
+          the OU account first and then do the normal RPC join */
+
+       if  ( create_in_ou ) {
+               status = net_precreate_machine_acct( ads, create_in_ou );
+               if ( !ADS_ERR_OK(status) ) {
+                       d_fprintf( stderr, "Failed to pre-create the machine object "
+                               "in OU %s.\n", create_in_ou);
+                       DEBUG(1, ("error calling net_precreate_machine_acct: %s\n", 
+                                 ads_errstr(status)));
+                       nt_status = ads_ntstatus(status);
+                       goto fail;
+               }
        }
 
-       ou_str = ads_ou_string(ads,org_unit);
-       asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path);
-       free(ou_str);
+       /* Do the domain join here */
 
-       rc = ads_search_dn(ads, &res, dn, NULL);
-       ads_msgfree(ads, res);
+       tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+       password = talloc_strdup(ctx, tmp_password);
+       
+       nt_status = net_join_domain(ctx, ads->config.ldap_server_name, 
+                                   &ads->ldap_ip, &short_domain_name, &domain_sid, password);
+       if ( !NT_STATUS_IS_OK(nt_status) ) {
+               DEBUG(1, ("call of net_join_domain failed: %s\n", 
+                         get_friendly_nt_error_msg(nt_status)));
+               goto fail;
+       }
 
-       if (rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) {
-               d_fprintf(stderr, "ads_join_realm: organizational unit %s does not exist (dn:%s)\n", 
-                        org_unit, dn);
-               ads_destroy(&ads);
-               return -1;
+       /* Check the short name of the domain */
+       
+       if ( !strequal(lp_workgroup(), short_domain_name) ) {
+               d_printf("The workgroup in %s does not match the short\n", dyn_CONFIGFILE);
+               d_printf("domain name obtained from the server.\n");
+               d_printf("Using the name [%s] from the server.\n", short_domain_name);
+               d_printf("You should set \"workgroup = %s\" in %s.\n", 
+                        short_domain_name, dyn_CONFIGFILE);
        }
-       free(dn);
+       
+       d_printf("Using short domain name -- %s\n", short_domain_name);
 
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
-               ads_destroy(&ads);
-               return -1;
+       /*  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 ( (netdom_store_machine_account( lp_workgroup(), domain_sid, password ) == -1)
+               || (netdom_store_machine_account( short_domain_name, domain_sid, password ) == -1) )
+       {
+               /* issue an internal error here for now.
+                * everything else would mean changing tdb routines. */
+               nt_status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
+       }
+
+       /* 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");
+               goto fail;
        }       
 
-       rc = ads_join_realm(ads, global_myname(), account_type, org_unit);
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "ads_join_realm: %s\n", ads_errstr(rc));
-               ads_destroy(&ads);
-               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. Please ensure that\n");
+               d_fprintf(stderr, "the DNS domain of this server matches the AD domain,\n");
+               d_fprintf(stderr, "Or rejoin with using Domain Admin credentials.\n");
+               
+               /* Disable the machine account in AD.  Better to fail than to leave 
+                  a confused admin.  */
+               
+               if ( net_ads_leave( 0, NULL ) != 0 ) {
+                       d_fprintf( stderr, "Failed to disable machine account in AD.  Please do so manually.\n");
+               }
+               
+               /* clear out the machine password */
+               
+               netdom_store_machine_account( lp_workgroup(), domain_sid, "" ); 
+               netdom_store_machine_account( short_domain_name, domain_sid, "" );
+               
+               nt_status = ads_ntstatus(status);
+               goto fail;
        }
 
-       rc = ads_domain_sid(ads, &dom_sid);
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "ads_domain_sid: %s\n", ads_errstr(rc));      
-               ads_destroy(&ads);
-               return -1;
+       if ( !net_derive_salting_principal( ctx, ads ) ) {
+               DEBUG(1,("Failed to determine salting principal\n"));
+               goto fail;
        }
 
-       if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
-               d_fprintf(stderr, "asprintf failed\n");
-               ads_destroy(&ads);
-               return -1;
+       if ( createupn ) {
+               pstring upn;
+               
+               /* default to using the short UPN name */
+               if ( !machineupn ) {
+                       snprintf( upn, sizeof(upn), "host/%s@%s", global_myname(), 
+                               ads->config.realm );
+                       machineupn = upn;
+               }
+               
+               status = net_set_machine_upn( ctx, ads, machineupn );
+               if ( !ADS_ERR_OK(status) )  {
+                       d_fprintf(stderr, "Failed to set userPrincipalName.  Are you a Domain Admin?\n");
+               }
        }
 
-       rc = ads_set_machine_password(ads, machine_account, password);
-       if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "ads_set_machine_password: %s\n", ads_errstr(rc));
-               ads_destroy(&ads);
-               return -1;
+       /* Try to set the operatingSystem attributes if asked */
+
+       if ( os_name && os_version ) {
+               status = net_set_os_attributes( ctx, ads, os_name, os_version );
+               if ( !ADS_ERR_OK(status) )  {
+                       d_fprintf(stderr, "Failed to set operatingSystem attributes.  "
+                                 "Are you a Domain Admin?\n");
+               }
        }
+
+       /* Now build the keytab, using the same ADS connection */
+
+       if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
+               DEBUG(1,("Error creating host keytab!\n"));
+       }
+
+#if defined(WITH_DNS_UPDATES)
+       /* We enter this block with user creds */
+       ads_kdestroy( NULL );   
+       ads_destroy(&ads);
+       ads = NULL;
        
-       /* make sure we get the right workgroup */
-       
-       if ( !(ctx = talloc_init("net ads join")) ) {
-               d_fprintf(stderr, "talloc_init() failed!\n");
-               ads_destroy(&ads);
-               return -1;
+       if ( (ads = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
+               /* kinit with the machine password */
+
+               use_in_memory_ccache();
+               asprintf( &ads->auth.user_name, "%s$", global_myname() );
+               ads->auth.password = secrets_fetch_machine_password(
+                       lp_workgroup(), NULL, NULL );
+               ads->auth.realm = SMB_STRDUP( lp_realm() );
+               ads_kinit_password( ads );
        }
        
-       rc = ads_workgroup_name(ads, ctx, &short_domain_name);
-       if ( ADS_ERR_OK(rc) ) {
-               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");
-                       d_printf("Using the name [%s] from the server.\n", short_domain_name);
-                       d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
-               }
-       } else {
-               short_domain_name = lp_workgroup();
+       if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
+               d_fprintf( stderr, "DNS update failed!\n" );
        }
        
-       d_printf("Using short domain name -- %s\n", short_domain_name);
+       /* exit from this block using machine creds */
+#endif
+
+       d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
+
+       SAFE_FREE(machine_account);
+       TALLOC_FREE( ctx );
+       ads_destroy(&ads);
        
-       /*  HACK ALRET!  Store the sid and password under bother 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"));
-               ads_destroy(&ads);
+       return 0;
+
+fail:
+       /* issue an overall failure message at the end. */
+       d_printf("Failed to join domain: %s\n", get_friendly_nt_error_msg(nt_status));
+
+       SAFE_FREE(machine_account);
+       TALLOC_FREE( ctx );
+        ads_destroy(&ads);
+
+        return -1;
+
+}
+
+/*******************************************************************
+ ********************************************************************/
+static int net_ads_dns_usage(int argc, const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+       d_printf("net ads dns <command>\n");
+       d_printf("Valid commands:\n");
+       d_printf("   register         Issue a dynamic DNS update request for our hostname\n");
+
+       return 0;
+#else
+       d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
+       return -1;
+#endif
+}
+
+/*******************************************************************
+ ********************************************************************/
+static int net_ads_dns_register(int argc, const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+       ADS_STRUCT *ads;
+       ADS_STATUS status;
+       TALLOC_CTX *ctx;
+       
+#ifdef DEVELOPER
+       talloc_enable_leak_report();
+#endif
+       
+       if (argc > 0) {
+               d_fprintf(stderr, "net ads dns register <name> <ip>\n");
                return -1;
        }
 
-       if (!secrets_store_machine_password(password, lp_workgroup(), sec_channel_type)) {
-               DEBUG(1,("Failed to save machine password\n"));
-               ads_destroy(&ads);
+       if (!(ctx = talloc_init("net_ads_dns"))) {
+               d_fprintf(stderr, "Could not initialise talloc context\n");
                return -1;
        }
 
-#ifdef HAVE_KRB5
-       if (!kerberos_derive_salting_principal(machine_account)) {
-               DEBUG(1,("Failed to determine salting principal\n"));
-               ads_destroy(&ads);
+       status = ads_startup(True, &ads);
+       if ( !ADS_ERR_OK(status) ) {
+               DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+               TALLOC_FREE(ctx);
                return -1;
        }
 
-       if (!kerberos_derive_cifs_salting_principals()) {
-               DEBUG(1,("Failed to determine salting principals\n"));
-               ads_destroy(&ads);
+       if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {             
+               d_fprintf( stderr, "DNS update failed!\n" );
+               ads_destroy( &ads );
+               TALLOC_FREE( ctx );
                return -1;
        }
+       
+       d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
+
+       ads_destroy(&ads);
+       TALLOC_FREE( ctx );
+       
+       return 0;
+#else
+       d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
+       return -1;
 #endif
+}
 
-       if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) {
-               DEBUG(1,("Failed to save domain sid\n"));
-               ads_destroy(&ads);
-               return -1;
-       }
+#if defined(WITH_DNS_UPDATES)
+DNS_ERROR do_gethostbyname(const char *server, const char *host);
+#endif
 
-       if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) {
-               DEBUG(1,("Failed to save machine password\n"));
-               ads_destroy(&ads);
-               return -1;
-       }
+static int net_ads_dns_gethostbyname(int argc, const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+       DNS_ERROR err;
        
-       /* Now build the keytab, using the same ADS connection */
-       if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
-               DEBUG(1,("Error creating host keytab!\n"));
+#ifdef DEVELOPER
+       talloc_enable_leak_report();
+#endif
+
+       if (argc != 2) {
+               d_fprintf(stderr, "net ads dns gethostbyname <server> "
+                         "<name>\n");
+               return -1;
        }
 
-       d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
+       err = do_gethostbyname(argv[0], argv[1]);
 
-       SAFE_FREE(password);
-       SAFE_FREE(machine_account);
-       if ( ctx ) {
-               talloc_destroy(ctx);
-       }
-       ads_destroy(&ads);
+       d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
+#endif
        return 0;
 }
 
+static int net_ads_dns(int argc, const char *argv[])
+{
+       struct functable func[] = {
+               {"REGISTER", net_ads_dns_register},
+               {"GETHOSTBYNAME", net_ads_dns_gethostbyname},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, net_ads_dns_usage);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
 int net_ads_printer_usage(int argc, const char **argv)
 {
        d_printf(
@@ -902,13 +1839,16 @@ 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;
+       LDAPMessage *res = NULL;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -939,9 +1879,9 @@ static int net_ads_printer_info(int argc, const char **argv)
        ADS_STRUCT *ads;
        ADS_STATUS rc;
        const char *servername, *printername;
-       void *res = NULL;
+       LDAPMessage *res = NULL;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -960,7 +1900,8 @@ static int net_ads_printer_info(int argc, const char **argv)
        rc = ads_find_printer_on_server(ads, &res, printername, servername);
 
        if (!ADS_ERR_OK(rc)) {
-               d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
+               d_fprintf(stderr, "Server '%s' not found: %s\n", 
+                       servername, ads_errstr(rc));
                ads_msgfree(ads, res);
                ads_destroy(&ads);
                return -1;
@@ -980,12 +1921,6 @@ static int net_ads_printer_info(int argc, const char **argv)
        return 0;
 }
 
-void do_drv_upgrade_printer(int msg_type, struct process_id src,
-                           void *buf, size_t len)
-{
-       return;
-}
-
 static int net_ads_printer_publish(int argc, const char **argv)
 {
         ADS_STRUCT *ads;
@@ -998,13 +1933,16 @@ static int net_ads_printer_publish(int argc, const char **argv)
        TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
        ADS_MODLIST mods = ads_init_mods(mem_ctx);
        char *prt_dn, *srv_dn, **srv_cn;
-       void *res = NULL;
+       char *srv_cn_escaped = NULL, *printername_escaped = NULL;
+       LDAPMessage *res = NULL;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+               talloc_destroy(mem_ctx);
                return -1;
        }
 
        if (argc < 1) {
+               talloc_destroy(mem_ctx);
                return net_ads_printer_usage(argc, argv);
        }
        
@@ -1032,6 +1970,7 @@ static int net_ads_printer_publish(int argc, const char **argv)
                d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
                         "for %s\n", servername, printername);
                ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
                return -1;
        }
 
@@ -1043,34 +1982,60 @@ static int net_ads_printer_publish(int argc, const char **argv)
                d_fprintf(stderr, "Could not find machine account for server %s\n", 
                         servername);
                ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
                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);
+       srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
+       printername_escaped = escape_rdn_val_string_alloc(printername);
+       if (!srv_cn_escaped || !printername_escaped) {
+               SAFE_FREE(srv_cn_escaped);
+               SAFE_FREE(printername_escaped);
+               d_fprintf(stderr, "Internal error, out of memory!");
+               ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
+               return -1;
+       }
+
+       asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
+
+       SAFE_FREE(srv_cn_escaped);
+       SAFE_FREE(printername_escaped);
 
        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);
+               SAFE_FREE(prt_dn);
                ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
                return -1;
        }
 
-       get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
-                                          printername);
+       if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
+                                                             printername))) {
+               SAFE_FREE(prt_dn);
+               ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
+               return -1;
+       }
 
         rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
         if (!ADS_ERR_OK(rc)) {
                 d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
+               SAFE_FREE(prt_dn);
                ads_destroy(&ads);
+               talloc_destroy(mem_ctx);
                 return -1;
         }
  
         d_printf("published printer\n");
+       SAFE_FREE(prt_dn);
        ads_destroy(&ads);
+       talloc_destroy(mem_ctx);
  
        return 0;
 }
@@ -1081,9 +2046,9 @@ static int net_ads_printer_remove(int argc, const char **argv)
        ADS_STATUS rc;
        const char *servername;
        char *prt_dn;
-       void *res = NULL;
+       LDAPMessage *res = NULL;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
 
@@ -1179,7 +2144,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, opt_host))) {
                return -1;
        }
 
@@ -1203,7 +2168,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_fprintf(stderr, "Password change failed :-( ...\n");
+               d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
                ads_destroy(&ads);
                return -1;
        }
@@ -1230,25 +2195,25 @@ int net_ads_changetrustpw(int argc, const char **argv)
 
        use_in_memory_ccache();
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
 
        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: HOST/%s\n", host_principal);
+       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_fprintf(stderr, "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");
@@ -1289,13 +2254,13 @@ static int net_ads_search(int argc, const char **argv)
        ADS_STATUS rc;
        const char *ldap_exp;
        const char **attrs;
-       void *res = NULL;
+       LDAPMessage *res = NULL;
 
        if (argc < 1) {
                return net_ads_search_usage(argc, argv);
        }
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -1334,6 +2299,7 @@ static int net_ads_dn_usage(int argc, const char **argv)
                "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\
                "to show in the results\n\n"\
                "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
+               "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
                );
        net_common_flags_usage(argc, argv);
        return -1;
@@ -1349,13 +2315,13 @@ static int net_ads_dn(int argc, const char **argv)
        ADS_STATUS rc;
        const char *dn;
        const char **attrs;
-       void *res = NULL;
+       LDAPMessage *res = NULL;
 
        if (argc < 1) {
                return net_ads_dn_usage(argc, argv);
        }
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
 
@@ -1382,21 +2348,88 @@ static int net_ads_dn(int argc, const char **argv)
        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;
+       LDAPMessage *res = NULL;
+       DOM_SID sid;
+
+       if (argc < 1) {
+               return net_ads_sid_usage(argc, argv);
+       }
+
+       if (!ADS_ERR_OK(ads_startup(False, &ads))) {
+               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;
+       }       
+
+       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;
+}
+
+
 static int net_ads_keytab_usage(int argc, const char **argv)
 {
        d_printf(
                "net ads keytab <COMMAND>\n"\
 "<COMMAND> can be either:\n"\
-"  CREATE    Creates a fresh keytab\n"\
 "  ADD       Adds new service principal\n"\
+"  CREATE    Creates a fresh keytab\n"\
 "  FLUSH     Flushes out all keytab entries\n"\
 "  HELP      Prints this help message\n"\
-"The ADD command will take arguments, the other commands\n"\
+"  LIST      List the keytab\n"\
+"The ADD and LIST command will take arguments, the other commands\n"\
 "will not take any arguments.   The arguments given to ADD\n"\
 "should be a list of principals to add.  For example, \n"\
 "   net ads keytab add srv1 srv2\n"\
 "will add principals for the services srv1 and srv2 to the\n"\
 "system's keytab.\n"\
+"The LIST command takes a keytabname.\n"\
 "\n"
                );
        return -1;
@@ -1407,7 +2440,7 @@ static int net_ads_keytab_flush(int argc, const char **argv)
        int ret;
        ADS_STRUCT *ads;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
        ret = ads_keytab_flush(ads);
@@ -1422,7 +2455,7 @@ static int net_ads_keytab_add(int argc, const char **argv)
        ADS_STRUCT *ads;
 
        d_printf("Processing principals to add...\n");
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
        for (i = 0; i < argc; i++) {
@@ -1437,7 +2470,7 @@ static int net_ads_keytab_create(int argc, const char **argv)
        ADS_STRUCT *ads;
        int ret;
 
-       if (!(ads = ads_startup())) {
+       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
                return -1;
        }
        ret = ads_keytab_create_default(ads);
@@ -1445,13 +2478,26 @@ static int net_ads_keytab_create(int argc, const char **argv)
        return ret;
 }
 
+static int net_ads_keytab_list(int argc, const char **argv)
+{
+       const char *keytab = NULL;
+
+       if (argc >= 1) {
+               keytab = argv[0];
+       }
+
+       return ads_keytab_list(keytab);
+}
+
+
 int net_ads_keytab(int argc, const char **argv)
 {
        struct functable func[] = {
-               {"CREATE", net_ads_keytab_create},
                {"ADD", net_ads_keytab_add},
+               {"CREATE", net_ads_keytab_create},
                {"FLUSH", net_ads_keytab_flush},
                {"HELP", net_ads_keytab_usage},
+               {"LIST", net_ads_keytab_list},
                {NULL, NULL}
        };
 
@@ -1470,14 +2516,13 @@ int net_ads_help(int argc, const char **argv)
                {"GROUP", net_ads_group_usage},
                {"PRINTER", net_ads_printer_usage},
                {"SEARCH", net_ads_search_usage},
-#if 0
                {"INFO", net_ads_info},
-               {"JOIN", net_ads_join},
+               {"JOIN", net_ads_join_usage},
+               {"DNS", net_ads_dns_usage},
                {"LEAVE", net_ads_leave},
                {"STATUS", net_ads_status},
                {"PASSWORD", net_ads_password},
                {"CHANGETRUSTPW", net_ads_changetrustpw},
-#endif
                {NULL, NULL}
        };
 
@@ -1494,14 +2539,17 @@ int net_ads(int argc, const char **argv)
                {"STATUS", net_ads_status},
                {"USER", net_ads_user},
                {"GROUP", net_ads_group},
+               {"DNS", net_ads_dns},
                {"PASSWORD", net_ads_password},
                {"CHANGETRUSTPW", net_ads_changetrustpw},
                {"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},
+               {"GPO", net_ads_gpo},
                {"HELP", net_ads_help},
                {NULL, NULL}
        };
@@ -1558,9 +2606,14 @@ int net_ads_check(void)
        return -1;
 }
 
+int net_ads_check_our_domain(void)
+{
+       return -1;
+}
+
 int net_ads(int argc, const char **argv)
 {
        return net_ads_usage(argc, argv);
 }
 
-#endif
+#endif /* WITH_ADS */