From a3c5277a93a13432aaa90c6a5450f2c72c86948f Mon Sep 17 00:00:00 2001 From: Jim McDonough Date: Tue, 20 May 2003 13:51:43 +0000 Subject: Fix bug #96: Use DNS decompression to properly parse cldap netlogon packets, otherwise repeated components will not decode correctly. Thanks to aliguori@us.ibm.com for the fix, and lukeh@padl.com for pointing us to the right docs. (This used to be commit a8d5d74cf80c6cae3eac1daa3f88d56373789560) --- source3/utils/net_ads_cldap.c | 242 +++++++++++++++++++++++------------------- 1 file changed, 132 insertions(+), 110 deletions(-) (limited to 'source3/utils') diff --git a/source3/utils/net_ads_cldap.c b/source3/utils/net_ads_cldap.c index 86d5abea4b..e74e4b5a4c 100644 --- a/source3/utils/net_ads_cldap.c +++ b/source3/utils/net_ads_cldap.c @@ -24,28 +24,28 @@ #ifdef HAVE_ADS -struct netlogon_string { - uint32 comp_len; - char **component; - uint8 extra_flag; -}; +#define MAX_DNS_LABEL 255 + 1 struct cldap_netlogon_reply { uint32 type; uint32 flags; GUID guid; - struct netlogon_string forest; - struct netlogon_string domain; - struct netlogon_string hostname; - - struct netlogon_string netbios_domain; - struct netlogon_string netbios_hostname; + char forest[MAX_DNS_LABEL]; + char unk0[MAX_DNS_LABEL]; + char domain[MAX_DNS_LABEL]; + char hostname[MAX_DNS_LABEL]; - struct netlogon_string user_name; - struct netlogon_string site_name; + char netbios_domain[MAX_DNS_LABEL]; + char unk1[MAX_DNS_LABEL]; + char netbios_hostname[MAX_DNS_LABEL]; - struct netlogon_string unk0; + char unk2[MAX_DNS_LABEL]; + char user_name[MAX_DNS_LABEL]; + char unk3[MAX_DNS_LABEL]; + char site_name[MAX_DNS_LABEL]; + char unk4[MAX_DNS_LABEL]; + char site_name_2[MAX_DNS_LABEL]; uint32 version; uint16 lmnt_token; @@ -53,38 +53,69 @@ struct cldap_netlogon_reply { }; /* - These strings are rather interesting... They are composed of a series of - length encoded strings, terminated by either 1) a zero length string or 2) - a 0xc0 byte with what appears to be a one byte flags immediately following. + These seem to be strings as described in RFC1035 4.1.4 and can be: + + - a sequence of labels ending in a zero octet + - a pointer + - a sequence of labels ending with a pointer + + A label is a byte where the first two bits must be zero and the remaining + bits represent the length of the label followed by the label itself. + Therefore, the length of a label is at max 64 bytes. Under RFC1035, a + sequence of labels cannot exceed 255 bytes. + + A pointer consists of a 14 bit offset from the beginning of the data. + + struct ptr { + unsigned ident:2; // must be 11 + unsigned offset:14; // from the beginning of data + }; + + This is used as a method to compress the packet by eliminated duplicate + domain components. Since a UDP packet should probably be < 512 bytes and a + DNS name can be up to 255 bytes, this actually makes a lot of sense. */ -static unsigned pull_netlogon_string(struct netlogon_string *ret,const char *d) +static unsigned pull_netlogon_string(char *ret, const char *ptr, + const char *data) { - const char *p = (const char *)d; - - ZERO_STRUCTP(ret); + char *pret = ret; + int followed_ptr = 0; + unsigned ret_len = 0; + memset(pret, 0, MAX_DNS_LABEL); do { - unsigned len = (unsigned char)*p; - p++; - - if (len > 0 && len != 0xc0) { - ret->component = realloc(ret->component, - ++ret->comp_len * - sizeof(char *)); - - ret->component[ret->comp_len - 1] = - smb_xstrndup(p, len); - p += len; - } else { - if (len == 0xc0) { - ret->extra_flag = *p; - p++; - }; - break; + if ((*ptr & 0xc0) == 0xc0) { + uint16 len; + + if (!followed_ptr) { + ret_len += 2; + followed_ptr = 1; + } + len = ((ptr[0] & 0x3f) << 8) | ptr[1]; + ptr = data + len; + } else if (*ptr) { + uint8 len = (uint8)*(ptr++); + + if ((pret - ret + len + 1) >= MAX_DNS_LABEL) { + d_printf("DC returning too long DNS name\n"); + return 0; + } + + if (pret != ret) { + *pret = '.'; + pret++; + } + memcpy(pret, ptr, len); + pret += len; + ptr += len; + + if (!followed_ptr) { + ret_len += (len + 1); + } } - } while (1); + } while (*ptr); - return (p - d); + return ret_len ? ret_len : 1; } /* @@ -95,7 +126,11 @@ static int send_cldap_netlogon(int sock, const char *domain, { ASN1_DATA data; char ntver[4]; +#ifdef CLDAP_USER_QUERY + char aac[4]; + SIVAL(aac, 0, 0x00000180); +#endif SIVAL(ntver, 0, ntversion); memset(&data, 0, sizeof(data)); @@ -121,6 +156,18 @@ static int send_cldap_netlogon(int sock, const char *domain, asn1_write_OctetString(&data, hostname, strlen(hostname)); asn1_pop_tag(&data); +#ifdef CLDAP_USER_QUERY + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "User", 4); + asn1_write_OctetString(&data, "SAMBA$", 6); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "AAC", 4); + asn1_write_OctetString(&data, aac, 4); + asn1_pop_tag(&data); +#endif + asn1_push_tag(&data, ASN1_CONTEXT(3)); asn1_write_OctetString(&data, "NtVer", 5); asn1_write_OctetString(&data, ntver, 4); @@ -144,7 +191,6 @@ static int send_cldap_netlogon(int sock, const char *domain, d_printf("failed to send cldap query (%s)\n", strerror(errno)); } - file_save("cldap_query.dat", data.data, data.length); asn1_free(&data); return 0; @@ -173,8 +219,6 @@ static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) } blob.length = ret; - file_save("cldap_reply.dat", blob.data, blob.length); - asn1_load(&data, blob); asn1_start_tag(&data, ASN1_SEQUENCE(0)); asn1_read_Integer(&data, &i1); @@ -196,8 +240,6 @@ static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) return -1; } - file_save("cldap_reply_core.dat", os3.data, os3.length); - p = os3.data; reply->type = IVAL(p, 0); p += 4; @@ -206,15 +248,25 @@ static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) memcpy(&reply->guid.info, p, GUID_SIZE); p += GUID_SIZE; - p += pull_netlogon_string(&reply->forest, p); - p += pull_netlogon_string(&reply->domain, p); - p += pull_netlogon_string(&reply->hostname, p); - p += pull_netlogon_string(&reply->netbios_domain, p); - p += pull_netlogon_string(&reply->netbios_hostname, p); - p += pull_netlogon_string(&reply->user_name, p); - p += pull_netlogon_string(&reply->site_name, p); + p += pull_netlogon_string(reply->forest, p, os3.data); + p += pull_netlogon_string(reply->unk0, p, os3.data); + p += pull_netlogon_string(reply->domain, p, os3.data); + p += pull_netlogon_string(reply->hostname, p, os3.data); + p += pull_netlogon_string(reply->netbios_domain, p, os3.data); + p += pull_netlogon_string(reply->unk1, p, os3.data); + p += pull_netlogon_string(reply->netbios_hostname, p, os3.data); + p += pull_netlogon_string(reply->unk2, p, os3.data); + + if (reply->type == SAMLOGON_AD_R) { + p += pull_netlogon_string(reply->user_name, p, os3.data); + } else { + *reply->user_name = 0; + } - p += pull_netlogon_string(&reply->unk0, p); + p += pull_netlogon_string(reply->unk3, p, os3.data); + p += pull_netlogon_string(reply->site_name, p, os3.data); + p += pull_netlogon_string(reply->unk4, p, os3.data); + p += pull_netlogon_string(reply->site_name_2, p, os3.data); reply->version = IVAL(p, 0); reply->lmnt_token = SVAL(p, 4); @@ -228,52 +280,6 @@ static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) return 0; } -/* - free a netlogon string -*/ -static void netlogon_string_free(struct netlogon_string *str) -{ - unsigned int i; - - for (i = 0; i < str->comp_len; ++i) { - SAFE_FREE(str->component[i]); - } - SAFE_FREE(str->component); -} - -/* - free a cldap reply packet -*/ -static void cldap_reply_free(struct cldap_netlogon_reply *reply) -{ - netlogon_string_free(&reply->forest); - netlogon_string_free(&reply->domain); - netlogon_string_free(&reply->hostname); - netlogon_string_free(&reply->netbios_domain); - netlogon_string_free(&reply->netbios_hostname); - netlogon_string_free(&reply->user_name); - netlogon_string_free(&reply->site_name); - netlogon_string_free(&reply->unk0); -} - -static void d_print_netlogon_string(const char *label, - struct netlogon_string *str) -{ - unsigned int i; - - if (str->comp_len) { - d_printf("%s", label); - if (str->extra_flag) { - d_printf("[%d]", str->extra_flag); - } - d_printf(": "); - for (i = 0; i < str->comp_len; ++i) { - d_printf("%s%s", (i ? "." : ""), str->component[i]); - } - d_printf("\n"); - } -} - /* do a cldap netlogon query */ @@ -289,6 +295,7 @@ int ads_cldap_netlogon(ADS_STRUCT *ads) inet_ntoa(ads->ldap_ip), ads->ldap_port); return -1; + } ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6); @@ -305,7 +312,18 @@ int ads_cldap_netlogon(ADS_STRUCT *ads) d_printf("Information for Domain Controller: %s\n\n", ads->config.ldap_server_name); - d_printf("Response Type: 0x%x\n", reply.type); + d_printf("Response Type: "); + switch (reply.type) { + case SAMLOGON_AD_UNK_R: + d_printf("SAMLOGON\n"); + break; + case SAMLOGON_AD_R: + d_printf("SAMLOGON_USER\n"); + break; + default: + d_printf("0x%x\n", reply.type); + break; + } d_printf("GUID: "); print_guid(&reply.guid); d_printf("Flags:\n" @@ -330,23 +348,27 @@ int ads_cldap_netlogon(ADS_STRUCT *ads) (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no", (reply.flags & ADS_NDNC) ? "yes" : "no"); - d_print_netlogon_string("Forest", &reply.forest); - d_print_netlogon_string("Domain", &reply.domain); - d_print_netlogon_string("Hostname", &reply.hostname); + printf("Forest:\t\t\t%s\n", reply.forest); + if (*reply.unk0) printf("Unk0:\t\t\t%s\n", reply.unk0); + printf("Domain:\t\t\t%s\n", reply.domain); + printf("Domain Controller:\t%s\n", reply.hostname); - d_print_netlogon_string("Pre-Win2k Domain", &reply.netbios_domain); - d_print_netlogon_string("Pre-Win2k Hostname", &reply.netbios_hostname); + printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain); + if (*reply.unk1) printf("Unk1:\t\t\t%s\n", reply.unk1); + printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname); - d_print_netlogon_string("User name", &reply.user_name); - d_print_netlogon_string("Site Name", &reply.site_name); - d_print_netlogon_string("Unknown Field", &reply.unk0); + if (*reply.unk2) printf("Unk2:\t\t\t%s\n", reply.unk2); + if (*reply.user_name) printf("User name:\t%s\n", reply.user_name); + + if (*reply.unk3) printf("Unk3:\t\t\t%s\n", reply.unk3); + printf("Site Name:\t\t%s\n", reply.site_name); + if (*reply.unk4) printf("Unk4:\t\t\t%s\n", reply.unk4); + printf("Site Name (2):\t\t%s\n", reply.site_name_2); d_printf("NT Version: %d\n", reply.version); d_printf("LMNT Token: %.2x\n", reply.lmnt_token); d_printf("LM20 Token: %.2x\n", reply.lm20_token); - cldap_reply_free(&reply); - return ret; } -- cgit