1/* 2 Samba Unix/Linux SMB client library 3 net ads cldap functions 4 Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) 5 Copyright (C) 2003 Jim McDonough (jmcd@us.ibm.com) 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#include "includes.h" 23#include "utils/net.h" 24 25#ifdef HAVE_ADS 26 27#define MAX_DNS_LABEL 255 + 1 28 29struct cldap_netlogon_reply { 30 uint32 type; 31 uint32 flags; 32 UUID_FLAT guid; 33 34 char forest[MAX_DNS_LABEL]; 35 char domain[MAX_DNS_LABEL]; 36 char hostname[MAX_DNS_LABEL]; 37 38 char netbios_domain[MAX_DNS_LABEL]; 39 char netbios_hostname[MAX_DNS_LABEL]; 40 41 char unk[MAX_DNS_LABEL]; 42 char user_name[MAX_DNS_LABEL]; 43 char site_name[MAX_DNS_LABEL]; 44 char site_name_2[MAX_DNS_LABEL]; 45 46 uint32 version; 47 uint16 lmnt_token; 48 uint16 lm20_token; 49}; 50 51/* 52 These seem to be strings as described in RFC1035 4.1.4 and can be: 53 54 - a sequence of labels ending in a zero octet 55 - a pointer 56 - a sequence of labels ending with a pointer 57 58 A label is a byte where the first two bits must be zero and the remaining 59 bits represent the length of the label followed by the label itself. 60 Therefore, the length of a label is at max 64 bytes. Under RFC1035, a 61 sequence of labels cannot exceed 255 bytes. 62 63 A pointer consists of a 14 bit offset from the beginning of the data. 64 65 struct ptr { 66 unsigned ident:2; // must be 11 67 unsigned offset:14; // from the beginning of data 68 }; 69 70 This is used as a method to compress the packet by eliminated duplicate 71 domain components. Since a UDP packet should probably be < 512 bytes and a 72 DNS name can be up to 255 bytes, this actually makes a lot of sense. 73*/ 74static unsigned pull_netlogon_string(char *ret, const char *ptr, 75 const char *data) 76{ 77 char *pret = ret; 78 int followed_ptr = 0; 79 unsigned ret_len = 0; 80 81 memset(pret, 0, MAX_DNS_LABEL); 82 do { 83 if ((*ptr & 0xc0) == 0xc0) { 84 uint16 len; 85 86 if (!followed_ptr) { 87 ret_len += 2; 88 followed_ptr = 1; 89 } 90 len = ((ptr[0] & 0x3f) << 8) | ptr[1]; 91 ptr = data + len; 92 } else if (*ptr) { 93 uint8 len = (uint8)*(ptr++); 94 95 if ((pret - ret + len + 1) >= MAX_DNS_LABEL) { 96 d_printf("DC returning too long DNS name\n"); 97 return 0; 98 } 99 100 if (pret != ret) { 101 *pret = '.'; 102 pret++; 103 } 104 memcpy(pret, ptr, len); 105 pret += len; 106 ptr += len; 107 108 if (!followed_ptr) { 109 ret_len += (len + 1); 110 } 111 } 112 } while (*ptr); 113 114 return followed_ptr ? ret_len : ret_len + 1; 115} 116 117/* 118 do a cldap netlogon query 119*/ 120static int send_cldap_netlogon(int sock, const char *domain, 121 const char *hostname, unsigned ntversion) 122{ 123 ASN1_DATA data; 124 char ntver[4]; 125#ifdef CLDAP_USER_QUERY 126 char aac[4]; 127 128 SIVAL(aac, 0, 0x00000180); 129#endif 130 SIVAL(ntver, 0, ntversion); 131 132 memset(&data, 0, sizeof(data)); 133 134 asn1_push_tag(&data,ASN1_SEQUENCE(0)); 135 asn1_write_Integer(&data, 4); 136 asn1_push_tag(&data, ASN1_APPLICATION(3)); 137 asn1_write_OctetString(&data, NULL, 0); 138 asn1_write_enumerated(&data, 0); 139 asn1_write_enumerated(&data, 0); 140 asn1_write_Integer(&data, 0); 141 asn1_write_Integer(&data, 0); 142 asn1_write_BOOLEAN2(&data, False); 143 asn1_push_tag(&data, ASN1_CONTEXT(0)); 144 145 asn1_push_tag(&data, ASN1_CONTEXT(3)); 146 asn1_write_OctetString(&data, "DnsDomain", 9); 147 asn1_write_OctetString(&data, domain, strlen(domain)); 148 asn1_pop_tag(&data); 149 150 asn1_push_tag(&data, ASN1_CONTEXT(3)); 151 asn1_write_OctetString(&data, "Host", 4); 152 asn1_write_OctetString(&data, hostname, strlen(hostname)); 153 asn1_pop_tag(&data); 154 155#ifdef CLDAP_USER_QUERY 156 asn1_push_tag(&data, ASN1_CONTEXT(3)); 157 asn1_write_OctetString(&data, "User", 4); 158 asn1_write_OctetString(&data, "SAMBA$", 6); 159 asn1_pop_tag(&data); 160 161 asn1_push_tag(&data, ASN1_CONTEXT(3)); 162 asn1_write_OctetString(&data, "AAC", 4); 163 asn1_write_OctetString(&data, aac, 4); 164 asn1_pop_tag(&data); 165#endif 166 167 asn1_push_tag(&data, ASN1_CONTEXT(3)); 168 asn1_write_OctetString(&data, "NtVer", 5); 169 asn1_write_OctetString(&data, ntver, 4); 170 asn1_pop_tag(&data); 171 172 asn1_pop_tag(&data); 173 174 asn1_push_tag(&data,ASN1_SEQUENCE(0)); 175 asn1_write_OctetString(&data, "NetLogon", 8); 176 asn1_pop_tag(&data); 177 asn1_pop_tag(&data); 178 asn1_pop_tag(&data); 179 180 if (data.has_error) { 181 d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs); 182 asn1_free(&data); 183 return -1; 184 } 185 186 if (write(sock, data.data, data.length) != (ssize_t)data.length) { 187 d_printf("failed to send cldap query (%s)\n", strerror(errno)); 188 } 189 190 asn1_free(&data); 191 192 return 0; 193} 194 195 196/* 197 receive a cldap netlogon reply 198*/ 199static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) 200{ 201 int ret; 202 ASN1_DATA data; 203 DATA_BLOB blob; 204 DATA_BLOB os1, os2, os3; 205 uint32 i1; 206 char *p; 207 208 blob = data_blob(NULL, 8192); 209 210 ret = read(sock, blob.data, blob.length); 211 212 if (ret <= 0) { 213 d_printf("no reply received to cldap netlogon\n"); 214 return -1; 215 } 216 blob.length = ret; 217 218 asn1_load(&data, blob); 219 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 220 asn1_read_Integer(&data, &i1); 221 asn1_start_tag(&data, ASN1_APPLICATION(4)); 222 asn1_read_OctetString(&data, &os1); 223 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 224 asn1_start_tag(&data, ASN1_SEQUENCE(0)); 225 asn1_read_OctetString(&data, &os2); 226 asn1_start_tag(&data, ASN1_SET); 227 asn1_read_OctetString(&data, &os3); 228 asn1_end_tag(&data); 229 asn1_end_tag(&data); 230 asn1_end_tag(&data); 231 asn1_end_tag(&data); 232 asn1_end_tag(&data); 233 234 if (data.has_error) { 235 d_printf("Failed to parse cldap reply\n"); 236 return -1; 237 } 238 239 p = (char *)os3.data; 240 241 reply->type = IVAL(p, 0); p += 4; 242 reply->flags = IVAL(p, 0); p += 4; 243 244 memcpy(&reply->guid.info, p, UUID_FLAT_SIZE); 245 p += UUID_FLAT_SIZE; 246 247 p += pull_netlogon_string(reply->forest, p, (const char *)os3.data); 248 p += pull_netlogon_string(reply->domain, p, (const char *)os3.data); 249 p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data); 250 p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data); 251 p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data); 252 p += pull_netlogon_string(reply->unk, p, (const char *)os3.data); 253 254 if (reply->type == SAMLOGON_AD_R) { 255 p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data); 256 } else { 257 *reply->user_name = 0; 258 } 259 260 p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data); 261 p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data); 262 263 reply->version = IVAL(p, 0); 264 reply->lmnt_token = SVAL(p, 4); 265 reply->lm20_token = SVAL(p, 6); 266 267 data_blob_free(&os1); 268 data_blob_free(&os2); 269 data_blob_free(&os3); 270 data_blob_free(&blob); 271 272 return 0; 273} 274 275/* 276 do a cldap netlogon query 277*/ 278int ads_cldap_netlogon(ADS_STRUCT *ads) 279{ 280 int sock; 281 int ret; 282 struct cldap_netlogon_reply reply; 283 const char *target = opt_host ? opt_host : inet_ntoa(ads->ldap_ip); 284 285 sock = open_udp_socket(target, ads->ldap_port); 286 if (sock == -1) { 287 d_printf("Failed to open udp socket to %s:%u\n", 288 inet_ntoa(ads->ldap_ip), 289 ads->ldap_port); 290 return -1; 291 292 } 293 294 ret = send_cldap_netlogon(sock, ads->config.realm, global_myname(), 6); 295 if (ret != 0) { 296 return ret; 297 } 298 ret = recv_cldap_netlogon(sock, &reply); 299 close(sock); 300 301 if (ret == -1) { 302 return -1; 303 } 304 305 d_printf("Information for Domain Controller: %s\n\n", 306 ads->config.ldap_server_name); 307 308 d_printf("Response Type: "); 309 switch (reply.type) { 310 case SAMLOGON_AD_UNK_R: 311 d_printf("SAMLOGON\n"); 312 break; 313 case SAMLOGON_AD_R: 314 d_printf("SAMLOGON_USER\n"); 315 break; 316 default: 317 d_printf("0x%x\n", reply.type); 318 break; 319 } 320 d_printf("GUID: %s\n", 321 smb_uuid_string_static(smb_uuid_unpack_static(reply.guid))); 322 d_printf("Flags:\n" 323 "\tIs a PDC: %s\n" 324 "\tIs a GC of the forest: %s\n" 325 "\tIs an LDAP server: %s\n" 326 "\tSupports DS: %s\n" 327 "\tIs running a KDC: %s\n" 328 "\tIs running time services: %s\n" 329 "\tIs the closest DC: %s\n" 330 "\tIs writable: %s\n" 331 "\tHas a hardware clock: %s\n" 332 "\tIs a non-domain NC serviced by LDAP server: %s\n", 333 (reply.flags & ADS_PDC) ? "yes" : "no", 334 (reply.flags & ADS_GC) ? "yes" : "no", 335 (reply.flags & ADS_LDAP) ? "yes" : "no", 336 (reply.flags & ADS_DS) ? "yes" : "no", 337 (reply.flags & ADS_KDC) ? "yes" : "no", 338 (reply.flags & ADS_TIMESERV) ? "yes" : "no", 339 (reply.flags & ADS_CLOSEST) ? "yes" : "no", 340 (reply.flags & ADS_WRITABLE) ? "yes" : "no", 341 (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no", 342 (reply.flags & ADS_NDNC) ? "yes" : "no"); 343 344 printf("Forest:\t\t\t%s\n", reply.forest); 345 printf("Domain:\t\t\t%s\n", reply.domain); 346 printf("Domain Controller:\t%s\n", reply.hostname); 347 348 printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain); 349 printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname); 350 351 if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk); 352 if (*reply.user_name) printf("User name:\t%s\n", reply.user_name); 353 354 printf("Site Name:\t\t%s\n", reply.site_name); 355 printf("Site Name (2):\t\t%s\n", reply.site_name_2); 356 357 d_printf("NT Version: %d\n", reply.version); 358 d_printf("LMNT Token: %.2x\n", reply.lmnt_token); 359 d_printf("LM20 Token: %.2x\n", reply.lm20_token); 360 361 return ret; 362} 363 364 365#endif 366