r23779: Change from v2 or later to v3 or later.
[kai/samba.git] / source3 / utils / net_ads.c
index 377bfa22b70d78dcbcde337e531f15643dab127b..888c5a58d9f7d839b79f7fa9f0d66d2ac3d460a0 100644 (file)
@@ -8,7 +8,7 @@
 
    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,
@@ -218,8 +218,6 @@ static ADS_STATUS ads_startup_int(BOOL only_own_domain, uint32 auth_flags, ADS_S
        char *cp;
        const char *realm = NULL;
        BOOL tried_closest_dc = False;
-       BOOL closest_dc = False;
-       BOOL site_matches = False;
 
        /* lp_realm() should be handled by a command line param, 
           However, the join requires that realm be set in smb.conf
@@ -290,7 +288,7 @@ retry:
                        return status;
                }
        
-               if (!need_password && !second_time) {
+               if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
                        need_password = True;
                        second_time = True;
                        goto retry;
@@ -304,17 +302,11 @@ retry:
         * This is done by reconnecting to ADS because only the first call to
         * ads_connect will give us our own sitename */
 
-       closest_dc = (ads->config.flags & ADS_CLOSEST);
-       site_matches = ads_sitename_match(ads);
-
-       DEBUG(10,("ads_startup_int: DC %s closest DC\n", closest_dc ? "is":"is *NOT*"));
-       DEBUG(10,("ads_startup_int: sitenames %s match\n", site_matches ? "do":"do *NOT*"));
-
        if ((only_own_domain || !opt_host) && !tried_closest_dc) {
 
                tried_closest_dc = True; /* avoid loop */
 
-               if (!closest_dc || !site_matches) {
+               if (!ads->config.tried_closest_dc) {
 
                        namecache_delete(ads->server.realm, 0x1C);
                        namecache_delete(ads->server.workgroup, 0x1C);
@@ -819,6 +811,7 @@ static int net_ads_leave(int argc, const char **argv)
        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"));
@@ -845,15 +838,15 @@ static int net_ads_leave(int argc, const char **argv)
                goto done;
        }
        
-       saf_store( cli->server_domain, cli->desthost );
-
-       if ( !NT_STATUS_IS_OK(netdom_get_domain_sid( ctx, cli, &dom_sid )) ) {
+       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);
 
-       /* Ty and delete it via LDAP - the old way we used to. */
+       /* Try and delete it via LDAP - the old way we used to. */
 
        adsret = ads_leave_realm(ads, global_myname());
        if (ADS_ERR_OK(adsret)) {
@@ -943,8 +936,8 @@ static NTSTATUS check_ads_config( void )
        }
 
        if ( lp_security() == SEC_ADS && !*lp_realm()) {
-               d_fprintf(stderr, "realm must be set in in smb.conf for ADS "
-                       "join to succeed.\n");
+               d_fprintf(stderr, "realm must be set in in %s for ADS "
+                       "join to succeed.\n", dyn_CONFIGFILE);
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -962,7 +955,8 @@ static NTSTATUS check_ads_config( void )
  ********************************************************************/
 
 static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername, 
-                               struct in_addr *ip, DOM_SID **dom_sid, 
+                               struct in_addr *ip, char **domain, 
+                               DOM_SID **dom_sid, 
                                const char *password)
 {
        NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
@@ -973,13 +967,16 @@ static NTSTATUS net_join_domain(TALLOC_CTX *ctx, const char *servername,
                goto done;
        }
        
-       saf_store( cli->server_domain, cli->desthost );
-
-       ret = netdom_get_domain_sid( ctx, cli, dom_sid );
+       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:
@@ -1120,6 +1117,72 @@ done:
        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)
  ********************************************************************/
@@ -1127,28 +1190,50 @@ done:
 static ADS_STATUS net_precreate_machine_acct( ADS_STRUCT *ads, const char *ou )
 {
        ADS_STATUS rc = ADS_ERROR(LDAP_SERVER_DOWN);
-       char *dn, *ou_str;
+       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) {
-               SAFE_FREE(ou_str);
-               return ADS_ERROR(LDAP_NO_MEMORY);
+       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);
-       ads_msgfree(ads, res);
+       if (!ADS_ERR_OK(rc)) {
+               d_fprintf(stderr, "The specified OU does not exist.\n");
+               goto done;
+       }
 
-       if (ADS_ERR_OK(rc)) {
                /* Attempt to create the machine account and bail if this fails.
                   Assume that the admin wants exactly what they requested */
 
                rc = ads_create_machine_acct( ads, global_myname(), dn );
-               if ( rc.error_type == ENUM_ADS_ERROR_LDAP && rc.err.rc == LDAP_ALREADY_EXISTS ) {
-                       rc = ADS_SUCCESS;
+       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 );
 
@@ -1215,79 +1300,125 @@ static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
 *******************************************************************/
 
 #if defined(WITH_DNS_UPDATES)
-static BOOL net_update_dns( TALLOC_CTX *ctx, ADS_STRUCT *ads ) 
+#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)
 {
-       int num_addrs;
-       struct in_addr *iplist = NULL;
        struct dns_rr_ns *nameservers = NULL;
        int ns_count = 0;
-       int ret = 0;
-       NTSTATUS dns_status;
-       fstring machine_name;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       DNS_ERROR dns_err;
        fstring dns_server;
-       const char *dnsdomain;
-       ADS_STRUCT *ads_s = NULL;
-               
-       name_to_fqdn( machine_name, global_myname() );
-       strlower_m( machine_name );
+       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);
+               d_printf("No DNS domain configured for %s. "
+                        "Unable to perform DNS Update.\n", machine_name);
+               status = NT_STATUS_INVALID_PARAMETER;
                goto done;
        }
        dnsdomain++;
 
-       dns_status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
-       if ( !NT_STATUS_IS_OK(dns_status) || (ns_count == 0)) {
-               DEBUG(3,("net_ads_join: Failed to find name server for the %s realm\n",
-                       ads->config.realm));
-               goto done;
-       }
+       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) */
 
-       /* Get our ip address (not the 127.0.0.x address but a real ip address) */
+               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;
+               }
 
-       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"));
-               ret = -1;
-               goto done;
-       }
+               root_dn = ads_pull_string(ads, ctx, msg,  "rootDomainNamingContext");
+               if ( !root_dn ) {
+                       ads_msgfree( ads, msg );                        
+                       goto done;
+               }
 
