librpc: add dcerpc_get_auth_{type,level,context_id}() helper functions
[samba.git] / dfs_server / dfs_server_ad.c
index 22d3263..84a19bd 100644 (file)
@@ -26,6 +26,8 @@
 #include "param/param.h"
 #include "lib/tsocket/tsocket.h"
 #include "dfs_server/dfs_server_ad.h"
+#include "lib/util/util_net.h"
+#include "libds/common/roles.h"
 
 #define MAX_DFS_RESPONSE 56*1024 /* 56 Kb */
 
@@ -37,6 +39,22 @@ struct dc_set {
        uint32_t count;
 };
 
+static void shuffle_dc_set(struct dc_set *list)
+{
+       uint32_t i;
+
+       for (i = list->count; i > 1; i--) {
+               uint32_t r;
+               const char *tmp;
+
+               r = generate_random() % i;
+
+               tmp = list->names[i - 1];
+               list->names[i - 1] = list->names[r];
+               list->names[r] = tmp;
+       }
+}
+
 /*
   fill a referral type structure
  */
@@ -49,8 +67,7 @@ static NTSTATUS fill_normal_dfs_referraltype(TALLOC_CTX *mem_ctx,
        ZERO_STRUCTP(ref);
        switch (version) {
        case 4:
-               version = 3;
-# if 0
+               ref->version = version;
                /* For the moment there is a bug with XP that don't seems to appriciate much
                 * level4 so we return just level 3 for everyone
                 */
@@ -75,7 +92,6 @@ static NTSTATUS fill_normal_dfs_referraltype(TALLOC_CTX *mem_ctx,
                        return NT_STATUS_NO_MEMORY;
                }
                return NT_STATUS_OK;
-#endif
        case 3:
                ref->version = version;
                ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
@@ -117,6 +133,13 @@ static NTSTATUS fill_domain_dfs_referraltype(TALLOC_CTX *mem_ctx,
                DEBUG(8, ("Called fill_domain_dfs_referraltype\n"));
                ref->version = version;
                ref->referral.v3.server_type = DFS_SERVER_NON_ROOT;
+#if 0
+               /* We use to have variable size, on Windows 2008R2 it's the same
+                * and it seems that it gives better results so ... let's use the same
+                * size.
+                *
+                * Additional note: XP SP2 will ask for version 3 and SP3 for version 4.
+                */
                /*
                 * It's hard coded ... don't think it's a good way but the
                 * sizeof return not the correct values
@@ -130,8 +153,11 @@ static NTSTATUS fill_domain_dfs_referraltype(TALLOC_CTX *mem_ctx,
                } else {
                        ref->referral.v3.size = 34;
                }
+#endif
+               /* As seen in w2k8r2 it always return the null GUID */
+               ref->referral.v3.size = 34;
                ref->referral.v3.entry_flags = DFS_FLAG_REFERRAL_DOMAIN_RESP;
-               ref->referral.v3.ttl = 600; /* As w2k3 */
+               ref->referral.v3.ttl = 600; /* As w2k3 and w2k8r2*/
                ref->referral.v3.referrals.r2.special_name = talloc_strdup(mem_ctx,
                                                                        domain);
                if (ref->referral.v3.referrals.r2.special_name == NULL) {
@@ -189,22 +215,28 @@ static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
         * Search all the object of class server in this site
         */
        dc_list = talloc_array(r, const char *, r->count);
-       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(dc_list, r);
+       if (dc_list == NULL) {
+               TALLOC_FREE(r);
+               return NT_STATUS_NO_MEMORY;
+       }
 
        /* TODO put some random here in the order */
        list->names = talloc_realloc(list, list->names, const char *, list->count + r->count);
-       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names, r);
+       if (list->names == NULL) {
+               TALLOC_FREE(r);
+               return NT_STATUS_NO_MEMORY;
+       }
 
        for (i = 0; i<r->count; i++) {
                struct ldb_dn  *dn;
-               struct ldb_result *r2;
+               struct ldb_message *msg;
 
                dn = ldb_msg_find_attr_as_dn(ldb, ctx, r->msgs[i], "serverReference");
                if (!dn) {
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
-               ret = ldb_search(ldb, r, &r2, dn, LDB_SCOPE_BASE, attrs2, "(objectClass=computer)");
+               ret = dsdb_search_one(ldb, r, &msg, dn, LDB_SCOPE_BASE, attrs2, 0, "(objectClass=computer)");
                if (ret != LDB_SUCCESS) {
                        DEBUG(2,(__location__ ": Search for computer on %s failed - %s\n",
                                 ldb_dn_get_linearized(dn), ldb_errstring(ldb)));
@@ -212,7 +244,7 @@ static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
                }
 
                if (dofqdn) {
-                       const char *dns = ldb_msg_find_attr_as_string(r2->msgs[0], "dNSHostName", NULL);
+                       const char *dns = ldb_msg_find_attr_as_string(msg, "dNSHostName", NULL);
                        if (dns == NULL) {
                                DEBUG(2,(__location__ ": dNSHostName missing on %s\n",
                                         ldb_dn_get_linearized(dn)));
@@ -221,19 +253,25 @@ static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
                        }
 
                        list->names[list->count] = talloc_strdup(list->names, dns);
-                       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(list->names[list->count], r);
+                       if (list->names[list->count] == NULL) {
+                               TALLOC_FREE(r);
+                               return NT_STATUS_NO_MEMORY;
+                       }
                } else {
                        char *tmp;
-                       const char *acct = ldb_msg_find_attr_as_string(r2->msgs[0], "sAMAccountName", NULL);
-                       if (acct == NULL) {
+                       const char *aname = ldb_msg_find_attr_as_string(msg, "sAMAccountName", NULL);
+                       if (aname == NULL) {
                                DEBUG(2,(__location__ ": sAMAccountName missing on %s\n",
                                         ldb_dn_get_linearized(dn)));
                                talloc_free(r);
                                return NT_STATUS_INTERNAL_ERROR;
                        }
 
-                       tmp = talloc_strdup(list->names, acct);
-                       NT_STATUS_HAVE_NO_MEMORY_AND_FREE(tmp, r);
+                       tmp = talloc_strdup(list->names, aname);
+                       if (tmp == NULL) {
+                               TALLOC_FREE(r);
+                               return NT_STATUS_NO_MEMORY;
+                       }
 
                        /* Netbios name is also the sAMAccountName for
                           computer but without the final $ */
@@ -241,9 +279,11 @@ static NTSTATUS get_dcs_insite(TALLOC_CTX *ctx, struct ldb_context *ldb,
                        list->names[list->count] = tmp;
                }
                list->count++;
-               talloc_free(r2);
+               talloc_free(msg);
        }
 
+       shuffle_dc_set(list);
+
        talloc_free(r);
        return NT_STATUS_OK;
 }
@@ -269,7 +309,7 @@ static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
        int ret;
        uint32_t current_pos = 0;
        NTSTATUS status;
-       TALLOC_CTX *subctx = talloc_new(ctx);
+       TALLOC_CTX *subctx;
 
        *pset_list = set_list = NULL;
 
@@ -325,14 +365,27 @@ static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
                /* All of this was to get the DN of the searched_site */
                sitedn = r->msgs[0]->dn;
 
-               set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+1);
-               NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
+               /*
+                * We will realloc + 2 because we will need one additional place
+                * for element at current_pos + 1 for the NULL element
+                */
+               set_list = talloc_realloc(subctx, set_list, struct dc_set *, current_pos+2);
+               if (set_list == NULL) {
+                       TALLOC_FREE(subctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
                set_list[current_pos] = talloc(set_list, struct dc_set);
-               NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
+               if (set_list[current_pos] == NULL) {
+                       TALLOC_FREE(subctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
                set_list[current_pos]->names = NULL;
                set_list[current_pos]->count = 0;
+
+               set_list[current_pos+1] = NULL;
+
                status = get_dcs_insite(subctx, ldb, sitedn,
                                        set_list[current_pos], need_fqdn);
                if (!NT_STATUS_IS_OK(status)) {
@@ -375,10 +428,16 @@ static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
                 */
                set_list = talloc_realloc(subctx, set_list, struct dc_set *,
                                          current_pos+2);
-               NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list, subctx);
+               if (set_list == NULL) {
+                       TALLOC_FREE(subctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
                set_list[current_pos] = talloc(ctx, struct dc_set);
-               NT_STATUS_HAVE_NO_MEMORY_AND_FREE(set_list[current_pos], subctx);
+               if (set_list[current_pos] == NULL) {
+                       TALLOC_FREE(subctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
 
                set_list[current_pos]->names = NULL;
                set_list[current_pos]->count = 0;
@@ -415,8 +474,6 @@ static NTSTATUS get_dcs(TALLOC_CTX *ctx, struct ldb_context *ldb,
                        }
                }
        }
-       current_pos++;
-       set_list[current_pos] = NULL;
 
        *pset_list = talloc_move(ctx, &set_list);
        talloc_free(subctx);
@@ -439,7 +496,7 @@ static NTSTATUS dodomain_referral(struct loadparm_context *lp_ctx,
        /* In the future this needs to be fetched from the ldb */
        uint32_t found_domain = 2;
 
-       if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
+       if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) {
                DEBUG(10 ,("Received a domain referral request on a non DC\n"));
                return NT_STATUS_INVALID_PARAMETER;
        }
@@ -521,7 +578,7 @@ static NTSTATUS dodc_referral(struct loadparm_context *lp_ctx,
        struct dfs_referral_type *referrals;
        const char *referral_str;
 
-       if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
+       if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) {
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -545,7 +602,7 @@ static NTSTATUS dodc_referral(struct loadparm_context *lp_ctx,
                }
        }
 
-       site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
+       site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL, true);
 
        status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
        if (!NT_STATUS_IS_OK(status)) {
@@ -632,7 +689,7 @@ static NTSTATUS dosysvol_referral(struct loadparm_context *lp_ctx,
        NTSTATUS status;
        struct dfs_referral_type *referrals;
 
-       if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_CONTROLLER) {
+       if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC) {
                return NT_STATUS_INVALID_PARAMETER;
        }
 
@@ -656,7 +713,7 @@ static NTSTATUS dosysvol_referral(struct loadparm_context *lp_ctx,
                }
        }
 
-       site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL);
+       site_name = samdb_client_site_name(sam_ctx, r, client_str, NULL, true);
 
        status = get_dcs(r, sam_ctx, site_name, need_fqdn, &set, 0);
        if (!NT_STATUS_IS_OK(status)) {
@@ -742,6 +799,8 @@ NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx,
        const char *dns_domain;
        const char *netbios_name;
        const char *dns_name;
+       const char **netbios_aliases;
+       char path_separator;
 
        if (!lpcfg_host_msdfs(lp_ctx)) {
                return NT_STATUS_FS_DRIVER_REQUIRED;
@@ -769,16 +828,18 @@ NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
-       while(*server_name && *server_name == '\\') {
+       path_separator = (*server_name == '/') ? '/' : '\\';
+
+       while(*server_name && *server_name == path_separator) {
                server_name++;
        }
 
-       dfs_name = strchr(server_name, '\\');
+       dfs_name = strchr_m(server_name, path_separator);
        if (dfs_name != NULL) {
                dfs_name[0] = '\0';
                dfs_name++;
 
-               link_path = strchr(dfs_name, '\\');
+               link_path = strchr_m(dfs_name, path_separator);
                if (link_path != NULL) {
                        link_path[0] = '\0';
                        link_path++;
@@ -808,7 +869,48 @@ NTSTATUS dfs_server_ad_get_referrals(struct loadparm_context *lp_ctx,
                 * handle it here.
                 */
                return NT_STATUS_NOT_FOUND;
+       }
 
+       if (is_ipaddress(server_name)) {
+               /*
+                * If it is not domain related do not
+                * handle it here.
+                */
+               return NT_STATUS_NOT_FOUND;
+       }
+
+       netbios_aliases = lpcfg_netbios_aliases(lp_ctx);
+       while (netbios_aliases && *netbios_aliases) {
+               const char *netbios_alias = *netbios_aliases;
+               char *dns_alias;
+               int cmp;
+
+               cmp = strcasecmp_m(server_name, netbios_alias);
+               if (cmp == 0) {
+                       /*
+                        * If it is not domain related do not
+                        * handle it here.
+                        */
+                       return NT_STATUS_NOT_FOUND;
+               }
+
+               dns_alias = talloc_asprintf(r, "%s.%s",
+                                           netbios_alias,
+                                           dns_domain);
+               if (dns_alias == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               cmp = strcasecmp_m(server_name, dns_alias);
+               talloc_free(dns_alias);
+               if (cmp == 0) {
+                       /*
+                        * If it is not domain related do not
+                        * handle it here.
+                        */
+                       return NT_STATUS_NOT_FOUND;
+               }
+               netbios_aliases++;
        }
 
        if ((strcasecmp_m(server_name, netbios_domain) != 0) &&