Radar 5503929 - AD plugin only registers forward DNS with AD This patch updates the "net ads dns register" command to register PTR records as well as A records so that reverse DNS resolution can work. The current limitations are: - only the first IP address gets a PTR record Index: samba/source/libaddns/dns.h =================================================================== --- samba/source/libaddns/dns.h.orig +++ samba/source/libaddns/dns.h @@ -3,6 +3,7 @@ Copyright (C) 2006 Krishna Ganugapati Copyright (C) 2006 Gerald Carter + Copyright (C) 2008 Apple Inc. All rights reserved. ** NOTE! The following LGPL license applies to the libaddns ** library. This does NOT imply that all of Samba is released @@ -216,6 +217,7 @@ void *talloc_zeronull(const void *contex #define QTYPE_MD 3 #define QTYPE_CNAME 5 #define QTYPE_SOA 6 +#define QTYPE_PTR 12 #define QTYPE_ANY 255 #define QTYPE_TKEY 249 #define QTYPE_TSIG 250 @@ -280,6 +282,11 @@ TXT 16 text strings #define DNS_NAME_ERROR 3 #define DNS_NOT_IMPLEMENTED 4 #define DNS_REFUSED 5 +#define DNS_YXDOMAIN 6 +#define DNS_YYRRSET 7 +#define DNS_NXRRSET 8 +#define DNS_NOTAUTH 9 +#define DNS_NOTZONE 10 typedef long HANDLE; @@ -502,12 +509,17 @@ DNS_ERROR dns_sign_update(struct dns_upd const char *keyname, const char *algorithmname, time_t time_signed, uint16 fudge); -DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx, +DNS_ERROR dns_create_update_request_a(TALLOC_CTX *mem_ctx, const char *domainname, const char *hostname, const struct in_addr *ip_addr, size_t num_adds, struct dns_update_request **preq); +DNS_ERROR dns_create_update_request_ptr(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *zone_name, + const struct in_addr ip, + struct dns_update_request **preq); #endif /* HAVE_GSSAPI_SUPPORT */ Index: samba/source/libaddns/dnsrecord.c =================================================================== --- samba/source/libaddns/dnsrecord.c.orig +++ samba/source/libaddns/dnsrecord.c @@ -2,6 +2,7 @@ Linux DNS client library implementation Copyright (C) 2006 Krishna Ganugapati Copyright (C) 2006 Gerald Carter + Copyright (C) 2008 Apple, Inc. All rights reserved. ** NOTE! The following LGPL license applies to the libaddns ** library. This does NOT imply that all of Samba is released @@ -24,6 +25,7 @@ */ #include "dns.h" +#include DNS_ERROR dns_create_query( TALLOC_CTX *mem_ctx, const char *name, uint16 q_type, uint16 q_class, @@ -120,6 +122,37 @@ DNS_ERROR dns_create_rrec(TALLOC_CTX *me return ERROR_DNS_SUCCESS; } +DNS_ERROR dns_create_ptr_record(TALLOC_CTX *mem_ctx, + const char * host, + uint32 ttl, struct in_addr ip, + struct dns_rrec **prec) +{ + DNS_ERROR err; + char * ptr; + + struct dns_domain_name * name; + struct dns_buffer * buf; + + buf = dns_create_buffer(mem_ctx); + + ptr = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.", + (ntohl(ip.s_addr) & 0x000000ff), + (ntohl(ip.s_addr) & 0x0000ff00) >> 8, + (ntohl(ip.s_addr) & 0x00ff0000) >> 16, + (ntohl(ip.s_addr) & 0xff000000) >> 24); + + dns_domain_name_from_string(mem_ctx, host, &name); + dns_marshall_domain_name(buf, name); + + /* For a PTR record, x.x.x.x.in-addr.arpa is the record name, and the + * canonical hostname is the record data. + */ + err = dns_create_rrec(mem_ctx, ptr, QTYPE_PTR, DNS_CLASS_IN, ttl, + buf->offset, (uint8_t *)buf->data, prec); + + return err; +} + DNS_ERROR dns_create_a_record(TALLOC_CTX *mem_ctx, const char *host, uint32 ttl, struct in_addr ip, struct dns_rrec **prec) @@ -356,8 +389,61 @@ DNS_ERROR dns_create_probe(TALLOC_CTX *m TALLOC_FREE(req); return err; } - -DNS_ERROR dns_create_update_request(TALLOC_CTX *mem_ctx, + +/* Create a PTR-record update request. */ +DNS_ERROR dns_create_update_request_ptr(TALLOC_CTX *mem_ctx, + const char *hostname, + const char *zone_name, + const struct in_addr ip, + struct dns_update_request **preq) +{ + struct dns_update_request *req; + struct dns_rrec *rec; + DNS_ERROR err; + + char * ptr_name; + + ptr_name = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.", + (ntohl(ip.s_addr) & 0x000000ff), + (ntohl(ip.s_addr) & 0x0000ff00) >> 8, + (ntohl(ip.s_addr) & 0x00ff0000) >> 16, + (ntohl(ip.s_addr) & 0xff000000) >> 24); + + err = dns_create_update(mem_ctx, zone_name, &req); + if (!ERR_DNS_IS_OK(err)) return err; + + err = dns_create_rrec(req, zone_name, QTYPE_ANY, + DNS_CLASS_ANY, 0, 0, NULL, &rec); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_add_rrec(req, rec, &req->num_preqs, &req->preqs); + if (!ERR_DNS_IS_OK(err)) goto error; + + /* Delete any PTR records. */ + err = dns_create_delete_record(req, ptr_name, QTYPE_PTR, DNS_CLASS_ANY, + &rec); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_add_rrec(req, rec, &req->num_updates, &req->updates); + if (!ERR_DNS_IS_OK(err)) goto error; + + /* Add the corresponding PTR record. */ + err = dns_create_ptr_record(req, hostname, 3600, ip, &rec); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_add_rrec(req, rec, &req->num_updates, &req->updates); + if (!ERR_DNS_IS_OK(err)) goto error; + + *preq = req; + return ERROR_DNS_SUCCESS; + + error: + TALLOC_FREE(req); + return err; +} + +/* Create an A-record update request. */ +DNS_ERROR dns_create_update_request_a(TALLOC_CTX *mem_ctx, const char *domainname, const char *hostname, const struct in_addr *ip_addrs, @@ -398,7 +484,19 @@ DNS_ERROR dns_create_update_request(TALL * .. and add our IPs */ + /* + * .. and add our IPs + */ + for ( i=0; i Copyright (C) 2006 Gerald Carter + Copyright (C) 2008 Apple Inc. All rights reserved. ** NOTE! The following LGPL license applies to the libaddns ** library. This does NOT imply that all of Samba is released @@ -26,6 +27,7 @@ #include "dns.h" #include +#include #include static int destroy_dns_connection(struct dns_connection *conn) @@ -172,13 +174,35 @@ static DNS_ERROR write_all(int fd, uint8 static DNS_ERROR dns_send_tcp(struct dns_connection *conn, const struct dns_buffer *buf) { + ssize_t ret; + struct iovec iov[2]; uint16 len = htons(buf->offset); - DNS_ERROR err; - err = write_all(conn->s, (uint8 *)&len, sizeof(len)); - if (!ERR_DNS_IS_OK(err)) return err; + /* The Wireshark DNS dissector can't reassemble DNS requests when they + * are split over multiple TCP segments. Sending the length prefix and + * the actual request in the same system call prevents this. + */ + iov[0].iov_base = &len; + iov[0].iov_len = sizeof(len); + iov[1].iov_base = buf->data; + iov[1].iov_len = buf->offset; + + ret = writev(conn->s, iov, 2); + if (ret <= 2) { + /* Either the write failed or we didn't get to write the + * length prefix. Either way, it's bad. + */ + return ERROR_DNS_SOCKET_ERROR; + } + + /* Partial write. */ + if (ret <= (buf->offset + sizeof(len))) { + size_t buffer_bytes_written = ret - 2; + return write_all(conn->s, buf->data + buffer_bytes_written, + buf->offset - buffer_bytes_written); + } - return write_all(conn->s, buf->data, buf->offset); + return ERROR_DNS_SUCCESS; } static DNS_ERROR dns_send_udp(struct dns_connection *conn, @@ -234,11 +258,15 @@ static DNS_ERROR read_all(int fd, uint8 } ret = read(fd, data + total, len - total); - if (ret <= 0) { + if (ret < 0) { /* EOF or error */ return ERROR_DNS_SOCKET_ERROR; } + if (ret == 0 && total != len) { + return ERROR_DNS_SOCKET_ERROR; + } + total += ret; } @@ -347,11 +375,15 @@ DNS_ERROR dns_transaction(TALLOC_CTX *me if (!ERR_DNS_IS_OK(err)) goto error; err = dns_send(conn, buf); - if (!ERR_DNS_IS_OK(err)) goto error; + if (!ERR_DNS_IS_OK(err)) { + goto error; + } TALLOC_FREE(buf); err = dns_receive(mem_ctx, conn, &buf); - if (!ERR_DNS_IS_OK(err)) goto error; + if (!ERR_DNS_IS_OK(err)) { + goto error; + } err = dns_unmarshall_request(mem_ctx, buf, resp); @@ -371,7 +403,9 @@ DNS_ERROR dns_update_transaction(TALLOC_ err = dns_transaction(mem_ctx, conn, dns_update2request(up_req), &resp); - if (!ERR_DNS_IS_OK(err)) return err; + if (!ERR_DNS_IS_OK(err)) { + return err; + } *up_resp = dns_request2update(resp); return ERROR_DNS_SUCCESS; Index: samba/source/utils/net_dns.c =================================================================== --- samba/source/utils/net_dns.c.orig +++ samba/source/utils/net_dns.c @@ -5,6 +5,7 @@ Copyright (C) Krishna Ganugapati (krishnag@centeris.com) 2006 Copyright (C) Gerald Carter 2006 + Copyright (C) 2008 Apple Inc. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,13 +27,40 @@ #include "dns.h" #if defined(WITH_DNS_UPDATES) +extern const char *dns_errstr(DNS_ERROR err); /********************************************************************* *********************************************************************/ -DNS_ERROR DoDNSUpdate(char *pszServerName, +static DNS_ERROR +negotiate_security_context(TALLOC_CTX * mem_ctx, + const char * pszDomainName, + const char * pszServerName, + char ** keyname, + gss_ctx_id_t * gss_context) +{ + DNS_ERROR err; + + if (!(*keyname = dns_generate_keyname( mem_ctx ))) { + return ERROR_DNS_NO_MEMORY; + } + + err = dns_negotiate_sec_ctx( pszDomainName, pszServerName, + *keyname, gss_context, DNS_SRV_ANY ); + + /* retry using the Windows 2000 DNS hack */ + if (!ERR_DNS_IS_OK(err)) { + return dns_negotiate_sec_ctx( pszDomainName, pszServerName, + *keyname, gss_context, + DNS_SRV_WIN2000 ); + } + + return ERROR_DNS_SUCCESS; +} + +static DNS_ERROR DoDNSUpdate_A(char *pszServerName, const char *pszDomainName, const char *pszHostName, - const struct in_addr *iplist, size_t num_addrs ) + const struct in_addr *iplist, size_t num_addrs) { DNS_ERROR err; struct dns_connection *conn; @@ -40,11 +68,7 @@ DNS_ERROR DoDNSUpdate(char *pszServerNam OM_uint32 minor; struct dns_update_request *req, *resp; - if ( (num_addrs <= 0) || !iplist ) { - return ERROR_DNS_INVALID_PARAMETER; - } - - if (!(mem_ctx = talloc_init("DoDNSUpdate"))) { + if (!(mem_ctx = talloc_init(__func__))) { return ERROR_DNS_NO_MEMORY; } @@ -73,7 +97,7 @@ DNS_ERROR DoDNSUpdate(char *pszServerNam * First try without signing */ - err = dns_create_update_request(mem_ctx, pszDomainName, pszHostName, + err = dns_create_update_request_a(mem_ctx, pszDomainName, pszHostName, iplist, num_addrs, &req); if (!ERR_DNS_IS_OK(err)) goto error; @@ -92,30 +116,17 @@ DNS_ERROR DoDNSUpdate(char *pszServerNam gss_ctx_id_t gss_context; char *keyname; - if (!(keyname = dns_generate_keyname( mem_ctx ))) { - err = ERROR_DNS_NO_MEMORY; - goto error; - } - - err = dns_negotiate_sec_ctx( pszDomainName, pszServerName, - keyname, &gss_context, DNS_SRV_ANY ); + err = negotiate_security_context(mem_ctx, + pszDomainName, pszServerName, + &keyname, &gss_context); - /* retry using the Windows 2000 DNS hack */ - if (!ERR_DNS_IS_OK(err)) { - err = dns_negotiate_sec_ctx( pszDomainName, pszServerName, - keyname, &gss_context, - DNS_SRV_WIN2000 ); - } - if (!ERR_DNS_IS_OK(err)) goto error; - err = dns_sign_update(req, gss_context, keyname, "gss.microsoft.com", time(NULL), 3600); gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER); - if (!ERR_DNS_IS_OK(err)) goto error; err = dns_update_transaction(mem_ctx, conn, req, &resp); @@ -123,40 +134,223 @@ DNS_ERROR DoDNSUpdate(char *pszServerNam err = (dns_response_code(resp->flags) == DNS_NO_ERROR) ? ERROR_DNS_SUCCESS : ERROR_DNS_UPDATE_FAILED; + if (!ERR_DNS_IS_OK(err)) goto error; + + TALLOC_FREE(mem_ctx); + return ERROR_DNS_SUCCESS; + + } + + +error: + TALLOC_FREE(mem_ctx); + return err; +} + +static DNS_ERROR DoDNSUpdate_PTR_with_zone(TALLOC_CTX *mem_ctx, + struct dns_connection *conn, + char *pszServerName, + const char *pszDomainName, + const char *pszHostName, + const char * zone_name, + const struct in_addr ip) +{ + DNS_ERROR err; + struct dns_update_request *ptr_req, *resp; + +#define WRONG_ZONE_RESPONSE(code) \ + (((code) == DNS_NOTZONE) || ((code) == DNS_NOTAUTH)) + + /* + * First try without signing + */ + + err = dns_create_update_request_ptr(mem_ctx, pszHostName, + zone_name, ip, &ptr_req); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_update_transaction(mem_ctx, conn, ptr_req, &resp); + if (!ERR_DNS_IS_OK(err)) goto error; + + if (dns_response_code(resp->flags) == DNS_NO_ERROR) { + return ERROR_DNS_SUCCESS; + } else if (WRONG_ZONE_RESPONSE(dns_response_code(resp->flags))) { + return ERROR_DNS_WRONG_ZONE; + } + + /* + * Okay, we have to try with signing + */ + { + OM_uint32 minor; + gss_ctx_id_t gss_context; + char *keyname; + + err = negotiate_security_context(mem_ctx, + pszDomainName, pszServerName, + &keyname, &gss_context); + + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_sign_update(ptr_req, gss_context, keyname, + "gss.microsoft.com", time(NULL), 3600); + + gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_update_transaction(mem_ctx, conn, ptr_req, &resp); + if (!ERR_DNS_IS_OK(err)) goto error; + + if (dns_response_code(resp->flags) == DNS_NO_ERROR) { + err = ERROR_DNS_SUCCESS; + } else if (WRONG_ZONE_RESPONSE(dns_response_code(resp->flags))) { + err = ERROR_DNS_WRONG_ZONE; + } else { + err = ERROR_DNS_UPDATE_FAILED; + } } +#undef WRONG_ZONE_RESPONSE error: + return err; +} + +static DNS_ERROR DoDNSUpdate_PTR(char *pszServerName, + const char *pszDomainName, + const char *pszHostName, + const struct in_addr ip) +{ + DNS_ERROR err; + struct dns_connection *conn; + char * zone_name; + TALLOC_CTX *mem_ctx; + + if (!(mem_ctx = talloc_init(__func__))) { + return ERROR_DNS_NO_MEMORY; + } + + zone_name = talloc_asprintf(mem_ctx, "%d.%d.%d.%d.in-addr.arpa.", + (ntohl(ip.s_addr) & 0x000000ff), + (ntohl(ip.s_addr) & 0x0000ff00) >> 8, + (ntohl(ip.s_addr) & 0x00ff0000) >> 16, + (ntohl(ip.s_addr) & 0xff000000) >> 24); + if (zone_name == NULL) { + err = ERROR_DNS_NO_MEMORY; + goto done; + } + + err = dns_open_connection( pszServerName, DNS_TCP, mem_ctx, &conn ); + if (!ERR_DNS_IS_OK(err)) { + goto done; + } + + /* Keep knocking off domain components until we find a zone that + * we can update out PTR record in. + */ + while ((zone_name = strchr(zone_name, '.'))) { + /* Skip the '.' we are pointing at. */ + zone_name++; + + /* Stop if we hit the end or the root DNS servers. */ + if (*zone_name == '\0' || + strcmp(zone_name, "in-addr.arpa.") == 0) { + err = ERROR_DNS_INVALID_NAME; + goto done; + } + + err = DoDNSUpdate_PTR_with_zone(mem_ctx, conn, + pszServerName, pszDomainName, pszHostName, + zone_name, ip); + + DEBUG(6, ("updating PTR for %s in %s zone: %s\n", + pszHostName, zone_name, dns_errstr(err))); + + if (ERR_DNS_IS_OK(err)) { + goto done; + } + + if (!ERR_DNS_EQUAL(err, ERROR_DNS_WRONG_ZONE)) { + goto done; + } + } + +done: TALLOC_FREE(mem_ctx); return err; } +DNS_ERROR DoDNSUpdate(char *pszServerName, + const char *pszDomainName, const char *pszHostName, + const struct in_addr *iplist, size_t num_addrs ) +{ + DNS_ERROR a_err; + DNS_ERROR ptr_err; + + if ( (num_addrs <= 0) || !iplist ) { + return ERROR_DNS_INVALID_PARAMETER; + } + + a_err = DoDNSUpdate_A(pszServerName, pszDomainName, pszHostName, + iplist, num_addrs); + if (!ERR_DNS_IS_OK(a_err)) { + d_printf("DNS A-record update for %s failed: %s\n", + pszHostName, dns_errstr(a_err)); + } + + ptr_err = DoDNSUpdate_PTR(pszServerName, pszDomainName, pszHostName, + iplist[0]); + if (!ERR_DNS_IS_OK(ptr_err)) { + d_printf("DNS PTR-record update for %s failed: %s\n", + pszHostName, dns_errstr(ptr_err)); + } + + if (!ERR_DNS_IS_OK(a_err)) { + return a_err; + } + + if (!ERR_DNS_IS_OK(ptr_err)) { + return ptr_err; + } + + return ERROR_DNS_SUCCESS; +} + /********************************************************************* *********************************************************************/ +static bool ip4_mask_match(struct in_addr ip, struct in_addr mask) +{ + return (ip.s_addr & mask.s_addr) == mask.s_addr; +} + int get_my_ip_address( struct in_addr **ips ) { - struct iface_struct nics[MAX_INTERFACES]; int i, n; - struct in_addr loopback_ip = *interpret_addr2("127.0.0.1"); + struct in_addr loopback_ip = *interpret_addr2("127.0.0.0"); struct in_addr *list; int count = 0; /* find the first non-loopback address from our list of interfaces */ - n = get_interfaces(nics, MAX_INTERFACES); + load_interfaces(); + n = iface_count(); if ( (list = SMB_MALLOC_ARRAY( struct in_addr, n )) == NULL ) { return -1; } - for ( i=0; iip, loopback_ip)) { + /* Skip addresses that are in the loopback network. */ + continue; } + + memcpy(&list[count++], &iface->ip, sizeof(struct in_addr)); } - *ips = list; + *ips = list; return count; } Index: samba/source/utils/net_ads.c =================================================================== --- samba/source/utils/net_ads.c.orig +++ samba/source/utils/net_ads.c @@ -1344,6 +1344,8 @@ static NTSTATUS map_krb_realm_to_dns_dom return "socket error"; } else if (ERR_DNS_EQUAL(err, ERROR_DNS_UPDATE_FAILED)) { return "DNS update failed"; + } else if (ERR_DNS_EQUAL(err, ERROR_DNS_WRONG_ZONE)) { + return "wrong DNS zone"; } else { return "invalid DNS error code"; } @@ -1425,15 +1427,8 @@ static NTSTATUS net_update_dns_internal( done: if (!NT_STATUS_IS_OK(status)) { - if (!ERR_DNS_IS_OK(dns_err)) { - d_printf("DNS update for %s failed: %s\n", - machine_name, - dns_errstr(dns_err)); - } else { - d_printf("DNS update for %s failed: %s\n", - machine_name, - get_friendly_nt_error_msg(status)); - } + d_printf("DNS update for %s failed: %s\n", + machine_name, get_friendly_nt_error_msg(status)); } SAFE_FREE( root_domain ); Index: samba/source/libaddns/dnserr.h =================================================================== --- samba/source/libaddns/dnserr.h.orig +++ samba/source/libaddns/dnserr.h @@ -66,6 +66,7 @@ typedef uint32 DNS_ERROR; #define ERROR_DNS_INVALID_MESSAGE ERROR_DNS(9) #define ERROR_DNS_SOCKET_ERROR ERROR_DNS(10) #define ERROR_DNS_UPDATE_FAILED ERROR_DNS(11) +#define ERROR_DNS_WRONG_ZONE ERROR_DNS(12) /* * About to be removed, transitional error