s3-libads: pass down local_service to kerberos_return_pac().
[gd/samba-autobuild/.git] / source3 / utils / net_ads.c
index 2a4b2ecec5997b4e1ce9c6ffefe92f6b222648ea..5a073b1cc71dd436ee76cfacfe65bf21fcbbbafd 100644 (file)
@@ -28,7 +28,7 @@
 #include "nsswitch/libwbclient/wbclient.h"
 #include "ads.h"
 #include "libads/cldap.h"
-#include "libads/dns.h"
+#include "../lib/addns/dnsquery.h"
 #include "../libds/common/flags.h"
 #include "librpc/gen_ndr/libnet_join.h"
 #include "libnet/libnet_join.h"
@@ -37,6 +37,8 @@
 #include "krb5_env.h"
 #include "../libcli/security/security.h"
 #include "libsmb/libsmb.h"
+#include "lib/param/loadparm.h"
+#include "utils/net_dns.h"
 
 #ifdef HAVE_ADS
 
@@ -286,7 +288,10 @@ retry:
                *cp++ = '\0';
                SAFE_FREE(ads->auth.realm);
                ads->auth.realm = smb_xstrdup(cp);
-               strupper_m(ads->auth.realm);
+               if (!strupper_m(ads->auth.realm)) {
+                       ads_destroy(&ads);
+                       return ADS_ERROR(LDAP_NO_MEMORY);
+               }
        }
 
        status = ads_connect(ads);
@@ -1018,7 +1023,7 @@ static int net_ads_leave(struct net_context *c, int argc, const char **argv)
                goto done;
        }
 
-       /* Based on what we requseted, we shouldn't get here, but if
+       /* Based on what we requested, we shouldn't get here, but if
           we did, it means the secrets were removed, and therefore
           we have left the domain */
        d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
@@ -1122,12 +1127,9 @@ static WERROR check_ads_config( void )
 
 #if defined(WITH_DNS_UPDATES)
 #include "../lib/addns/dns.h"
-DNS_ERROR DoDNSUpdate(char *pszServerName,
-                     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,
+static NTSTATUS net_update_dns_internal(struct net_context *c,
+                                       TALLOC_CTX *ctx, ADS_STRUCT *ads,
                                        const char *machine_name,
                                        const struct sockaddr_storage *addrs,
                                        int num_addrs)
@@ -1137,6 +1139,7 @@ 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 *dns_hosts_file;
        const char *dnsdomain = NULL;
        char *root_domain = NULL;
 
@@ -1148,7 +1151,9 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
        }
        dnsdomain++;
 
-       status = ads_dns_lookup_ns( ctx, dnsdomain, &nameservers, &ns_count );
+       dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
+       status = ads_dns_lookup_ns(ctx, dns_hosts_file,
+                                  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
@@ -1186,10 +1191,11 @@ 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 );
+               status = ads_dns_lookup_ns(ctx, dns_hosts_file, 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 "
+                       DEBUG(3,("net_update_dns_internal: Failed to find name server for the %s "
                         "realm\n", ads->config.realm));
                        goto done;
                }
