net: Be more tolerant while joining.
[ira/wip.git] / source3 / utils / net_ads.c
index 71217b5137bd9774baaf5bf9c4b38e7e622c5f5b..c8bfc2630cc8283870f92a90cf949b766f9f3784 100644 (file)
@@ -1,5 +1,5 @@
-/* 
-   Samba Unix/Linux SMB client library 
+/*
+   Samba Unix/Linux SMB client library
    net ads commands
    Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
    Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
    it under the terms of the GNU General Public License as published by
    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,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.  
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "utils/net.h"
 
+#include "libnet/libnet.h"
+
 #ifdef HAVE_ADS
 
 int net_ads_usage(int argc, const char **argv)
@@ -57,7 +59,7 @@ int net_ads_usage(int argc, const char **argv)
        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;
 }
 
@@ -78,15 +80,18 @@ static const char *assume_own_realm(void)
 */
 static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
 {
+       char addr[INET6_ADDRSTRLEN];
        struct cldap_netlogon_reply reply;
+       struct GUID tmp_guid;
 
-       if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap.ip), ads->server.realm, &reply ) ) {
+       print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+       if ( !ads_cldap_netlogon(addr, 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("Information for Domain Controller: %s\n\n",
+               addr);
 
        d_printf("Response Type: ");
        switch (reply.type) {
@@ -100,8 +105,10 @@ static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
                d_printf("0x%x\n", reply.type);
                break;
        }
-       d_printf("GUID: %s\n", 
-                smb_uuid_string_static(smb_uuid_unpack_static(reply.guid))); 
+
+       smb_uuid_unpack(reply.guid, &tmp_guid);
+       d_printf("GUID: %s\n", smb_uuid_string(talloc_tos(), tmp_guid));
+
        d_printf("Flags:\n"
                 "\tIs a PDC:                                   %s\n"
                 "\tIs a GC of the forest:                      %s\n"
@@ -144,7 +151,6 @@ static int net_ads_cldap_netlogon(ADS_STRUCT *ads)
        return 0;
 }
 
-
 /*
   this implements the CLDAP based netlogon lookup requests
   for finding the domain controller of a ADS domain
@@ -171,6 +177,7 @@ static int net_ads_lookup(int argc, const char **argv)
 static int net_ads_info(int argc, const char **argv)
 {
        ADS_STRUCT *ads;
+       char addr[INET6_ADDRSTRLEN];
 
        if (!ADS_ERR_OK(ads_startup_nobind(False, &ads))) {
                d_fprintf(stderr, "Didn't find the ldap server!\n");
@@ -189,7 +196,9 @@ static int net_ads_info(int argc, const char **argv)
                d_fprintf( stderr, "Failed to get server's current time!\n");
        }
 
-       d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap.ip));
+       print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+
+       d_printf("LDAP server: %s\n", addr);
        d_printf("LDAP server name: %s\n", ads->config.ldap_server_name);
        d_printf("Realm: %s\n", ads->config.realm);
        d_printf("Bind Path: %s\n", ads->config.bind_path);
@@ -208,17 +217,17 @@ static void use_in_memory_ccache(void) {
        setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
 }
 
-static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
+static ADS_STATUS ads_startup_int(bool only_own_domain, uint32 auth_flags, ADS_STRUCT **ads_ret)
 {
        ADS_STRUCT *ads = NULL;
        ADS_STATUS status;
-       BOOL need_password = False;
-       BOOL second_time = False;
+       bool need_password = False;
+       bool second_time = False;
        char *cp;
        const char *realm = NULL;
-       BOOL tried_closest_dc = False;
+       bool tried_closest_dc = False;
 
-       /* lp_realm() should be handled by a command line param, 
+       /* 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 */
@@ -262,7 +271,7 @@ retry:
        ads->auth.user_name = smb_xstrdup(opt_user_name);
 
        /*
-        * If the username is of the form "name@realm", 
+        * If the username is of the form "name@realm",
         * extract the realm and convert to upper case.
         * This is only used to establish the connection.
         */
@@ -277,13 +286,13 @@ retry:
 
        if (!ADS_ERR_OK(status)) {
 
-               if (NT_STATUS_EQUAL(ads_ntstatus(status), 
+               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;
@@ -318,12 +327,12 @@ retry:
        return status;
 }
 
-ADS_STATUS ads_startup(BOOL only_own_domain, ADS_STRUCT **ads)
+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)
+ADS_STATUS ads_startup_nobind(bool only_own_domain, ADS_STRUCT **ads)
 {
        return ads_startup_int(only_own_domain, ADS_AUTH_NO_BIND, ads);
 }
@@ -362,25 +371,28 @@ 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;
+       char addr[INET6_ADDRSTRLEN];
        struct cldap_netlogon_reply reply;
 
        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;
        }
-       
-       if ( !ads_cldap_netlogon( inet_ntoa(ads->ldap.ip), ads->server.realm, &reply ) ) {
+
+       print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+       if ( !ads_cldap_netlogon(addr, ads->server.realm, &reply ) ) {
                d_fprintf(stderr, "CLDAP query failed!\n");
                return -1;
        }
@@ -388,13 +400,13 @@ static int net_ads_workgroup(int argc, const char **argv)
        d_printf("Workgroup: %s\n", reply.netbios_domain);
 
        ads_destroy(&ads);
-       
+
        return 0;
 }
 
 
 
-static BOOL usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
+static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
 {
        char **disp_fields = (char **) data_area;
 
@@ -402,7 +414,7 @@ static BOOL usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *d
                if (disp_fields[0]) {
                        if (!strchr_m(disp_fields[0], '$')) {
                                if (disp_fields[1])
-                                       d_printf("%-21.21s %s\n", 
+                                       d_printf("%-21.21s %s\n",
                                               disp_fields[0], disp_fields[1]);
                                else
                                        d_printf("%s\n", disp_fields[0]);
@@ -425,7 +437,7 @@ static BOOL usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *d
 static int net_ads_user_usage(int argc, const char **argv)
 {
        return net_help_user(argc, argv);
-} 
+}
 
 static int ads_user_add(int argc, const char **argv)
 {
@@ -437,7 +449,7 @@ static int ads_user_add(int argc, const char **argv)
        char *ou_str = NULL;
 
        if (argc < 1) return net_ads_user_usage(argc, argv);
-       
+
        if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
@@ -448,7 +460,7 @@ static int ads_user_add(int argc, const char **argv)
                d_fprintf(stderr, "ads_user_add: %s\n", ads_errstr(status));
                goto done;
        }
-       
+
        if (ads_count_replies(ads, res)) {
                d_fprintf(stderr, "ads_user_add: User %s already exists\n", argv[0]);
                goto done;
@@ -469,7 +481,7 @@ static int ads_user_add(int argc, const char **argv)
        }
 
        /* if no password is to be set, we're done */
-       if (argc == 1) { 
+       if (argc == 1) {
                d_printf("User %s added\n", argv[0]);
                rc = 0;
                goto done;
@@ -477,7 +489,7 @@ static int ads_user_add(int argc, const char **argv)
 
        /* try setting the password */
        asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
-       status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], 
+       status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
                                       ads->auth.time_offset);
        safe_free(upn);
        if (ADS_ERR_OK(status)) {
@@ -541,7 +553,7 @@ static int ads_user_info(int argc, const char **argv)
                SAFE_FREE(escaped_user);
                return -1;
        }
-       
+
        grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
                                    (LDAPMessage *)res, "memberOf");
 
@@ -555,7 +567,7 @@ static int ads_user_info(int argc, const char **argv)
                }
                ldap_value_free(grouplist);
        }
-       
+
        ads_msgfree(ads, res);
        ads_destroy(&ads);
        SAFE_FREE(escaped_user);
@@ -572,7 +584,7 @@ static int ads_user_delete(int argc, const char **argv)
        if (argc < 1) {
                return net_ads_user_usage(argc, argv);
        }
-       
+
        if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
@@ -593,7 +605,7 @@ static int ads_user_delete(int argc, const char **argv)
                ads_destroy(&ads);
                return 0;
        }
-       d_fprintf(stderr, "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;
@@ -612,7 +624,7 @@ int net_ads_user(int argc, const char **argv)
        const char *shortattrs[] = {"sAMAccountName", NULL};
        const char *longattrs[] = {"sAMAccountName", "description", NULL};
        char *disp_fields[2] = {NULL, NULL};
-       
+
        if (argc == 0) {
                if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                        return -1;
@@ -622,11 +634,11 @@ int net_ads_user(int argc, const char **argv)
                        d_printf("\nUser name             Comment"\
                                 "\n-----------------------------\n");
 
-               rc = ads_do_search_all_fn(ads, ads->config.bind_path, 
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path,
                                          LDAP_SCOPE_SUBTREE,
-                                         "(objectCategory=user)", 
+                                         "(objectCategory=user)",
                                          opt_long_list_entries ? longattrs :
-                                         shortattrs, usergrp_display, 
+                                         shortattrs, usergrp_display,
                                          disp_fields);
                ads_destroy(&ads);
                return ADS_ERR_OK(rc) ? 0 : -1;
@@ -638,7 +650,7 @@ int net_ads_user(int argc, const char **argv)
 static int net_ads_group_usage(int argc, const char **argv)
 {
        return net_help_group(argc, argv);
-} 
+}
 
 static int ads_group_add(int argc, const char **argv)
 {
@@ -651,7 +663,7 @@ static int ads_group_add(int argc, const char **argv)
        if (argc < 1) {
                return net_ads_group_usage(argc, argv);
        }
-       
+
        if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
@@ -662,7 +674,7 @@ static int ads_group_add(int argc, const char **argv)
                d_fprintf(stderr, "ads_group_add: %s\n", ads_errstr(status));
                goto done;
        }
-       
+
        if (ads_count_replies(ads, res)) {
                d_fprintf(stderr, "ads_group_add: Group %s already exists\n", argv[0]);
                goto done;
@@ -702,7 +714,7 @@ static int ads_group_delete(int argc, const char **argv)
        if (argc < 1) {
                return net_ads_group_usage(argc, argv);
        }
-       
+
        if (!ADS_ERR_OK(ads_startup(False, &ads))) {
                return -1;
        }
@@ -723,7 +735,7 @@ static int ads_group_delete(int argc, const char **argv)
                ads_destroy(&ads);
                return 0;
        }
-       d_fprintf(stderr, "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;
@@ -750,11 +762,11 @@ int net_ads_group(int argc, const char **argv)
                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, 
-                                         "(objectCategory=group)", 
-                                         opt_long_list_entries ? longattrs : 
-                                         shortattrs, usergrp_display, 
+               rc = ads_do_search_all_fn(ads, ads->config.bind_path,
+                                         LDAP_SCOPE_SUBTREE,
+                                         "(objectCategory=group)",
+                                         opt_long_list_entries ? longattrs :
+                                         shortattrs, usergrp_display,
                                          disp_fields);
 
                ads_destroy(&ads);
@@ -800,17 +812,12 @@ static int net_ads_status(int argc, const char **argv)
 
 static int net_ads_leave(int argc, const char **argv)
 {
-       ADS_STRUCT *ads = NULL;
-       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;      
+       struct libnet_UnjoinCtx *r = NULL;
+       WERROR werr;
 
-       if (!secrets_init()) {
-               DEBUG(1,("Failed to initialise secrets database\n"));
+       if (!*lp_realm()) {
+               d_fprintf(stderr, "No realm set, are we joined ?\n");
                return -1;
        }
 
@@ -819,57 +826,57 @@ static int net_ads_leave(int argc, const char **argv)
                return -1;
        }
 
-       /* The finds a DC and takes care of getting the 
-          user creds if necessary */
+       use_in_memory_ccache();
 
-       if (!ADS_ERR_OK(ads_startup(True, &ads))) {
+       werr = libnet_init_UnjoinCtx(ctx, &r);
+       if (!W_ERROR_IS_OK(werr)) {
+               d_fprintf(stderr, "Could not initialise unjoin context.\n");
                return -1;
        }
 
-       /* make RPC calls here */
-
-       if ( !NT_STATUS_IS_OK(connect_to_ipc_krb5(&cli, &ads->ldap.ip, 
-               ads->config.ldap_server_name)) )
-       {
+       r->in.debug             = true;
+       r->in.dc_name           = opt_host;
+       r->in.domain_name       = lp_realm();
+       r->in.admin_account     = opt_user_name;
+       r->in.admin_password    = net_prompt_pass(opt_user_name);
+       r->in.modify_config     = lp_config_backend_is_registry();
+       r->in.unjoin_flags      = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+                                 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+
+       werr = libnet_Unjoin(ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
+               d_printf("Failed to leave domain: %s\n",
+                        r->out.error_string ? r->out.error_string :
+                        get_friendly_werror_msg(werr));
                goto done;
        }
-       
-       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &short_domain_name, &dom_sid )) ) {
+
+       if (W_ERROR_IS_OK(werr)) {
+               d_printf("Deleted account for '%s' in realm '%s'\n",
+                       r->in.machine_name, r->out.dns_domain_name);
                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);
-               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);
-               }
+       /* We couldn't delete it - see if the disable succeeded. */
+       if (r->out.disabled_machine_account) {
+               d_printf("Disabled account for '%s' in realm '%s'\n",
+                       r->in.machine_name, r->out.dns_domain_name);
+               werr = WERR_OK;
+               goto done;
        }
 
-done:
+       d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+                 r->in.machine_name, r->out.dns_domain_name);
 
-       if ( cli ) 
-               cli_shutdown(cli);
+ done:
+       TALLOC_FREE(r);
+       TALLOC_FREE(ctx);
 
-       ads_destroy(&ads);
-       TALLOC_FREE( ctx );
+       if (W_ERROR_IS_OK(werr)) {
+               return 0;
+       }
 
-       return ret;
+       return -1;
 }
 
 static NTSTATUS net_ads_join_ok(void)
@@ -882,7 +889,7 @@ static NTSTATUS net_ads_join_ok(void)
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       net_use_machine_password();
+       net_use_krb_machine_account();
 
        status = ads_startup(True, &ads);
        if (!ADS_ERR_OK(status)) {
@@ -904,7 +911,7 @@ int net_ads_testjoin(int argc, const char **argv)
        /* Display success or failure */
        status = net_ads_join_ok();
        if (!NT_STATUS_IS_OK(status)) {
-               fprintf(stderr,"Join to domain is not valid: %s\n", 
+               fprintf(stderr,"Join to domain is not valid: %s\n",
                        get_friendly_nt_error_msg(status));
                return -1;
        }
@@ -917,378 +924,27 @@ int net_ads_testjoin(int argc, const char **argv)
   Simple configu checks before beginning the join
  ********************************************************************/
 
-static NTSTATUS check_ads_config( void )
+static WERROR 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;
+               return WERR_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;
+               return WERR_INVALID_COMPUTER_NAME;
        }
 
        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);
+                       "join to succeed.\n", get_dyn_CONFIGFILE());
+               return WERR_INVALID_PARAM;
        }
 