-       /* Drop the user creds  */
+               root_domain = ads_build_domain( root_dn );
 
-       ads_kdestroy( NULL );
+               /* cleanup */
+               ads_msgfree( ads, msg );
 
-       ads_s = ads_init( ads->server.realm, ads->server.workgroup, ads->server.ldap_server );
-       if ( !ads_s ) {
-               DEBUG(1,("net_ads_join: ads_init() failed!\n"));
-               ret = -1;
-               goto done;
-       }
+               /* try again for NS servers */
 
-       /* kinit with the machine password */
+               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;
+               }
 
-       asprintf( &ads_s->auth.user_name, "%s$", global_myname() );
-       ads_s->auth.password = secrets_fetch_machine_password( lp_workgroup(), NULL, NULL );
-       ads_s->auth.realm = SMB_STRDUP( lp_realm() );
-       ads_kinit_password( ads_s );
+               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 */
+       /* 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 );
 
-       ret = DoDNSUpdate(dns_server, dnsdomain, machine_name, iplist, num_addrs );
-       if ( ret ) {
-               DEBUG(1, ("Error creating dns update!\n"));
+       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 );
-       if ( ads_s )
-               ads_destroy( &ads_s );
-               
-       return (ret == 0);
+       return status;
 }
 #endif
 
@@ -1318,6 +1449,13 @@ static int net_ads_join_usage(int argc, const char **argv)
        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;
 }
@@ -1331,15 +1469,18 @@ int net_ads_join(int argc, const char **argv)
        ADS_STATUS status;
        NTSTATUS nt_status;
        char *machine_account = NULL;
-       const char *short_domain_name = NULL;
+       char *short_domain_name = NULL;
        char *tmp_password, *password;
-       struct cldap_netlogon_reply cldap_reply;
        TALLOC_CTX *ctx = NULL;
        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)) {
@@ -1347,6 +1488,10 @@ int net_ads_join(int argc, const char **argv)
                goto fail;
        }
 
+       /* find a DC to initialize the server affinity cache */
+
+       get_dc_name( lp_workgroup(), lp_realm(), dc_name, &dcip );
+
        status = ads_startup(True, &ads);
        if (!ADS_ERR_OK(status)) {
                DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
@@ -1355,9 +1500,9 @@ int net_ads_join(int argc, const char **argv)
        }
 
        if (strcmp(ads->config.realm, lp_realm()) != 0) {
-               d_fprintf(stderr, "realm of remote server (%s) and realm in smb.conf "
+               d_fprintf(stderr, "realm of remote server (%s) and realm in %s "
                        "(%s) DO NOT match.  Aborting join\n", ads->config.realm, 
-                       lp_realm());
+                       dyn_CONFIGFILE, lp_realm());
                nt_status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
@@ -1377,7 +1522,21 @@ 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");
+                               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;
                        }               
