This commit was manufactured by cvs2svn to create branch 'SAMBA_3_0'.(This used to...
[kai/samba.git] / source3 / utils / net_ads_cldap.c
index ad66af0439708d3c84d4a790d6b376b20e8927a0..e74e4b5a4cf14c68426d2d5afd8e52f7a7749c9d 100644 (file)
 
 #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)
 {
-       char *p = (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);
@@ -140,11 +187,10 @@ static int send_cldap_netlogon(int sock, const char *domain,
                return -1;
        }
 
-       if (write(sock, data.data, data.length) != data.length) {
+       if (write(sock, data.data, data.length) != (ssize_t)data.length) {
                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)
-{
-       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)
-{
-       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;
 }