-       /* 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 );
+       return WERR_OK;
 }
 
 /*******************************************************************
@@ -1298,14 +954,13 @@ static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
 #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 );
-
+                     const char *pszDomainName, const char *pszHostName,
+                     const struct sockaddr_storage *sslist,
+                     size_t num_addrs );
 
 static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
                                        const char *machine_name,
-                                       const struct in_addr *addrs,
+                                       const struct sockaddr_storage *addrs,
                                        int num_addrs)
 {
        struct dns_rr_ns *nameservers = NULL;
@@ -1313,8 +968,8 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        DNS_ERROR dns_err;
        fstring dns_server;
-       const char *dnsdomain = NULL;   
-       char *root_domain = NULL;       
+       const char *dnsdomain = NULL;
+       char *root_domain = NULL;
 
        if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
                d_printf("No DNS domain configured for %s. "
@@ -1327,23 +982,23 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
        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 
+                  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->ldap.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;                              
-                       }                       
+                               goto done;
+                       }
                }
-               
-               ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE, 
+
+               ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
                                       "(objectclass=*)", rootname_attrs, &msg);
                if (!ADS_ERR_OK(ads_status)) {
                        goto done;
@@ -1351,7 +1006,7 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
 
                root_dn = ads_pull_string(ads, ctx, msg,  "rootDomainNamingContext");
                if ( !root_dn ) {
-                       ads_msgfree( ads, msg );                        
+                       ads_msgfree( ads, msg );
                        goto done;
                }
 
@@ -1363,15 +1018,15 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
                /* 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)) {                     
+
+               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;                
-               
+               dnsdomain = root_domain;
+
        }
 
        /* Now perform the dns update - we'll try non-secure and if we fail,
@@ -1387,14 +1042,14 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
 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;
+       struct sockaddr_storage *iplist = NULL;
        fstring machine_name;
        NTSTATUS status;
 
@@ -1406,7 +1061,7 @@ static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
 
        num_addrs = get_my_ip_address( &iplist );
        if ( num_addrs <= 0 ) {
-               DEBUG(4,("net_ads_join: Failed to find my non-loopback IP "
+               DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
                         "addresses!\n"));
                return NT_STATUS_INVALID_PARAMETER;
        }
@@ -1419,23 +1074,9 @@ static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
 #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");
@@ -1458,59 +1099,45 @@ static int net_ads_join_usage(int argc, const char **argv)
 
 /*******************************************************************
  ********************************************************************/