@@ -1200,6 +1206,17 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
 
        for (i=0; i < ns_count; i++) {
 
+               uint32_t flags = DNS_UPDATE_SIGNED |
+                                DNS_UPDATE_UNSIGNED |
+                                DNS_UPDATE_UNSIGNED_SUFFICIENT |
+                                DNS_UPDATE_PROBE |
+                                DNS_UPDATE_PROBE_SUFFICIENT;
+
+               if (c->opt_force) {
+                       flags &= ~DNS_UPDATE_PROBE_SUFFICIENT;
+                       flags &= ~DNS_UPDATE_UNSIGNED_SUFFICIENT;
+               }
+
                status = NT_STATUS_UNSUCCESSFUL;
 
                /* Now perform the dns update - we'll try non-secure and if we fail,
@@ -1207,7 +1224,7 @@ static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
 
                fstrcpy( dns_server, nameservers[i].hostname );
 
-               dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
+               dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs, flags);
                if (ERR_DNS_IS_OK(dns_err)) {
                        status = NT_STATUS_OK;
                        goto done;
@@ -1234,7 +1251,8 @@ done:
        return status;
 }
 
-static NTSTATUS net_update_dns_ext(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
+static NTSTATUS net_update_dns_ext(struct net_context *c,
+                                  TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
                                   const char *hostname,
                                   struct sockaddr_storage *iplist,
                                   int num_addrs)
@@ -1248,7 +1266,9 @@ static NTSTATUS net_update_dns_ext(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
        } else {
                name_to_fqdn( machine_name, lp_netbios_name() );
        }
-       strlower_m( machine_name );
+       if (!strlower_m( machine_name )) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
 
        if (num_addrs == 0 || iplist == NULL) {
                /*
@@ -1264,18 +1284,18 @@ static NTSTATUS net_update_dns_ext(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
                iplist = iplist_alloc;
        }
 
-       status = net_update_dns_internal(mem_ctx, ads, machine_name,
+       status = net_update_dns_internal(c, mem_ctx, ads, machine_name,
                                         iplist, num_addrs);
 
        SAFE_FREE(iplist_alloc);
        return status;
 }
 
-static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, const char *hostname)
+static NTSTATUS net_update_dns(struct net_context *c, TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, const char *hostname)
 {
        NTSTATUS status;
 
-       status = net_update_dns_ext(mem_ctx, ads, hostname, NULL, 0);
+       status = net_update_dns_ext(c, mem_ctx, ads, hostname, NULL, 0);
        return status;
 }
 #endif
@@ -1295,6 +1315,8 @@ static int net_ads_join_usage(struct net_context *c, int argc, const char **argv
                   "                      E.g. \"createcomputer=Computers/Servers/Unix\"\n"
                   "                      NB: A backslash '\\' is used as escape at multiple levels and may\n"
                   "                          need to be doubled or even quadrupled.  It is not used as a separator.\n"));
+       d_printf(_("   machinepass=PASS   Set the machine password to a specific value during the join.\n"
+                  "                      The deault password is random.\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"
                   "                      NB: osName and osVer must be specified together for either to take effect.\n"
@@ -1304,8 +1326,94 @@ static int net_ads_join_usage(struct net_context *c, int argc, const char **argv
        return -1;
 }
 
-/*******************************************************************
- ********************************************************************/
+
+static void _net_ads_join_dns_updates(struct net_context *c, TALLOC_CTX *ctx, struct libnet_JoinCtx *r)
+{
+#if defined(WITH_DNS_UPDATES)
+       ADS_STRUCT *ads_dns = NULL;
+       int ret;
+       NTSTATUS status;
+
+       /*
+        * In a clustered environment, don't do dynamic dns updates:
+        * Registering the set of ip addresses that are assigned to
+        * the interfaces of the node that performs the join does usually
+        * not have the desired effect, since the local interfaces do not
+        * carry the complete set of the cluster's public IP addresses.
+        * And it can also contain internal addresses that should not
+        * be visible to the outside at all.
+        * In order to do dns updates in a clustererd setup, use
+        * net ads dns register.
+        */
+       if (lp_clustering()) {
+               d_fprintf(stderr, _("Not doing automatic DNS update in a "
+                                   "clustered setup.\n"));
+               return;
+       }
+
+       if (!r->out.domain_is_ad) {
+               return;
+       }
+
+       /*
+        * We enter this block with user creds.
+        * kinit with the machine password to do dns update.
+        */
+
+       ads_dns = ads_init(lp_realm(), NULL, r->in.dc_name);
+
+       if (ads_dns == NULL) {
+               d_fprintf(stderr, _("DNS update failed: out of memory!\n"));
+               goto done;
+       }
+
+       use_in_memory_ccache();
+
+       ret = asprintf(&ads_dns->auth.user_name, "%s$", lp_netbios_name());
+       if (ret == -1) {
+               d_fprintf(stderr, _("DNS update failed: out of memory\n"));
+               goto done;
+       }
+
+       ads_dns->auth.password = secrets_fetch_machine_password(
+               r->out.netbios_domain_name, NULL, NULL);
+       if (ads_dns->auth.password == NULL) {
+               d_fprintf(stderr, _("DNS update failed: out of memory\n"));
+               goto done;
+       }
+
+       ads_dns->auth.realm = SMB_STRDUP(r->out.dns_domain_name);
+       if (ads_dns->auth.realm == NULL) {
+               d_fprintf(stderr, _("DNS update failed: out of memory\n"));
+               goto done;
+       }
+
+       if (!strupper_m(ads_dns->auth.realm)) {
+               d_fprintf(stderr, _("strupper_m %s failed\n"), ads_dns->auth.realm);
+               goto done;
+       }
+
+       ret = ads_kinit_password(ads_dns);
+       if (ret != 0) {
+               d_fprintf(stderr,
+                         _("DNS update failed: kinit failed: %s\n"),
+                         error_message(ret));
+               goto done;
+       }
+
+       status = net_update_dns(c, ctx, ads_dns, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_fprintf( stderr, _("DNS update failed: %s\n"),
+                         nt_errstr(status));
+       }
+
+done:
+       ads_destroy(&ads_dns);
+#endif
+
+       return;
+}
+
 
 int net_ads_join(struct net_context *c, int argc, const char **argv)
 {
@@ -1315,6 +1423,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        WERROR werr = WERR_SETUP_NOT_JOINED;
        bool createupn = false;
        const char *machineupn = NULL;
+       const char *machine_password = NULL;
        const char *create_in_ou = NULL;
        int i;
        const char *os_name = NULL;
@@ -1376,6 +1485,13 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
                                goto fail;
                        }
                }