@@ -1396,7 +1555,7 @@ int net_ads_join(int argc, const char **argv)
                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", argv[0]);
+                               "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);
@@ -1410,29 +1569,21 @@ int net_ads_join(int argc, const char **argv)
        password = talloc_strdup(ctx, tmp_password);
        
        nt_status = net_join_domain(ctx, ads->config.ldap_server_name, 
-                                   &ads->ldap_ip, &domain_sid, password);
+                                   &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;
        }
-       
+
        /* Check the short name of the domain */
        
-       ZERO_STRUCT( cldap_reply );
-       
-       if ( ads_cldap_netlogon( ads->config.ldap_server_name, 
-               ads->server.realm, &cldap_reply ) ) 
-       {
-               short_domain_name = talloc_strdup( ctx, cldap_reply.netbios_domain );
-               if ( !strequal(lp_workgroup(), short_domain_name) ) {
-                       d_printf("The workgroup in smb.conf does not match the short\n");
-                       d_printf("domain name obtained from the server.\n");
-                       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 ( !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);
        }
        
        d_printf("Using short domain name -- %s\n", short_domain_name);
@@ -1504,22 +1655,47 @@ int net_ads_join(int argc, const char **argv)
                }
        }
 
+       /* 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;
        
-       if ( !net_update_dns( ctx, ads ) ) {
+       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->config.realm);
+       d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->server.realm);
 
        SAFE_FREE(machine_account);
        TALLOC_FREE( ctx );
@@ -1559,39 +1735,35 @@ static int net_ads_dns_usage(int argc, const char **argv)
 /*******************************************************************
  ********************************************************************/
  
-static int net_ads_dns(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;
-       BOOL register_dns = False;
-       int i;
        
-       status = ads_startup(True, &ads);
-       if ( !ADS_ERR_OK(status) ) {
-               DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+#ifdef DEVELOPER
+       talloc_enable_leak_report();
+#endif
+       
+       if (argc > 0) {
+               d_fprintf(stderr, "net ads dns register <name> <ip>\n");
                return -1;
        }
 
        if (!(ctx = talloc_init("net_ads_dns"))) {
-               DEBUG(0, ("Could not initialise talloc context\n"));
+               d_fprintf(stderr, "Could not initialise talloc context\n");
                return -1;
        }
 
-       /* process additional command line args */
-       
-       for ( i=0; i<argc; i++ ) {
-               if ( strequal(argv[i], "register") ) {
-                       register_dns = True;
-               }
-               else {
-                       d_fprintf(stderr, "Bad option: %s\n", argv[i]);
-                       return -1;
-               }
+       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 ( !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 );
@@ -1610,6 +1782,43 @@ static int net_ads_dns(int argc, const char **argv)
 #endif
 }
 
+#if defined(WITH_DNS_UPDATES)
+DNS_ERROR do_gethostbyname(const char *server, const char *host);
+#endif
+
+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
+
+       if (argc != 2) {
+               d_fprintf(stderr, "net ads dns gethostbyname <server> "
+                         "<name>\n");
+               return -1;
+       }
+
+       err = do_gethostbyname(argv[0], argv[1]);
+
+       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);
+}
+
 /*******************************************************************
  ********************************************************************/
 
@@ -1712,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;
@@ -1730,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;
+       char *srv_cn_escaped = NULL, *printername_escaped = NULL;
        LDAPMessage *res = NULL;
 
        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);
        }
        
@@ -1764,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;
        }
 
@@ -1775,37 +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((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;
        }
 
        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;
 }
@@ -2069,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;
@@ -2187,16 +2418,18 @@ 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;
@@ -2245,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}
        };