+
 int net_ads_join(int argc, const char **argv)
 {
-       ADS_STRUCT *ads = NULL;
-       ADS_STATUS status;
-       NTSTATUS nt_status;
-       char *machine_account = NULL;
-       char *short_domain_name = NULL;
-       char *tmp_password, *password;
        TALLOC_CTX *ctx = NULL;
-       DOM_SID *domain_sid = NULL;
-       BOOL createupn = False;
+       struct libnet_JoinCtx *r = NULL;
+       const char *domain = lp_realm();
+       WERROR werr = WERR_SETUP_NOT_JOINED;
+       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 */
+       bool modify_config = lp_config_backend_is_registry();
 
-       get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
+       if (!modify_config) {
 
-       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;
+               werr = check_ads_config();
+               if (!W_ERROR_IS_OK(werr)) {
+                       d_fprintf(stderr, "Invalid configuration.  Exiting....\n");
+                       goto fail;
+               }
        }
 
-       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;
+       if (!(ctx = talloc_init("net_ads_join"))) {
+               d_fprintf(stderr, "Could not initialise talloc context.\n");
+               werr = WERR_NOMEM;
                goto fail;
        }
 
-       if (!(ctx = talloc_init("net_ads_join"))) {
-               d_fprintf(stderr, "Could not initialise talloc context.\n");
-               nt_status = NT_STATUS_NO_MEMORY;
+       use_in_memory_ccache();
+
+       werr = libnet_init_JoinCtx(ctx, &r);
+       if (!W_ERROR_IS_OK(werr)) {
                goto fail;
        }
 
        /* process additional command line args */
-       
+
        for ( i=0; i<argc; i++ ) {
                if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
                        createupn = True;
@@ -1519,201 +1146,119 @@ int net_ads_join(int argc, const char **argv)
                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;
+                               werr = WERR_INVALID_PARAM;
                                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;
+                               werr = WERR_INVALID_PARAM;
                                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;
+                               werr = WERR_INVALID_PARAM;
                                goto fail;
-                       }               
+                       }
                }
                else {
-                       d_fprintf(stderr, "Bad option: %s\n", argv[i]);
-                       nt_status = NT_STATUS_INVALID_PARAMETER;
-                       goto fail;
+                       domain = argv[i];
                }
        }
 