+               else if ( !strncasecmp_m(argv[i], "machinepass", strlen("machinepass")) ) {
+                       if ( (machine_password = get_string_param(argv[i])) == NULL ) {
+                               d_fprintf(stderr, _("Please supply a valid password to set as trust account password.\n"));
+                               werr = WERR_INVALID_PARAM;
+                               goto fail;
+                       }
+               }
                else {
                        domain = argv[i];
                }
@@ -1405,6 +1521,7 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        r->in.dc_name           = c->opt_host;
        r->in.admin_account     = c->opt_user_name;
        r->in.admin_password    = net_prompt_pass(c, c->opt_user_name);
+       r->in.machine_password  = machine_password;
        r->in.debug             = true;
        r->in.use_kerberos      = c->opt_kerberos;
        r->in.modify_config     = modify_config;
@@ -1437,64 +1554,19 @@ int net_ads_join(struct net_context *c, int argc, const char **argv)
        d_printf(_("Using short domain name -- %s\n"), r->out.netbios_domain_name);
 
        if (r->out.dns_domain_name) {
-               d_printf(_("Joined '%s' to realm '%s'\n"), r->in.machine_name,
+               d_printf(_("Joined '%s' to dns domain '%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 defined(WITH_DNS_UPDATES)
        /*
-        * In a clustered environment, don't do dynamic dns updates:
-        * Registering the set of ip addresses that are assigned to
-        * the interfaces of the node that performs the join does usually
-        * not have the desired effect, since the local interfaces do not
-        * carry the complete set of the cluster's public IP addresses.
-        * And it can also contain internal addresses that should not
-        * be visible to the outside at all.
-        * In order to do dns updates in a clustererd setup, use
-        * net ads dns register.
+        * We try doing the dns update (if it was compiled in).
+        * If the dns update fails, we still consider the join
+        * operation as succeeded if we came this far.
         */
-       if (lp_clustering()) {
-               d_fprintf(stderr, _("Not doing automatic DNS update in a "
-                                   "clustered setup.\n"));
-               goto done;
-       }
-
-       if (r->out.domain_is_ad) {
-               /* We enter this block with user creds */
-               ADS_STRUCT *ads_dns = NULL;
-
-               ads_dns = ads_init(lp_realm(), NULL, r->in.dc_name);
-
-               if (ads_dns == NULL) {
-                       d_fprintf(stderr, _("DNS update failed: out of memory!\n"));
-                       goto done;
-               }
-
-               /* kinit with the machine password */
-
-               use_in_memory_ccache();
-               if (asprintf( &ads_dns->auth.user_name, "%s$", lp_netbios_name()) == -1) {
-                       goto fail;
-               }
-               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);
-               strupper_m(ads_dns->auth.realm);
-               ads_kinit_password(ads_dns);
-
-               if (!NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns, NULL))) {
-                       d_fprintf( stderr, _("DNS update failed!\n"));
-               }
-
-               /* exit from this block using machine creds */
-               ads_destroy(&ads_dns);
-       }
-
-done:
-#endif
+       _net_ads_join_dns_updates(c, ctx, r);
 
        TALLOC_FREE(r);
        TALLOC_FREE( ctx );
@@ -1590,7 +1662,7 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
                return -1;
        }
 