-       /* 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;
-               }
+       if (!*domain) {
+               d_fprintf(stderr, "Please supply a valid domain name\n");
+               werr = WERR_INVALID_PARAM;
+               goto fail;
        }
 
        /* Do the domain join here */
 
-       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)));
+       r->in.domain_name       = domain;
+       r->in.create_upn        = createupn;
+       r->in.upn               = machineupn;
+       r->in.account_ou        = create_in_ou;
+       r->in.os_name           = os_name;
+       r->in.os_version        = os_version;
+       r->in.dc_name           = opt_host;
+       r->in.admin_account     = opt_user_name;
+       r->in.admin_password    = net_prompt_pass(opt_user_name);
+       r->in.debug             = true;
+       r->in.modify_config     = modify_config;
+       r->in.join_flags        = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+                                 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
+                                 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
+
+       werr = libnet_Join(ctx, r);
+       if (!W_ERROR_IS_OK(werr)) {
                goto fail;
        }
 
        /* 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);
+
+       if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
+               d_printf("The workgroup in %s does not match the short\n", get_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);
-       }
-       
-       d_printf("Using short domain name -- %s\n", short_domain_name);
-
-       /*  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;
+               d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
+               d_printf("You should set \"workgroup = %s\" in %s.\n",
+                        r->out.netbios_domain_name, get_dyn_CONFIGFILE());
        }
 
-       /* 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;
-       }       
-
-       /* 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;
-       }
+       d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
 
-       if ( !net_derive_salting_principal( ctx, ads ) ) {
-               DEBUG(1,("Failed to determine salting principal\n"));
-               goto fail;
+       if (r->out.dns_domain_name) {
+               d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
+                       r->out.dns_domain_name);
+       } else {
+               d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
+                       r->out.netbios_domain_name);
        }
 
-       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");
+#if defined(WITH_DNS_UPDATES)
+       if (r->out.domain_is_ad) {
+               /* We enter this block with user creds */
+               ADS_STRUCT *ads_dns = NULL;
+
+               if ( (ads_dns = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
+                       /* kinit with the machine password */
+
+                       use_in_memory_ccache();
+                       asprintf( &ads_dns->auth.user_name, "%s$", global_myname() );
+                       ads_dns->auth.password = secrets_fetch_machine_password(
+                               r->out.netbios_domain_name, NULL, NULL );
+                       ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
+                       ads_kinit_password( ads_dns );
                }
-       }
 
-       /* 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");
+               if ( !ads_dns || !NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns )) ) {
+                       d_fprintf( stderr, "DNS update failed!\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"));
+               /* exit from this block using machine creds */
+               ads_destroy(&ads_dns);
        }
-
-#if defined(WITH_DNS_UPDATES)
-       /* We enter this block with user creds */
-       ads_kdestroy( NULL );   
-       ads_destroy(&ads);
-       ads = NULL;
-       
-       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 );
-       }
-       
-       if ( !ads || !NT_STATUS_IS_OK(net_update_dns( ctx, ads )) ) {
-               d_fprintf( stderr, "DNS update failed!\n" );
-       }
-       
-       /* 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(r);
        TALLOC_FREE( ctx );
-       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);
+       d_printf("Failed to join domain: %s\n",
+               r && r->out.error_string ? r->out.error_string :
+               get_friendly_werror_msg(werr));
        TALLOC_FREE( ctx );
-        ads_destroy(&ads);
 
         return -1;
-
 }
 
 /*******************************************************************
  ********************************************************************/
+
 static int net_ads_dns_usage(int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
@@ -1730,20 +1275,20 @@ static int net_ads_dns_usage(int argc, const char **argv)
 
 /*******************************************************************
  ********************************************************************/
+
 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");
+               d_fprintf(stderr, "net ads dns register\n");
                return -1;
        }
 