-       ntstatus = net_update_dns_ext(ctx, ads, hostname, addrs, num_addrs);
+       ntstatus = net_update_dns_ext(c, ctx, ads, hostname, addrs, num_addrs);
        if (!NT_STATUS_IS_OK(ntstatus)) {
                d_fprintf( stderr, _("DNS update failed!\n") );
                ads_destroy( &ads );
@@ -1611,10 +1683,6 @@ static int net_ads_dns_register(struct net_context *c, int argc, const char **ar
 #endif
 }
 
-#if defined(WITH_DNS_UPDATES)
-DNS_ERROR do_gethostbyname(const char *server, const char *host);
-#endif
-
 static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char **argv)
 {
 #if defined(WITH_DNS_UPDATES)
@@ -1889,7 +1957,7 @@ static int net_ads_printer_publish(struct net_context *c, int argc, const char *
        SAFE_FREE(srv_cn_escaped);
        SAFE_FREE(printername_escaped);
 
-       nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss.syntax_id, &pipe_hnd);
+       nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss, &pipe_hnd);
        if (!NT_STATUS_IS_OK(nt_status)) {
                d_fprintf(stderr, _("Unable to open a connection to the spoolss pipe on %s\n"),
                         servername);
@@ -2034,6 +2102,7 @@ static int net_ads_password(struct net_context *c, int argc, const char **argv)
        const char *new_password = NULL;
        char *chr, *prompt;
        const char *user;
+       char pwd[256] = {0};
        ADS_STATUS ret;
 
        if (c->display_usage) {
@@ -2092,15 +2161,22 @@ static int net_ads_password(struct net_context *c, int argc, const char **argv)
        if (argv[1]) {
                new_password = (const char *)argv[1];
        } else {
+               int rc;
+
                if (asprintf(&prompt, _("Enter new password for %s:"), user) == -1) {
                        return -1;
                }
-               new_password = getpass(prompt);
+               rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true);
+               if (rc < 0) {
+                       return -1;
+               }
+               new_password = pwd;
                free(prompt);
        }
 
        ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
                                auth_password, user, new_password, ads->auth.time_offset);
+       memset(pwd, '\0', sizeof(pwd));
        if (!ADS_ERR_OK(ret)) {
                d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(ret));
                ads_destroy(&ads);
@@ -2143,7 +2219,11 @@ int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
        }
 
        fstrcpy(my_name, lp_netbios_name());
-       strlower_m(my_name);
+       if (!strlower_m(my_name)) {
+               ads_destroy(&ads);
+               return -1;
+       }
+
        if (asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm) == -1) {
                ads_destroy(&ads);
                return -1;
@@ -2213,7 +2293,7 @@ static int net_ads_search(struct net_context *c, int argc, const char **argv)
        ldap_exp = argv[0];
        attrs = (argv + 1);
 
-       rc = ads_do_search_all(ads, ads->config.bind_path,
+       rc = ads_do_search_retry(ads, ads->config.bind_path,
                               LDAP_SCOPE_SUBTREE,
                               ldap_exp, attrs, &res);
        if (!ADS_ERR_OK(rc)) {
@@ -2524,10 +2604,11 @@ static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **ar
        NTSTATUS status;
        int ret = -1;
        const char *impersonate_princ_s = NULL;
+       const char *local_service = NULL;
 
        if (c->display_usage) {
                d_printf(  "%s\n"
-                          "net ads kerberos pac\n"
+                          "net ads kerberos pac [impersonation_principal]\n"
                           "    %s\n",
                         _("Usage:"),
                         _("Dump the Kerberos PAC"));
@@ -2543,6 +2624,12 @@ static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **ar
                impersonate_princ_s = argv[0];
        }
 
+       local_service = talloc_asprintf(mem_ctx, "%s$@%s",
+                                       lp_netbios_name(), lp_realm());
+       if (local_service == NULL) {
+               goto out;
+       }
+
        c->opt_password = net_prompt_pass(c, c->opt_user_name);
 
        status = kerberos_return_pac(mem_ctx,
@@ -2556,6 +2643,7 @@ static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **ar
                                     true,
                                     2592000, /* one month */
                                     impersonate_princ_s,
+                                    local_service,
                                     &info);
        if (!NT_STATUS_IS_OK(status)) {
                d_printf(_("failed to query kerberos PAC: %s\n"),