@@ -1759,18 +1304,18 @@ static int net_ads_dns_register(int argc, const char **argv)
                return -1;
        }
 
-       if ( !NT_STATUS_IS_OK(net_update_dns(ctx, 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");
@@ -1786,7 +1331,7 @@ static int net_ads_dns_gethostbyname(int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
        DNS_ERROR err;
-       
+
 #ifdef DEVELOPER
        talloc_enable_leak_report();
 #endif
@@ -1896,7 +1441,7 @@ 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, "Server '%s' not found: %s\n", 
+               d_fprintf(stderr, "Server '%s' not found: %s\n",
                        servername, ads_errstr(rc));
                ads_msgfree(ads, res);
                ads_destroy(&ads);
@@ -1924,7 +1469,7 @@ static int net_ads_printer_publish(int argc, const char **argv)
        const char *servername, *printername;
        struct cli_state *cli;
        struct rpc_pipe_client *pipe_hnd;
-       struct in_addr          server_ip;
+       struct sockaddr_storage server_ss;
        NTSTATUS nt_status;
        TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
        ADS_MODLIST mods = ads_init_mods(mem_ctx);
@@ -1941,7 +1486,7 @@ static int net_ads_printer_publish(int argc, const char **argv)
                talloc_destroy(mem_ctx);
                return net_ads_printer_usage(argc, argv);
        }
-       
+
        printername = argv[0];
 
        if (argc == 2) {
@@ -1949,17 +1494,17 @@ static int net_ads_printer_publish(int argc, const char **argv)
        } else {
                servername = global_myname();
        }
-               
+
        /* Get printer data from SPOOLSS */
 
-       resolve_name(servername, &server_ip, 0x20);
+       resolve_name(servername, &server_ss, 0x20);
 
-       nt_status = cli_full_connection(&cli, global_myname(), servername, 
-                                       &server_ip, 0,
-                                       "IPC$", "IPC",  
+       nt_status = cli_full_connection(&cli, global_myname(), servername,
+                                       &server_ss, 0,
+                                       "IPC$", "IPC",
                                        opt_user_name, opt_workgroup,
-                                       opt_password ? opt_password : "", 
-                                       CLI_FULL_CONNECTION_USE_KERBEROS, 
+                                       opt_password ? opt_password : "",
+                                       CLI_FULL_CONNECTION_USE_KERBEROS,
                                        Undefined, NULL);
 
        if (NT_STATUS_IS_ERR(nt_status)) {
@@ -2027,12 +1572,12 @@ static int net_ads_printer_publish(int argc, const char **argv)
                talloc_destroy(mem_ctx);
                 return -1;
         }
+
         d_printf("published printer\n");
        SAFE_FREE(prt_dn);
        ads_destroy(&ads);
        talloc_destroy(mem_ctx);
+
        return 0;
 }
 
@@ -2098,7 +1643,7 @@ static int net_ads_printer(int argc, const char **argv)
                {"REMOVE", net_ads_printer_remove},
                {NULL, NULL}
        };
-       
+
        return net_run_function(argc, argv, func, net_ads_printer_usage);
 }
 
@@ -2130,7 +1675,7 @@ static int net_ads_password(int argc, const char **argv)
                user = c;
        }
 
-       use_in_memory_ccache();    
+       use_in_memory_ccache();
        c = strchr_m(auth_principal, '@');
        if (c) {
                realm = ++c;
@@ -2138,7 +1683,7 @@ static int net_ads_password(int argc, const char **argv)
                realm = lp_realm();
        }
 
-       /* use the realm so we can eventually change passwords for users 
+       /* use the realm so we can eventually change passwords for users
        in realms other than default */
        if (!(ads = ads_init(realm, opt_workgroup, opt_host))) {
                return -1;
@@ -2147,8 +1692,8 @@ static int net_ads_password(int argc, const char **argv)
        /* we don't actually need a full connect, but it's the easy way to
                fill in the KDC's addresss */
        ads_connect(ads);
-    
-       if (!ads || !ads->config.realm) {
+
+       if (!ads->config.realm) {
                d_fprintf(stderr, "Didn't find the kerberos server!\n");
                return -1;
        }
@@ -2161,7 +1706,7 @@ static int net_ads_password(int argc, const char **argv)
                free(prompt);
        }
 
-       ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, 
+       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: %s\n", ads_errstr(ret));
@@ -2176,7 +1721,7 @@ static int net_ads_password(int argc, const char **argv)
 }
 
 int net_ads_changetrustpw(int argc, const char **argv)
-{    
+{
        ADS_STRUCT *ads;
        char *host_principal;
        fstring my_name;
@@ -2187,7 +1732,7 @@ int net_ads_changetrustpw(int argc, const char **argv)
                return -1;
        }
 
-       net_use_machine_password();
+       net_use_krb_machine_account();
 
        use_in_memory_ccache();
 
@@ -2208,7 +1753,7 @@ int net_ads_changetrustpw(int argc, const char **argv)
                SAFE_FREE(host_principal);
                return -1;
        }
-    
+
        d_printf("Password change for principal %s succeeded.\n", host_principal);
 
        if (lp_use_kerberos_keytab()) {
@@ -2270,7 +1815,7 @@ static int net_ads_search(int argc, const char **argv)
                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));
 
@@ -2324,14 +1869,14 @@ static int net_ads_dn(int argc, const char **argv)
        dn = argv[0];
        attrs = (argv + 1);
 
-       rc = ads_do_search_all(ads, dn, 
+       rc = ads_do_search_all(ads, dn,
                               LDAP_SCOPE_BASE,
                               "(objectclass=*)", attrs, &res);
        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));
 
@@ -2395,7 +1940,7 @@ static int net_ads_sid(int argc, const char **argv)
                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));
 
@@ -2505,6 +2050,120 @@ use keytab functions.\n");
        return net_run_function(argc, argv, func, net_ads_keytab_usage);
 }
 
+static int net_ads_kerberos_usage(int argc, const char **argv)
+{
+       d_printf(
+               "net ads kerberos <COMMAND>\n"\
+               "<COMMAND> can be either:\n"\
+               "  RENEW     Renew TGT from existing credential cache\n"\
+               "  PAC       Dumps the Kerberos PAC\n"\
+               "  KINIT     Retrieve Ticket Granting Ticket (TGT)\n"\
+               "\n"
+       );
+
+       return -1;
+}
+
+static int net_ads_kerberos_renew(int argc, const char **argv)
+{
+       int ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
+       if (ret) {
+               d_printf("failed to renew kerberos ticket: %s\n",
+                       error_message(ret));
+       }
+       return ret;
+}
+
+static int net_ads_kerberos_pac(int argc, const char **argv)
+{
+       struct PAC_DATA *pac = NULL;
+       struct PAC_LOGON_INFO *info = NULL;
+       TALLOC_CTX *mem_ctx = NULL;
+       NTSTATUS status;
+       int ret = -1;
+
+       mem_ctx = talloc_init("net_ads_kerberos_pac");
+       if (!mem_ctx) {
+               goto out;
+       }
+
+       opt_password = net_prompt_pass(opt_user_name);
+
+       status = kerberos_return_pac(mem_ctx,
+                                    opt_user_name,
+                                    opt_password,
+                                    0,
+                                    NULL,
+                                    NULL,
+                                    NULL,
+                                    True,
+                                    True,
+                                    2592000, /* one month */
+                                    &pac);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_printf("failed to query kerberos PAC: %s\n",
+                       nt_errstr(status));
+               goto out;
+       }
+
+       info = get_logon_info_from_pac(pac);
+       if (info) {
+               const char *s;
+               s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
+               d_printf("The Pac: %s\n", s);
+       }
+
+       ret = 0;
+ out:
+       TALLOC_FREE(mem_ctx);
+       return ret;
+}
+
+static int net_ads_kerberos_kinit(int argc, const char **argv)
+{
+       TALLOC_CTX *mem_ctx = NULL;
+       int ret = -1;
+       NTSTATUS status;
+
+       mem_ctx = talloc_init("net_ads_kerberos_kinit");
+       if (!mem_ctx) {
+               goto out;
+       }
+
+       opt_password = net_prompt_pass(opt_user_name);
+
+       ret = kerberos_kinit_password_ext(opt_user_name,
+                                         opt_password,
+                                         0,
+                                         NULL,
+                                         NULL,
+                                         NULL,
+                                         True,
+                                         True,
+                                         2592000, /* one month */
+                                         &status);
+       if (ret) {
+               d_printf("failed to kinit password: %s\n",
+                       nt_errstr(status));
+       }
+ out:
+       return ret;
+}
+
+int net_ads_kerberos(int argc, const char **argv)
+{
+       struct functable func[] = {
+               {"KINIT", net_ads_kerberos_kinit},
+               {"RENEW", net_ads_kerberos_renew},
+               {"PAC", net_ads_kerberos_pac},
+               {"HELP", net_ads_kerberos_usage},
+               {NULL, NULL}
+       };
+
+       return net_run_function(argc, argv, func, net_ads_kerberos_usage);
+}
+
+
 int net_ads_help(int argc, const char **argv)
 {
        struct functable func[] = {
@@ -2546,10 +2205,11 @@ int net_ads(int argc, const char **argv)
                {"LOOKUP", net_ads_lookup},
                {"KEYTAB", net_ads_keytab},
                {"GPO", net_ads_gpo},
+               {"KERBEROS", net_ads_kerberos},
                {"HELP", net_ads_help},
                {NULL, NULL}
        };
-       
+
        return net_run_function(argc, argv, func, net_ads_usage);
 }
 
@@ -2566,6 +2226,11 @@ int net_ads_keytab(int argc, const char **argv)
        return net_ads_noads();
 }
 
+int net_ads_kerberos(int argc, const char **argv)
+{
+       return net_ads_noads();
+}
+
 int net_ads_usage(int argc, const char **argv)
 {
        return net_ads_noads();