smbns_dyndns.c revision 2
2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <assert.h>
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <arpa/nameser.h>
2N/A#include <net/if.h>
2N/A#include <resolv.h>
2N/A#include <sys/time.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <netdb.h>
2N/A#include <syslog.h>
2N/A#include <gssapi/gssapi.h>
2N/A#include <kerberosv5/krb5.h>
2N/A
2N/A#include "res_update.h"
2N/A
2N/A#include <smbns_dyndns.h>
2N/A#include <smbns_krb.h>
2N/A
2N/A/*
2N/A * The following can be removed once head/arpa/nameser_compat.h
2N/A * defines BADSIG, BADKEY and BADTIME.
2N/A */
2N/A#ifndef BADSIG
2N/A#define BADSIG ns_r_badsig
2N/A#endif /* BADSIG */
2N/A
2N/A#ifndef BADKEY
2N/A#define BADKEY ns_r_badkey
2N/A#endif /* BADKEY */
2N/A
2N/A#ifndef BADTIME
2N/A#define BADTIME ns_r_badtime
2N/A#endif /* BADTIME */
2N/A
2N/A/*
2N/A * DYNDNS_TTL is the time to live in DNS caches. Note that this
2N/A * does not affect the entry in the authoritative DNS database.
2N/A */
2N/A#define DYNDNS_TTL 1200
2N/A
2N/A/* Microsoft AD interoperability */
2N/A#define DYNDNS_MSAD_GSS_ALG "gss.microsoft.com" /* algorithm name */
2N/A#define DYNDNS_MSAD_KEY_EXPIRE 86400 /* 24 hours */
2N/A
2N/A/* lint-free alternatives to NS_PUT16, NS_PUT32 */
2N/A#define DYNDNS_PUT16(src, dst) (ns_put16((src), (dst)), (dst) += 2)
2N/A#define DYNDNS_PUT32(src, dst) (ns_put32((src), (dst)), (dst) += 4)
2N/A
2N/A/* add a host integer to a network short */
2N/A#define DYNDNS_ADD16(x, y) ((x) = htons(ntohs((x)) + (y)))
2N/A
2N/Astatic const char * const dyndns_hex_digits = "0123456789abcdef";
2N/A
2N/A/*
2N/A * Dynamic DNS update API for kclient.
2N/A *
2N/A * Returns 0 upon success. Otherwise, returns -1.
2N/A */
2N/Aint
2N/Adyndns_update(char *fqdn)
2N/A{
2N/A int rc;
2N/A
2N/A if (smb_nic_init() != SMB_NIC_SUCCESS)
2N/A return (-1);
2N/A
2N/A (void) smb_strlwr(fqdn);
2N/A rc = dyndns_zone_update(fqdn);
2N/A smb_nic_fini();
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Convert IPv4 or IPv6 sockaddr to hostname.
2N/A * The value is lowercase per RFC 4120 section 6.2.1.
2N/A */
2N/Astatic int
2N/Adyndns_getnameinfo(union res_sockaddr_union *sockaddr,
2N/A char *hostname, int hostlen, int flags)
2N/A{
2N/A socklen_t socklen;
2N/A int rc;
2N/A
2N/A if (sockaddr->sin.sin_family == AF_INET)
2N/A socklen = sizeof (struct sockaddr_in);
2N/A else if (sockaddr->sin6.sin6_family == AF_INET)
2N/A socklen = sizeof (struct sockaddr_in6);
2N/A else
2N/A socklen = 0;
2N/A if ((rc = (getnameinfo((struct sockaddr *)sockaddr, socklen,
2N/A hostname, hostlen, NULL, 0, flags))) == 0)
2N/A (void) smb_strlwr(hostname);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Log a DNS error message
2N/A */
2N/Astatic void
2N/Adyndns_syslog(int severity, int errnum, const char *text)
2N/A{
2N/A struct {
2N/A int errnum;
2N/A char *errmsg;
2N/A } errtab[] = {
2N/A { FORMERR, "message format error" },
2N/A { SERVFAIL, "server internal error" },
2N/A { NXDOMAIN, "entry should exist but does not exist" },
2N/A { NOTIMP, "not supported" },
2N/A { REFUSED, "operation refused" },
2N/A { YXDOMAIN, "entry should not exist but does exist" },
2N/A { YXRRSET, "RRSet should not exist but does exist" },
2N/A { NXRRSET, "RRSet should exist but does not exist" },
2N/A { NOTAUTH, "server is not authoritative for specified zone" },
2N/A { NOTZONE, "name not within specified zone" },
2N/A { BADSIG, "bad transaction signature (TSIG)" },
2N/A { BADKEY, "bad transaction key (TKEY)" },
2N/A { BADTIME, "time not synchronized" },
2N/A };
2N/A
2N/A char *errmsg = "unknown error";
2N/A int i;
2N/A
2N/A if (errnum == NOERROR)
2N/A return;
2N/A
2N/A for (i = 0; i < (sizeof (errtab) / sizeof (errtab[0])); ++i) {
2N/A if (errtab[i].errnum == errnum) {
2N/A errmsg = errtab[i].errmsg;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A syslog(severity, "dyndns: %s: %s: %d", text, errmsg, errnum);
2N/A}
2N/A
2N/A/*
2N/A * Log a DNS update description message
2N/A */
2N/Astatic void
2N/Adyndns_log_update(int severity,
2N/A dyndns_update_op_t update_op, dyndns_zone_dir_t update_zone,
2N/A const char *hostname, int af, const void *addr)
2N/A{
2N/A const char *zone_str;
2N/A const char *af_str;
2N/A char addr_str[INET6_ADDRSTRLEN];
2N/A
2N/A zone_str = (update_zone == DYNDNS_ZONE_FWD) ? "forward" : "reverse";
2N/A af_str = (af == AF_INET) ? "IPv4" : "IPv6";
2N/A if (addr != NULL)
2N/A if (inet_ntop(af, addr, addr_str, sizeof (addr_str)) == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: log_update: addr ntop");
2N/A return;
2N/A }
2N/A
2N/A switch (update_op) {
2N/A case DYNDNS_UPDATE_ADD:
2N/A syslog(severity, "dyndns: update started: "
2N/A "add hostname %s %s address %s in %s zone",
2N/A hostname, af_str, addr_str, zone_str);
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_ALL:
2N/A if (update_zone == DYNDNS_ZONE_FWD) {
2N/A syslog(severity, "dyndns: update started: "
2N/A "delete all %s addresses for hostname %s "
2N/A "in forward zone", af_str, hostname);
2N/A } else if (update_zone == DYNDNS_ZONE_REV) {
2N/A syslog(severity, "dyndns: update started: "
2N/A "delete all hostnames for %s address %s "
2N/A "in reverse zone", af_str, addr_str);
2N/A }
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_CLEAR:
2N/A if (update_zone == DYNDNS_ZONE_FWD) {
2N/A syslog(severity, "dyndns: update started: "
2N/A "delete hostname %s in forward zone", hostname);
2N/A } else if (update_zone == DYNDNS_ZONE_REV) {
2N/A syslog(severity, "dyndns: update started: "
2N/A "delete %s address %s in reverse zone",
2N/A af_str, addr_str);
2N/A }
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_ONE:
2N/A syslog(severity, "dyndns: update started: "
2N/A "delete hostname %s %s address %s in %s zone",
2N/A hostname, af_str, addr_str, zone_str);
2N/A break;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * display_stat
2N/A * Display GSS error message from error code. This routine is used to display
2N/A * the mechanism independent and mechanism specific error messages for GSS
2N/A * routines. The major status error code is the mechanism independent error
2N/A * code and the minor status error code is the mechanism specific error code.
2N/A * Parameters:
2N/A * maj: GSS major status
2N/A * min: GSS minor status
2N/A * Returns:
2N/A * None
2N/A */
2N/Astatic void
2N/Adisplay_stat(OM_uint32 maj, OM_uint32 min)
2N/A{
2N/A gss_buffer_desc msg;
2N/A OM_uint32 msg_ctx = 0;
2N/A OM_uint32 status, min2;
2N/A
2N/A status = gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID,
2N/A &msg_ctx, &msg);
2N/A
2N/A if ((status != GSS_S_COMPLETE) && (status != GSS_S_CONTINUE_NEEDED))
2N/A return;
2N/A
2N/A if (msg.value == NULL)
2N/A return;
2N/A
2N/A syslog(LOG_ERR, "dyndns (secure update): GSS major status error: %s",
2N/A (char *)msg.value);
2N/A (void) gss_release_buffer(&min2, &msg);
2N/A
2N/A status = gss_display_status(&min2, min, GSS_C_MECH_CODE,
2N/A GSS_C_NULL_OID, &msg_ctx, &msg);
2N/A
2N/A
2N/A if ((status != GSS_S_COMPLETE) && (status != GSS_S_CONTINUE_NEEDED))
2N/A return;
2N/A
2N/A if (msg.value == NULL)
2N/A return;
2N/A
2N/A syslog(LOG_ERR, "dyndns (secure update): GSS minor status error: %s",
2N/A (char *)msg.value);
2N/A (void) gss_release_buffer(&min2, &msg);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_addr_to_ptrname
2N/A *
2N/A * Convert a network address structure into a domain name for reverse lookup.
2N/A * IPv4 addresses are converted to in-addr.arpa names (RFC 1035 section 3.5).
2N/A * IPv6 addresses are converted to ip6.arpa names (RFC 3596 section 2.5).
2N/A *
2N/A * Parameters:
2N/A * af: address family of *addr, AF_INET or AF_INET6
2N/A * addr: pointer to in_addr or in6_addr address structure
2N/A * buf: buffer to hold reverse domain name
2N/A * buf_sz: length of buffer
2N/A * Returns:
2N/A * 0 on error, or length of domain name written to buf
2N/A */
2N/Astatic size_t
2N/Adyndns_addr_to_ptrname(int af, const void *addr, char *buf, size_t buf_sz)
2N/A{
2N/A union {
2N/A const struct in_addr *in;
2N/A const struct in6_addr *in6;
2N/A } u;
2N/A size_t len;
2N/A uint8_t a, b, c, d;
2N/A int i;
2N/A
2N/A switch (af) {
2N/A case AF_INET:
2N/A u.in = addr;
2N/A a = (ntohl(u.in->s_addr) & 0xff000000) >> 24;
2N/A b = (ntohl(u.in->s_addr) & 0x00ff0000) >> 16;
2N/A c = (ntohl(u.in->s_addr) & 0x0000ff00) >> 8;
2N/A d = (ntohl(u.in->s_addr) & 0x000000ff);
2N/A len = snprintf(NULL, 0, "%d.%d.%d.%d.in-addr.arpa",
2N/A d, c, b, a);
2N/A if (len >= buf_sz)
2N/A return (0);
2N/A (void) snprintf(buf, buf_sz, "%d.%d.%d.%d.in-addr.arpa",
2N/A d, c, b, a);
2N/A break;
2N/A case AF_INET6:
2N/A u.in6 = addr;
2N/A /*
2N/A * The IPv6 reverse lookup domain name format has
2N/A * 4 chars for each address byte, followed by "ip6.arpa".
2N/A */
2N/A len = (4 * IPV6_ADDR_LEN) + (sizeof ("ip6.arpa") - 1);
2N/A if (len >= buf_sz)
2N/A return (0);
2N/A for (i = (IPV6_ADDR_LEN - 1); i >= 0; i--) {
2N/A *buf++ = dyndns_hex_digits[u.in6->s6_addr[i] & 0x0f];
2N/A *buf++ = '.';
2N/A *buf++ = dyndns_hex_digits[u.in6->s6_addr[i] >> 4];
2N/A *buf++ = '.';
2N/A }
2N/A (void) strlcpy(buf, "ip6.arpa", sizeof ("ip6.arpa"));
2N/A break;
2N/A default:
2N/A return (0);
2N/A }
2N/A
2N/A return (len);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_get_update_info
2N/A *
2N/A * Given a valid resolver context, update direction, and a host name and
2N/A * address pair, return the information needed to perform a DNS update.
2N/A * DNS zone and SOA nameservers are found by searching current DNS data.
2N/A *
2N/A * Example:
2N/A * dyndns_get_update_info(statp, DYNDNS_ZONE_FWD, type,
2N/A * "host.org.example.com", "10.0.0.1", ...) might return:
2N/A * type: ns_t_a
2N/A * zone_buf: "example.com"
2N/A * name_buf: "host.org.example.com"
2N/A * data_buf: "10.0.0.1"
2N/A * dyndns_get_update_info(statp, DYNDNS_ZONE_REV, type,
2N/A * "host.org.example.com", "10.0.0.1", ...) might return:
2N/A * type: ns_t_ptr
2N/A * zone_buf: "10.in-addr.arpa"
2N/A * name_buf: "1.0.0.10.in-addr.arpa"
2N/A * data_buf: "host.org.example.com"
2N/A *
2N/A * Parameters:
2N/A * statp: resolver state
2N/A * update_zone: one of DYNDNS_ZONE_FWD or DYNDNS_ZONE_REV
2N/A * hostname: host name, as a string
2N/A * af: address family of *addr, AF_INET or AF_INET6
2N/A * addr: pointer to in_addr or in6_addr address structure
2N/A * type: type of DNS record to update
2N/A * zone_buf: buffer to receive zone owning name
2N/A * zone_sz: size of zone_buf buffer
2N/A * name_buf: buffer to receive name to update
2N/A * name_sz: size of name_buf buffer
2N/A * data_buf: buffer to receive data to update for name
2N/A * data_sz: size of data_buf buffer
2N/A * ns: buffer to receive nameservers found for zone
2N/A * ns_cnt: number of nameservers that fit in ns buffer
2N/A * Returns:
2N/A * -1 on error, or number of nameservers returned in ns
2N/A */
2N/Astatic int
2N/Adyndns_get_update_info(res_state statp, dyndns_zone_dir_t update_zone,
2N/A const char *hostname, int af, const void *addr, ns_type *type,
2N/A char *zone_buf, size_t zone_sz,
2N/A char *name_buf, size_t name_sz,
2N/A char *data_buf, size_t data_sz,
2N/A union res_sockaddr_union *ns, int ns_cnt)
2N/A{
2N/A switch (update_zone) {
2N/A case DYNDNS_ZONE_FWD:
2N/A switch (af) {
2N/A case AF_INET:
2N/A *type = ns_t_a;
2N/A break;
2N/A case AF_INET6:
2N/A *type = ns_t_aaaa;
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A if (strlcpy(name_buf, hostname, name_sz) >= name_sz)
2N/A return (-1);
2N/A if (addr != NULL)
2N/A if (inet_ntop(af, addr, data_buf, data_sz) == NULL)
2N/A return (-1);
2N/A break;
2N/A case DYNDNS_ZONE_REV:
2N/A if (!(af == AF_INET || af == AF_INET6))
2N/A return (-1);
2N/A *type = ns_t_ptr;
2N/A if (dyndns_addr_to_ptrname(af, addr, name_buf, name_sz) == 0)
2N/A return (-1);
2N/A if (hostname != NULL)
2N/A if (strlcpy(data_buf, hostname, data_sz) >= data_sz)
2N/A return (-1);
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A
2N/A return (res_findzonecut2(statp, name_buf, ns_c_in, RES_EXHAUSTIVE,
2N/A zone_buf, zone_sz, ns, ns_cnt));
2N/A}
2N/A
2N/A/*
2N/A * dyndns_build_tkey
2N/A *
2N/A * Build a TKEY RDATA section according to RFC 2930 section 2.
2N/A *
2N/A * May be called with buf == NULL and buf_sz == 0, in which case the
2N/A * function does not actually build the TKEY RDATA, but only returns the
2N/A * space needed for TKEY RDATA as specified by the tkey parameter.
2N/A *
2N/A * Parameters:
2N/A * buf: buffer to store TKEY RDATA
2N/A * buf_sz: buffer length
2N/A * tkey: pointer to DNS TKEY RDATA structure
2N/A * Returns:
2N/A * -1 on error, or length of data written (or that would be written) to buf
2N/A */
2N/Astatic ssize_t
2N/Adyndns_build_tkey(uchar_t *buf, size_t buf_sz, const dyndns_tkey_rdata_t *tkey)
2N/A{
2N/A ns_nname alg_buf;
2N/A size_t alg_len;
2N/A size_t tkey_len;
2N/A
2N/A if (tkey == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: tkey == NULL");
2N/A return (-1);
2N/A }
2N/A if (tkey->tk_alg_name == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: tk_alg_name == NULL");
2N/A return (-1);
2N/A }
2N/A
2N/A if (ns_name_pton(tkey->tk_alg_name, alg_buf, sizeof (alg_buf)) == -1) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: tk_alg_name pton");
2N/A return (-1);
2N/A }
2N/A alg_len = ns_name_length(alg_buf, sizeof (alg_buf));
2N/A
2N/A tkey_len = alg_len + tkey->tk_key_size + tkey->tk_other_size + 16;
2N/A
2N/A if ((buf == NULL) && (buf_sz == 0))
2N/A return (tkey_len);
2N/A
2N/A if (tkey_len > buf_sz) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: buf too small");
2N/A return (-1);
2N/A }
2N/A
2N/A if (tkey->tk_key_size > 0 && tkey->tk_key_data == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: "
2N/A "tk_key_size > 0 && tk_key_data == NULL");
2N/A return (-1);
2N/A }
2N/A if (tkey->tk_other_size > 0 && tkey->tk_other_data == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey: "
2N/A "tk_other_size > 0 && tk_other_data == NULL");
2N/A return (-1);
2N/A }
2N/A
2N/A (void) memcpy(buf, alg_buf, alg_len);
2N/A buf += alg_len;
2N/A DYNDNS_PUT32(tkey->tk_incept_time, buf);
2N/A DYNDNS_PUT32(tkey->tk_expire_time, buf);
2N/A DYNDNS_PUT16(tkey->tk_mode, buf);
2N/A DYNDNS_PUT16(tkey->tk_error, buf);
2N/A DYNDNS_PUT16(tkey->tk_key_size, buf);
2N/A if (tkey->tk_key_size > 0) {
2N/A (void) memcpy(buf, tkey->tk_key_data, tkey->tk_key_size);
2N/A buf += tkey->tk_key_size;
2N/A }
2N/A DYNDNS_PUT16(tkey->tk_other_size, buf);
2N/A if (tkey->tk_other_size > 0)
2N/A (void) memcpy(buf, tkey->tk_other_data, tkey->tk_other_size);
2N/A
2N/A return (tkey_len);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_build_tsig
2N/A *
2N/A * Build a complete TSIG RR section according to RFC 2845 section 2.3,
2N/A * including either all fields, or only those fields used in computation
2N/A * of the message signature data.
2N/A *
2N/A * Because the TSIG RR is added to an existing DNS message for message
2N/A * digest computation, this function takes as parameters a buffer
2N/A * containing an existing message and its length. The TSIG RR is
2N/A * appended to the existing message in the same buffer.
2N/A *
2N/A * May be called with buf == NULL and buf_sz == 0, in which case the
2N/A * function does not actually build the TSIG RR, but only returns the
2N/A * space needed for a TSIG RR as specified by the other parameters.
2N/A *
2N/A * Parameters:
2N/A * buf: buffer to store TSIG RR
2N/A * buf_sz: buffer length
2N/A * msg_len: length of existing message in buffer
2N/A * key_name: key name, in C string format
2N/A * tsig: pointer to DNS TSIG RDATA structure
2N/A * digest_data: one of DYNDNS_DIGEST_SIGNED or DYNDNS_DIGEST_UNSIGNED
2N/A * Returns:
2N/A * -1 on error, or length of data written (or that would be written) to buf
2N/A */
2N/Astatic ssize_t
2N/Adyndns_build_tsig(uchar_t *buf, size_t buf_sz, size_t msg_len,
2N/A const char *key_name, const dyndns_tsig_rdata_t *tsig,
2N/A dyndns_digest_data_t digest_data)
2N/A{
2N/A ns_nname key_buf;
2N/A size_t key_len;
2N/A ns_nname alg_buf;
2N/A size_t alg_len;
2N/A size_t tsig_rrlen, tsig_rdlen;
2N/A uint32_t sign, fudge;
2N/A
2N/A if (tsig == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: tsig == NULL");
2N/A return (-1);
2N/A }
2N/A if (tsig->ts_alg_name == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: ts_alg_name == NULL");
2N/A return (-1);
2N/A }
2N/A
2N/A if (ns_name_pton(key_name, key_buf, sizeof (key_buf)) == -1) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: key_name pton");
2N/A return (-1);
2N/A }
2N/A key_len = ns_name_length(key_buf, sizeof (key_buf));
2N/A
2N/A if (ns_name_pton(tsig->ts_alg_name, alg_buf, sizeof (alg_buf)) == -1) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: ts_alg_name pton");
2N/A return (-1);
2N/A }
2N/A alg_len = ns_name_length(alg_buf, sizeof (alg_buf));
2N/A
2N/A switch (digest_data) {
2N/A case DYNDNS_DIGEST_SIGNED:
2N/A tsig_rrlen = key_len + NS_RRFIXEDSZ;
2N/A tsig_rdlen = alg_len + tsig->ts_other_size +
2N/A tsig->ts_mac_size + 16;
2N/A break;
2N/A case DYNDNS_DIGEST_UNSIGNED:
2N/A tsig_rrlen = key_len + NS_RRFIXEDSZ - 4;
2N/A tsig_rdlen = alg_len + tsig->ts_other_size + 12;
2N/A break;
2N/A default:
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: bad digest_data");
2N/A return (-1);
2N/A }
2N/A
2N/A if ((buf == NULL) && (buf_sz == 0))
2N/A return (tsig_rrlen + tsig_rdlen);
2N/A
2N/A if (msg_len + tsig_rrlen + tsig_rdlen > buf_sz) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: buf too small");
2N/A return (-1);
2N/A }
2N/A
2N/A if (tsig->ts_mac_size > 0 && tsig->ts_mac_data == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: "
2N/A "ts_mac_size > 0 && ts_mac_data == NULL");
2N/A return (-1);
2N/A }
2N/A if (tsig->ts_other_size > 0 && tsig->ts_other_data == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tsig: "
2N/A "ts_other_size > 0 && ts_other_data == NULL");
2N/A return (-1);
2N/A }
2N/A
2N/A sign = tsig->ts_sign_time >> 16;
2N/A fudge = (tsig->ts_sign_time << 16) | tsig->ts_fudge_time;
2N/A
2N/A buf += msg_len;
2N/A
2N/A /*
2N/A * TSIG RR fields:
2N/A * name [type] class ttl [rdlen]
2N/A */
2N/A (void) memcpy(buf, key_buf, key_len);
2N/A buf += key_len;
2N/A if (digest_data == DYNDNS_DIGEST_SIGNED)
2N/A DYNDNS_PUT16(ns_t_tsig, buf);
2N/A DYNDNS_PUT16(ns_c_any, buf);
2N/A DYNDNS_PUT32(0, buf);
2N/A if (digest_data == DYNDNS_DIGEST_SIGNED)
2N/A DYNDNS_PUT16(tsig_rdlen, buf);
2N/A
2N/A /*
2N/A * TSIG RDATA fields:
2N/A * alg_name sign_time fudge_time [mac_size] [mac_data] [orig_id] error
2N/A * other_size other_data
2N/A */
2N/A (void) memcpy(buf, alg_buf, alg_len);
2N/A buf += alg_len;
2N/A DYNDNS_PUT32(sign, buf);
2N/A DYNDNS_PUT32(fudge, buf);
2N/A if (digest_data == DYNDNS_DIGEST_SIGNED) {
2N/A DYNDNS_PUT16(tsig->ts_mac_size, buf);
2N/A if (tsig->ts_mac_size > 0) {
2N/A (void) memcpy(buf, tsig->ts_mac_data,
2N/A tsig->ts_mac_size);
2N/A buf += tsig->ts_mac_size;
2N/A }
2N/A DYNDNS_PUT16(tsig->ts_orig_id, buf);
2N/A }
2N/A DYNDNS_PUT16(tsig->ts_error, buf);
2N/A DYNDNS_PUT16(tsig->ts_other_size, buf);
2N/A if (tsig->ts_other_size > 0)
2N/A (void) memcpy(buf, tsig->ts_other_data, tsig->ts_other_size);
2N/A
2N/A return (tsig_rrlen + tsig_rdlen);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_build_tkey_msg
2N/A *
2N/A * This routine is used to build the TKEY message to transmit GSS tokens
2N/A * during GSS security context establishment for secure DNS update. The
2N/A * TKEY message format uses the DNS query message format. The TKEY section
2N/A * is the answer section of the query message format.
2N/A *
2N/A * Parameters:
2N/A * statp: resolver state
2N/A * buf: buffer to store TKEY message
2N/A * buf_sz: size of buffer
2N/A * key_name: TKEY key name
2N/A * (this same key name must be also be used in the TSIG message)
2N/A * tkey: pointer to DNS TKEY RDATA structure
2N/A * Returns:
2N/A * -1 on error, or length of message written to buf
2N/A */
2N/Astatic ssize_t
2N/Adyndns_build_tkey_msg(res_state statp, uchar_t *buf, int buf_sz,
2N/A const char *key_name, const dyndns_tkey_rdata_t *tkey)
2N/A{
2N/A ssize_t buf_len;
2N/A uchar_t *tkey_buf;
2N/A size_t tkey_sz;
2N/A ns_newmsg newmsg;
2N/A ns_nname key_nname;
2N/A
2N/A tkey_sz = dyndns_build_tkey(NULL, 0, tkey);
2N/A if ((tkey_buf = malloc(tkey_sz)) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A return (-1);
2N/A }
2N/A if (dyndns_build_tkey(tkey_buf, tkey_sz, tkey) == -1) {
2N/A free(tkey_buf);
2N/A return (-1);
2N/A }
2N/A
2N/A if (ns_newmsg_init(buf, buf_sz, &newmsg) != 0) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey_msg: ns_newmsg_init");
2N/A free(tkey_buf);
2N/A return (-1);
2N/A }
2N/A if (ns_name_pton(key_name, key_nname, sizeof (key_nname)) < 0) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey_msg: key_name pton");
2N/A free(tkey_buf);
2N/A return (-1);
2N/A }
2N/A ns_newmsg_id(&newmsg, statp->id = res_nrandomid(statp));
2N/A ns_newmsg_flag(&newmsg, ns_f_opcode, ns_o_query);
2N/A if (ns_newmsg_q(&newmsg, key_nname, ns_t_tkey, ns_c_in) != 0) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey_msg: ns_newmsg_q");
2N/A free(tkey_buf);
2N/A return (-1);
2N/A }
2N/A if (ns_newmsg_rr(&newmsg, ns_s_an, key_nname,
2N/A ns_t_tkey, ns_c_any, 0, tkey_sz, tkey_buf) != 0) {
2N/A syslog(LOG_DEBUG, "dyndns: build_tkey_msg: ns_newmsg_rr");
2N/A free(tkey_buf);
2N/A return (-1);
2N/A }
2N/A buf_len = ns_newmsg_done(&newmsg);
2N/A
2N/A free(tkey_buf);
2N/A
2N/A return (buf_len);
2N/A}
2N/A
2N/A/*
2N/A * Construct a service name for the target system (i.e. the DNS server) as
2N/A * follows:
2N/A *
2N/A * DNS/<fqhostname>@<realm>
2N/A *
2N/A * and convert it to GSS-API internal format.
2N/A */
2N/Astatic int
2N/Adyndns_getkrb5svcname(const char *fqhostname, const char *realm,
2N/A gss_name_t *output_name)
2N/A{
2N/A gss_buffer_desc svcbuf;
2N/A OM_uint32 minor, major;
2N/A char *name = NULL;
2N/A int num_bytes;
2N/A
2N/A if ((num_bytes = asprintf(&name, "DNS/%s@%s",
2N/A fqhostname, realm)) == -1)
2N/A return (-1);
2N/A
2N/A svcbuf.length = num_bytes;
2N/A svcbuf.value = name;
2N/A
2N/A if ((major = gss_import_name(&minor, &svcbuf,
2N/A GSS_C_NO_OID, output_name)) != GSS_S_COMPLETE) {
2N/A display_stat(major, minor);
2N/A (void) free(svcbuf.value);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) free(svcbuf.value);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Construct a service name for the GSS initiator (i.e. our dyndns client)
2N/A * as follows:
2N/A *
2N/A * <sam_account>@<realm>
2N/A *
2N/A * and convert it to GSS-API internal format.
2N/A */
2N/Astatic int
2N/Adyndns_getkrb5clntname(const char *realm, gss_name_t *output_name)
2N/A{
2N/A gss_buffer_desc svcbuf;
2N/A OM_uint32 minor, major;
2N/A char *name = NULL;
2N/A int num_bytes;
2N/A char sam_acct[MAXHOSTNAMELEN];
2N/A
2N/A if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0)
2N/A return (-1);
2N/A
2N/A if ((num_bytes = asprintf(&name, "%s@%s", sam_acct, realm)) == -1)
2N/A return (-1);
2N/A
2N/A svcbuf.length = num_bytes;
2N/A svcbuf.value = name;
2N/A
2N/A if ((major = gss_import_name(&minor, &svcbuf,
2N/A GSS_C_NO_OID, output_name)) != GSS_S_COMPLETE) {
2N/A display_stat(major, minor);
2N/A (void) free(svcbuf.value);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) free(svcbuf.value);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_establish_sec_ctx
2N/A *
2N/A * This routine is used to establish a security context with the DNS server
2N/A * by building TKEY messages and sending them to the DNS server. TKEY messages
2N/A * are also received from the DNS server for processing. The security context
2N/A * establishment is done with the GSS client on the system producing a token
2N/A * and sending the token within the TKEY message to the GSS server on the DNS
2N/A * server. The GSS server then processes the token and then send a TKEY reply
2N/A * message with a new token to be processed by the GSS client. The GSS client
2N/A * processes the new token and then generates a new token to be sent to the
2N/A * GSS server. This cycle is continued until the security establishment is
2N/A * done.
2N/A *
2N/A * See RFC 3645 section 3 for standards information.
2N/A *
2N/A * Parameters:
2N/A * statp: : resolver state
2N/A * gss_context : handle to security context
2N/A * cred_handle : handle to credential
2N/A * target : server principal
2N/A * key_name : TKEY key name
2N/A * (this same key name must be also be used in the TSIG message)
2N/A * Returns:
2N/A * gss_context : handle to security context
2N/A * -1 on failure, 0 on success
2N/A */
2N/Astatic int
2N/Adyndns_establish_sec_ctx(res_state statp,
2N/A gss_ctx_id_t *gss_context, gss_cred_id_t cred_handle, gss_name_t target,
2N/A const char *key_name)
2N/A{
2N/A int buf_sz = NS_MAXMSG;
2N/A int rbuf_sz = NS_MAXMSG;
2N/A uchar_t *buf, *rbuf;
2N/A int buf_len, rbuf_len;
2N/A int rcode;
2N/A OM_uint32 min, maj;
2N/A gss_buffer_desc in_tok, out_tok;
2N/A gss_buffer_desc *inputptr;
2N/A int gss_flags;
2N/A OM_uint32 ret_flags;
2N/A ns_msg msg;
2N/A ns_rr rr;
2N/A dyndns_tkey_rdata_t tkey;
2N/A struct timeval tv;
2N/A int tkey_alg_len;
2N/A
2N/A if ((buf = malloc(buf_sz)) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A return (-1);
2N/A }
2N/A if ((rbuf = malloc(rbuf_sz)) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A free(buf);
2N/A return (-1);
2N/A }
2N/A
2N/A tkey.tk_alg_name = DYNDNS_MSAD_GSS_ALG;
2N/A tkey.tk_mode = DYNDNS_TKEY_MODE_GSS;
2N/A tkey.tk_error = ns_r_noerror;
2N/A tkey.tk_other_size = 0;
2N/A tkey.tk_other_data = NULL;
2N/A
2N/A inputptr = GSS_C_NO_BUFFER;
2N/A *gss_context = GSS_C_NO_CONTEXT;
2N/A gss_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | GSS_C_REPLAY_FLAG |
2N/A GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
2N/A do {
2N/A (void) gettimeofday(&tv, 0);
2N/A maj = gss_init_sec_context(&min, cred_handle, gss_context,
2N/A target, GSS_C_NO_OID, gss_flags, 0, NULL, inputptr, NULL,
2N/A &out_tok, &ret_flags, NULL);
2N/A
2N/A if (maj != GSS_S_COMPLETE && maj != GSS_S_CONTINUE_NEEDED) {
2N/A display_stat(maj, min);
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((maj == GSS_S_COMPLETE) &&
2N/A !(ret_flags & GSS_C_REPLAY_FLAG)) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "no replay attack detection available");
2N/A if (out_tok.length > 0)
2N/A (void) gss_release_buffer(&min, &out_tok);
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((maj == GSS_S_COMPLETE) &&
2N/A !(ret_flags & GSS_C_MUTUAL_FLAG)) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "remote peer did not authenticate itself");
2N/A if (out_tok.length > 0)
2N/A (void) gss_release_buffer(&min, &out_tok);
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A if (out_tok.length > 0) {
2N/A tkey.tk_incept_time = tv.tv_sec;
2N/A tkey.tk_expire_time = tv.tv_sec +
2N/A DYNDNS_MSAD_KEY_EXPIRE;
2N/A tkey.tk_key_size = out_tok.length;
2N/A tkey.tk_key_data = out_tok.value;
2N/A if ((buf_len = dyndns_build_tkey_msg(statp,
2N/A buf, buf_sz, key_name, &tkey)) <= 0) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "could not build key exchange message");
2N/A (void) gss_release_buffer(&min, &out_tok);
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A (void) gss_release_buffer(&min, &out_tok);
2N/A
2N/A if ((rbuf_len = res_nsend(statp, buf, buf_len,
2N/A rbuf, rbuf_sz)) <= 0) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "could not send key exchange message");
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A if (ns_initparse(rbuf, rbuf_len, &msg) != 0) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "could not parse key exchange message");
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A rcode = ns_msg_getflag(msg, ns_f_rcode);
2N/A
2N/A if (rcode != NOERROR) {
2N/A dyndns_syslog(LOG_ERR, rcode,
2N/A "secure update error: key exchange error");
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A if (ns_parserr(&msg, ns_s_an, 0, &rr) != 0) {
2N/A syslog(LOG_ERR, "dyndns: secure update error: "
2N/A "could not parse peer key data");
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A
2N/A /* Extract token length and value from TKEY RDATA */
2N/A tkey_alg_len =
2N/A ns_name_length(&rr.rdata[0], rr.rdlength);
2N/A in_tok.length = ns_get16(&rr.rdata[tkey_alg_len +
2N/A DYNDNS_TKEY_OFFSET_KEYSIZE]);
2N/A in_tok.value = (void *)&rr.rdata[tkey_alg_len +
2N/A DYNDNS_TKEY_OFFSET_KEYDATA];
2N/A
2N/A inputptr = &in_tok;
2N/A }
2N/A
2N/A } while (maj != GSS_S_COMPLETE);
2N/A
2N/A free(buf);
2N/A free(rbuf);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_get_sec_context
2N/A *
2N/A * Get security context for secure dynamic DNS update. This routine opens
2N/A * a socket to the DNS server and establishes a security context with
2N/A * the DNS server using host principal to perform secure dynamic DNS update.
2N/A *
2N/A * Parameters:
2N/A * statp: resolver state
2N/A * hostname: fully qualified hostname
2N/A * dns_srv_ip: ip address of hostname in network byte order
2N/A * Returns:
2N/A * 0 on failure, or gss credential context
2N/A */
2N/Astatic gss_ctx_id_t
2N/Adyndns_get_sec_context(res_state statp,
2N/A const char *hostname, union res_sockaddr_union *dns_srv_ip)
2N/A{
2N/A gss_name_t initiator = GSS_C_NO_NAME, target = GSS_C_NO_NAME;
2N/A gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL;
2N/A gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
2N/A OM_uint32 minor, major;
2N/A char dns_srv_hostname[MAXHOSTNAMELEN];
2N/A char *sam_acct, *realm;
2N/A char ad_domain[MAXHOSTNAMELEN];
2N/A const char *key_name = hostname;
2N/A
2N/A if (dyndns_getnameinfo(dns_srv_ip, dns_srv_hostname,
2N/A sizeof (dns_srv_hostname), 0))
2N/A return (NULL);
2N/A
2N/A (void) smb_getdomainname_ad(ad_domain, sizeof (ad_domain));
2N/A if ((realm = smb_krb5_domain2realm(ad_domain)) == NULL)
2N/A return (NULL);
2N/A
2N/A if (dyndns_getkrb5clntname(realm, &initiator) != 0)
2N/A goto cleanup;
2N/A
2N/A if (dyndns_getkrb5svcname(dns_srv_hostname, realm,
2N/A &target) != 0)
2N/A goto cleanup;
2N/A
2N/A if ((sam_acct = smb_krb5_get_pn_by_id(SMB_KRB5_PN_ID_SAM_ACCT,
2N/A SMB_PN_KEYTAB_ENTRY, ad_domain)) == NULL)
2N/A goto cleanup;
2N/A
2N/A if (smb_kinit(sam_acct, NULL) != 0)
2N/A goto cleanup;
2N/A
2N/A major = gss_acquire_cred(&minor, initiator, 0, GSS_C_NULL_OID_SET,
2N/A GSS_C_INITIATE, &cred_handle, NULL, NULL);
2N/A
2N/A if (GSS_ERROR(major)) {
2N/A display_stat(major, minor);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (dyndns_establish_sec_ctx(statp, &gss_context, cred_handle, target,
2N/A key_name) != 0) {
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&minor, &gss_context,
2N/A NULL);
2N/A gss_context = NULL;
2N/A }
2N/A
2N/Acleanup:
2N/A free(realm);
2N/A free(sam_acct);
2N/A (void) gss_release_name(&minor, &initiator);
2N/A (void) gss_release_name(&minor, &target);
2N/A (void) gss_release_cred(&minor, &cred_handle);
2N/A return (gss_context);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_build_update_msg
2N/A *
2N/A * This routine builds the update request message for adding and removing DNS
2N/A * entries which is used for non-secure and secure DNS update.
2N/A *
2N/A * Parameters:
2N/A * statp: resolver state
2N/A * buf: buffer to build message
2N/A * buf_sz: buffer size
2N/A * zone: zone owning name
2N/A * name: name to update
2N/A * type: type of data to update for name
2N/A * data: value of data to update for name
2N/A * ttl: time to live of new DNS RR
2N/A * (if update_op == DYNDNS_UPDATE_ADD)
2N/A * update_op: one of:
2N/A * DYNDNS_UPDATE_ADD adds entries
2N/A * DYNDNS_UPDATE_DEL_ALL deletes entries of one name & type.
2N/A * DYNDNS_UPDATE_DEL_CLEAR deletes entries of one name.
2N/A * DYNDNS_UPDATE_DEL_ONE deletes one entry.
2N/A *
2N/A * Returns:
2N/A * -1 on error, or length of message written to buf
2N/A */
2N/Astatic ssize_t
2N/Adyndns_build_update_msg(res_state statp, uchar_t *buf, size_t buf_sz,
2N/A const char *zone, const char *name, ns_type type, const char *data,
2N/A uint32_t ttl, dyndns_update_op_t update_op)
2N/A{
2N/A ns_updque updque;
2N/A ns_updrec *updrec_zn, *updrec_ud;
2N/A ssize_t msg_len;
2N/A
2N/A if ((updrec_zn = res_mkupdrec(ns_s_zn, zone,
2N/A ns_c_in, ns_t_soa, 0)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((updrec_ud = res_mkupdrec(ns_s_ud, name,
2N/A ns_c_invalid, ns_t_invalid, 0)) == NULL)
2N/A return (-1);
2N/A
2N/A switch (update_op) {
2N/A case DYNDNS_UPDATE_ADD:
2N/A /* Add to an RRset */
2N/A updrec_ud->r_opcode = ns_uop_add;
2N/A updrec_ud->r_class = ns_c_in;
2N/A updrec_ud->r_type = type;
2N/A updrec_ud->r_ttl = ttl;
2N/A updrec_ud->r_data = (uchar_t *)data;
2N/A updrec_ud->r_size = strlen(data) + 1;
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_ALL:
2N/A /* Delete an RRset */
2N/A updrec_ud->r_opcode = ns_uop_delete;
2N/A updrec_ud->r_class = ns_c_any;
2N/A updrec_ud->r_type = type;
2N/A updrec_ud->r_ttl = 0;
2N/A updrec_ud->r_data = NULL;
2N/A updrec_ud->r_size = 0;
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_CLEAR:
2N/A /* Delete all RRsets from a name */
2N/A updrec_ud->r_opcode = ns_uop_delete;
2N/A updrec_ud->r_class = ns_c_any;
2N/A updrec_ud->r_type = ns_t_any;
2N/A updrec_ud->r_ttl = 0;
2N/A updrec_ud->r_data = NULL;
2N/A updrec_ud->r_size = 0;
2N/A break;
2N/A case DYNDNS_UPDATE_DEL_ONE:
2N/A /* Delete an RR from an RRset */
2N/A updrec_ud->r_opcode = ns_uop_delete;
2N/A updrec_ud->r_class = ns_c_none;
2N/A updrec_ud->r_type = type;
2N/A updrec_ud->r_ttl = 0;
2N/A updrec_ud->r_data = (uchar_t *)data;
2N/A updrec_ud->r_size = strlen(data) + 1;
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A
2N/A /*LINTED*/
2N/A INIT_LIST(updque);
2N/A /*LINTED*/
2N/A APPEND(updque, updrec_zn, r_glink);
2N/A /*LINTED*/
2N/A APPEND(updque, updrec_ud, r_glink);
2N/A
2N/A if ((msg_len = res_nmkupdate(statp, HEAD(updque), buf, buf_sz)) < 0)
2N/A return (-1);
2N/A
2N/A res_freeupdrec(updrec_zn);
2N/A res_freeupdrec(updrec_ud);
2N/A
2N/A return (msg_len);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_search_entry
2N/A * Query DNS server for entry. This routine can indicate if an entry exist
2N/A * or not during forward or reverse lookup. Also can indicate if the data
2N/A * of the entry matched. For example, for forward lookup, the entry is
2N/A * searched using the hostname and the data is the IP address. For reverse
2N/A * lookup, the entry is searched using the IP address and the data is the
2N/A * hostname.
2N/A * Parameters:
2N/A * update_zone: one of DYNDNS_ZONE_FWD or DYNDNS_ZONE_REV
2N/A * hostname : fully qualified hostname
2N/A * family : address family of *addr, AF_INET or AF_INET6
2N/A * addr : address of hostname in network format
2N/A * Returns:
2N/A * is_match: is 1 for found matching entry, otherwise 0
2N/A * 1 : an entry exist but not necessarily match
2N/A * 0 : an entry does not exist
2N/A * -1 : an error occurred
2N/A */
2N/Astatic int
2N/Adyndns_search_entry(dyndns_zone_dir_t update_zone,
2N/A const char *hostname, int family, const void *addr, int *is_match)
2N/A{
2N/A union res_sockaddr_union ipaddr, dnsip;
2N/A char dns_srv_hostname[NI_MAXHOST];
2N/A struct addrinfo hints, *res = NULL;
2N/A int salen;
2N/A
2N/A *is_match = 0;
2N/A switch (family) {
2N/A case AF_INET:
2N/A salen = sizeof (ipaddr.sin);
2N/A (void) memcpy(&ipaddr.sin, addr, salen);
2N/A break;
2N/A case AF_INET6:
2N/A salen = sizeof (ipaddr.sin6);
2N/A (void) memcpy(&ipaddr.sin6, addr, salen);
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A if (update_zone == DYNDNS_ZONE_FWD) {
2N/A bzero((char *)&hints, sizeof (hints));
2N/A hints.ai_family = family;
2N/A hints.ai_flags = AI_NUMERICHOST;
2N/A if (getaddrinfo(hostname, NULL, &hints, &res)) {
2N/A return (NULL);
2N/A }
2N/A if (res) {
2N/A /*
2N/A * if both ips aren't the same family skip to
2N/A * the next record
2N/A */
2N/A do {
2N/A if ((res->ai_family == AF_INET) &&
2N/A (family == AF_INET)) {
2N/A (void) memcpy(&dnsip.sin,
2N/A &res->ai_addr[0], salen);
2N/A if (ipaddr.sin.sin_addr.s_addr ==
2N/A dnsip.sin.sin_addr.s_addr) {
2N/A *is_match = 1;
2N/A break;
2N/A }
2N/A } else if ((res->ai_family == AF_INET6) &&
2N/A (family == AF_INET6)) {
2N/A (void) memcpy(&dnsip.sin6,
2N/A &res->ai_addr[0], salen);
2N/A /* need compare macro here */
2N/A if (!memcmp(&ipaddr.sin6.sin6_addr,
2N/A &dnsip.sin6.sin6_addr, IN6ADDRSZ)) {
2N/A *is_match = 1;
2N/A break;
2N/A }
2N/A }
2N/A } while (res->ai_next);
2N/A freeaddrinfo(res);
2N/A return (1);
2N/A }
2N/A } else {
2N/A if (dyndns_getnameinfo(&ipaddr, dns_srv_hostname, NI_MAXHOST,
2N/A 0))
2N/A return (NULL);
2N/A
2N/A if (strncasecmp(dns_srv_hostname, hostname,
2N/A strlen(hostname)) == 0) {
2N/A *is_match = 1;
2N/A }
2N/A return (1);
2N/A }
2N/A
2N/A /* entry does not exist */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_update_nameaddr
2N/A *
2N/A * Perform non-secure dynamic DNS update. On error, perform secure update
2N/A * on behalf of the localhost's domain machine account if the localhost is a
2N/A * domain member.
2N/A *
2N/A * Secure dynamic DNS update using host credentials requires the system to
2N/A * be configured as a Kerberos client. That configuration is done as part of the
2N/A * AD domain join.
2N/A *
2N/A * This routine creates a new resolver context, builds the update request
2N/A * message, and sends the message to the DNS server. The response is received
2N/A * and check for error. If there is no error then the local NSS cached is
2N/A * purged. DNS may be used to check to see if an entry already exist before
2N/A * adding or to see if an entry does exist before removing it. Adding
2N/A * duplicate entries or removing non-existing entries does not cause any
2N/A * problems. DNS is not check when doing a delete all.
2N/A *
2N/A * Parameters:
2N/A * update_op: one of:
2N/A * DYNDNS_UPDATE_ADD adds entries
2N/A * DYNDNS_UPDATE_DEL_ALL deletes entries of one name & type.
2N/A * DYNDNS_UPDATE_DEL_CLEAR deletes entries of one name.
2N/A * DYNDNS_UPDATE_DEL_ONE deletes one entry.
2N/A * update_zone: one of DYNDNS_ZONE_FWD or DYNDNS_ZONE_REV
2N/A * hostname : fully qualified hostname
2N/A * af : address family of *addr, AF_INET or AF_INET6
2N/A * addr : address of hostname in network format
2N/A * ttl : cached time of this entry by others and not within DNS
2N/A * database
2N/A * do_check : one of:
2N/A * DYNDNS_CHECK_NONE for no DNS checking before update
2N/A * DYNDNS_CHECK_EXIST to check first in DNS
2N/A * fqhn : one of:
2N/A * fully qualified host name; required for secure update
2N/A * may be NULL if secure update not required
2N/A * Returns:
2N/A * -1: error
2N/A * 0: success
2N/A */
2N/Aint
2N/Adyndns_update_nameaddr(
2N/A dyndns_update_op_t update_op, dyndns_zone_dir_t update_zone,
2N/A const char *hostname, int af, const void *addr, uint32_t ttl,
2N/A dyndns_check_opt_t do_check, const char *fqhn)
2N/A{
2N/A int is_exist, is_match;
2N/A int dyndns_security_modes;
2N/A char ad_domain[MAXHOSTNAMELEN];
2N/A char *ad_domainp;
2N/A int buf_sz = NS_MAXMSG;
2N/A int rbuf_sz = NS_MAXMSG;
2N/A uchar_t *buf, *rbuf;
2N/A int buf_len, rbuf_len;
2N/A union {
2N/A uchar_t *cp;
2N/A HEADER *hp;
2N/A } mu;
2N/A res_state statp;
2N/A int msg_len;
2N/A ns_type type;
2N/A char zone[NS_MAXDNAME];
2N/A char name[NS_MAXDNAME];
2N/A char data[NS_MAXDNAME];
2N/A union res_sockaddr_union ns[MAXNS];
2N/A int ns_cnt, ns_cur;
2N/A char ns_addr[INET6_ADDRSTRLEN];
2N/A int ret;
2N/A boolean_t secure_update;
2N/A
2N/A if (update_zone == DYNDNS_ZONE_FWD && hostname == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: update_nameaddr: "
2N/A "requested forward update but no hostname provided");
2N/A return (-1);
2N/A }
2N/A if (update_zone == DYNDNS_ZONE_REV && addr == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: update_nameaddr: "
2N/A "requested reverse update but no address provided");
2N/A return (-1);
2N/A }
2N/A if (update_op == DYNDNS_UPDATE_ADD ||
2N/A update_op == DYNDNS_UPDATE_DEL_ONE)
2N/A if (hostname == NULL && addr == NULL) {
2N/A syslog(LOG_DEBUG, "dyndns: update_nameaddr: "
2N/A "requested update requires hostname and address");
2N/A return (-1);
2N/A }
2N/A
2N/A /* don't check af if it is not used by the update */
2N/A if (!(update_op == DYNDNS_UPDATE_DEL_CLEAR &&
2N/A update_zone == DYNDNS_ZONE_FWD) &&
2N/A !(af == AF_INET || af == AF_INET6)) {
2N/A syslog(LOG_DEBUG, "dyndns: update_nameaddr: "
2N/A "address family %d not supported", af);
2N/A return (-1);
2N/A }
2N/A
2N/A if (setlogmask(0) & LOG_MASK(LOG_INFO))
2N/A dyndns_log_update(LOG_INFO, update_op, update_zone,
2N/A hostname, af, addr);
2N/A
2N/A if (do_check == DYNDNS_CHECK_EXIST &&
2N/A update_op != DYNDNS_UPDATE_DEL_ALL) {
2N/A if ((is_exist = dyndns_search_entry(update_zone, hostname,
2N/A af, addr, &is_match)) < 0) {
2N/A syslog(LOG_ERR,
2N/A "dyndns: error while checking current DNS entry");
2N/A return (-1);
2N/A }
2N/A
2N/A if (update_op == DYNDNS_UPDATE_ADD && is_exist && is_match) {
2N/A syslog(LOG_INFO, "dyndns: update finished: "
2N/A "nothing to add");
2N/A return (0);
2N/A } else if (update_op != DYNDNS_UPDATE_ADD && !is_exist) {
2N/A syslog(LOG_INFO, "dyndns: update finished: "
2N/A "nothing to delete");
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * TBD: read a property to set which update mode(s) to attempt
2N/A */
2N/A dyndns_security_modes = DYNDNS_SECURITY_NONE;
2N/A if ((smb_config_get_secmode() == SMB_SECMODE_DOMAIN) && (fqhn != NULL))
2N/A dyndns_security_modes |= DYNDNS_SECURITY_GSS;
2N/A
2N/A if (dyndns_security_modes & DYNDNS_SECURITY_GSS) {
2N/A if (smb_getdomainname_ad(ad_domain, sizeof (ad_domain)) != 0) {
2N/A if ((ad_domainp = strchr(fqhn, '.')) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: bad domain name");
2N/A return (-1);
2N/A }
2N/A ++ad_domainp;
2N/A } else {
2N/A ad_domainp = ad_domain;
2N/A }
2N/A
2N/A if (!smb_krb5_kt_find(SMB_KRB5_PN_ID_SAM_ACCT, ad_domainp,
2N/A smb_krb5_kt_getpath())) {
2N/A syslog(LOG_NOTICE, "dyndns: secure update unavailable: "
2N/A "cannot find host principal \"%s\" "
2N/A "for realm \"%s\" in local keytab file.",
2N/A fqhn, ad_domainp);
2N/A dyndns_security_modes &= ~DYNDNS_SECURITY_GSS;
2N/A }
2N/A }
2N/A
2N/A secure_update = (dyndns_security_modes & DYNDNS_SECURITY_GSS);
2N/A
2N/A if ((buf = malloc(buf_sz)) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A return (-1);
2N/A }
2N/A if ((rbuf = malloc(rbuf_sz)) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A free(buf);
2N/A return (-1);
2N/A }
2N/A
2N/A mu.cp = buf;
2N/A
2N/A if ((statp = calloc(1, sizeof (struct __res_state))) == NULL) {
2N/A syslog(LOG_ERR, "dyndns: %m");
2N/A free(buf);
2N/A free(rbuf);
2N/A return (-1);
2N/A }
2N/A if (res_ninit(statp) != 0) {
2N/A syslog(LOG_ERR, "dyndns: update error: "
2N/A "could not initialize resolver");
2N/A free(buf);
2N/A free(rbuf);
2N/A free(statp);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * WORKAROUND:
2N/A * 7013458 libresolv2 res_nsend is sometimes inconsistent
2N/A * for UDP vs. TCP queries
2N/A *
2N/A * Remove the next statement when 7013458 is fixed.
2N/A */
2N/A statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
2N/A RES_PRF_QUES | RES_PRF_ANS | RES_PRF_AUTH | RES_PRF_ADD;
2N/A
2N/A if ((ns_cnt = dyndns_get_update_info(statp,
2N/A update_zone, hostname, af, addr, &type,
2N/A zone, sizeof (zone),
2N/A name, sizeof (name),
2N/A data, sizeof (data),
2N/A ns, MAXNS)) == -1) {
2N/A syslog(LOG_ERR, "dyndns: update error: "
2N/A "could not setup update information");
2N/A free(buf);
2N/A free(rbuf);
2N/A res_ndestroy(statp);
2N/A free(statp);
2N/A return (-1);
2N/A }
2N/A
2N/A if ((msg_len = dyndns_build_update_msg(statp, buf, buf_sz,
2N/A zone, name, type, data, ttl, update_op)) <= 0) {
2N/A syslog(LOG_ERR, "dyndns: update error: "
2N/A "could not build update message");
2N/A free(buf);
2N/A free(rbuf);
2N/A res_ndestroy(statp);
2N/A free(statp);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Updates are attempted using the servers listed in resolv.conf.
2N/A */
2N/A ns_cnt = res_getservers(statp, ns, MAXNS);
2N/A
2N/A /*
2N/A * TBD: revise this algorithm to use the primary master nameserver
2N/A * (returned by dyndns_get_update_info above) and the authoritative
2N/A * nameservers found by searching the zone's NS RRset.
2N/A */
2N/A
2N/A for (ns_cur = 0; ns_cur < ns_cnt; ns_cur++) {
2N/A
2N/A *ns_addr = '\0';
2N/A if (ns[ns_cur].sin.sin_family == AF_INET) {
2N/A (void) inet_ntop(AF_INET, &ns[ns_cur].sin.sin_addr,
2N/A ns_addr, sizeof (ns_addr));
2N/A } else if (ns[ns_cur].sin6.sin6_family == AF_INET6) {
2N/A (void) inet_ntop(AF_INET6, &ns[ns_cur].sin6.sin6_addr,
2N/A ns_addr, sizeof (ns_addr));
2N/A }
2N/A if (strlen(ns_addr) > 0)
2N/A syslog(LOG_INFO,
2N/A "dyndns: trying update on server at %s", ns_addr);
2N/A
2N/A (void) res_setservers(statp, &ns[ns_cur], 1);
2N/A
2N/A /* attempt non-secure update on this server */
2N/A if (dyndns_security_modes & DYNDNS_SECURITY_NONE) {
2N/A ns_msg msg;
2N/A int rcode;
2N/A
2N/A /* set new message ID if needed */
2N/A if (ns_cur > 0)
2N/A mu.hp->id =
2N/A htons(statp->id = res_nrandomid(statp));
2N/A
2N/A if ((rbuf_len = res_nsend(statp, buf, msg_len,
2N/A rbuf, rbuf_sz)) <= 0) {
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A if (ns_initparse(rbuf, rbuf_len, &msg) != 0) {
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A rcode = ns_msg_getflag(msg, ns_f_rcode);
2N/A
2N/A if (rcode == NOERROR) {
2N/A syslog(LOG_INFO,
2N/A "dyndns: non-secure update completed");
2N/A ret = 0;
2N/A break;
2N/A }
2N/A
2N/A /*
2N/A * DNS servers typically return REFUSED error code when
2N/A * they allow secure updates only. If secure update
2N/A * will be attempted next, suppress the expected
2N/A * REFUSED error messages when performing
2N/A * non-secure updates. Otherwise, "REFUSED" error
2N/A * messages should be logged.
2N/A */
2N/A if ((secure_update && (rcode != REFUSED)) ||
2N/A (!secure_update)) {
2N/A dyndns_syslog(LOG_ERR, rcode,
2N/A "non-secure update response code");
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A /* attempt secure update on this server */
2N/A if (secure_update) {
2N/A dyndns_tsig_rdata_t tsig;
2N/A struct timeval tv;
2N/A const char *key_name;
2N/A OM_uint32 min, maj;
2N/A gss_buffer_desc in_mic, out_mic;
2N/A gss_ctx_id_t gss_context;
2N/A ssize_t tsig_len;
2N/A ns_msg msg;
2N/A int rcode;
2N/A
2N/A tsig.ts_alg_name = DYNDNS_MSAD_GSS_ALG;
2N/A tsig.ts_fudge_time = NS_TSIG_FUDGE;
2N/A tsig.ts_error = ns_r_noerror;
2N/A tsig.ts_mac_size = 0;
2N/A tsig.ts_mac_data = NULL;
2N/A tsig.ts_other_size = 0;
2N/A tsig.ts_other_data = NULL;
2N/A
2N/A /*
2N/A * TBD: per RFC 3645 section 3.1.2,
2N/A * the key name SHOULD be made globally unique
2N/A */
2N/A key_name = fqhn;
2N/A
2N/A if ((gss_context = dyndns_get_sec_context(statp,
2N/A key_name, &ns[ns_cur])) == NULL) {
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A /* set new message ID if needed */
2N/A if (ns_cur > 0 ||
2N/A dyndns_security_modes & ~DYNDNS_SECURITY_GSS)
2N/A mu.hp->id =
2N/A htons(statp->id = res_nrandomid(statp));
2N/A
2N/A tsig.ts_orig_id = statp->id;
2N/A
2N/A (void) gettimeofday(&tv, 0);
2N/A tsig.ts_sign_time = tv.tv_sec;
2N/A if ((tsig_len = dyndns_build_tsig(buf, buf_sz, msg_len,
2N/A key_name, &tsig, DYNDNS_DIGEST_UNSIGNED)) <= 0) {
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A in_mic.length = msg_len + tsig_len;
2N/A in_mic.value = buf;
2N/A
2N/A /* sign update message */
2N/A (void) gettimeofday(&tv, 0);
2N/A tsig.ts_sign_time = tv.tv_sec;
2N/A if ((maj = gss_get_mic(&min, gss_context, 0,
2N/A &in_mic, &out_mic)) != GSS_S_COMPLETE) {
2N/A display_stat(maj, min);
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A tsig.ts_mac_size = out_mic.length;
2N/A tsig.ts_mac_data = out_mic.value;
2N/A
2N/A if ((tsig_len = dyndns_build_tsig(buf, buf_sz, msg_len,
2N/A key_name, &tsig, DYNDNS_DIGEST_SIGNED)) == -1) {
2N/A (void) gss_release_buffer(&min, &out_mic);
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A DYNDNS_ADD16(mu.hp->arcount, 1);
2N/A
2N/A (void) gss_release_buffer(&min, &out_mic);
2N/A
2N/A buf_len = msg_len + tsig_len;
2N/A
2N/A if ((rbuf_len = res_nsend(statp, buf, buf_len,
2N/A rbuf, rbuf_sz)) <= 0) {
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A DYNDNS_ADD16(mu.hp->arcount, -1);
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A if (ns_initparse(rbuf, rbuf_len, &msg) != 0) {
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A DYNDNS_ADD16(mu.hp->arcount, -1);
2N/A ret = -1;
2N/A continue;
2N/A }
2N/A
2N/A if (gss_context != GSS_C_NO_CONTEXT)
2N/A (void) gss_delete_sec_context(&min,
2N/A &gss_context, NULL);
2N/A
2N/A rcode = ns_msg_getflag(msg, ns_f_rcode);
2N/A
2N/A /* check here for update request is successful */
2N/A if (rcode == NOERROR) {
2N/A syslog(LOG_INFO,
2N/A "dyndns: secure update completed");
2N/A ret = 0;
2N/A break;
2N/A }
2N/A
2N/A dyndns_syslog(LOG_ERR, rcode,
2N/A "secure update response code");
2N/A DYNDNS_ADD16(mu.hp->arcount, -1);
2N/A ret = -1;
2N/A }
2N/A }
2N/A
2N/A free(buf);
2N/A free(rbuf);
2N/A
2N/A res_ndestroy(statp);
2N/A free(statp);
2N/A
2N/A if (ret != 0) {
2N/A syslog(LOG_ERR, "dyndns: %s failed on all configured name "
2N/A "servers", (secure_update) ?
2N/A "both non-secure and secure updates" :
2N/A "non-secure update");
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_zone_update
2N/A * Perform dynamic update on both forward and reverse lookup zone using
2N/A * the specified hostname and IP addresses. Before updating DNS, existing
2N/A * host entries with the same hostname in the forward lookup zone are removed
2N/A * and existing pointer entries with the same IP addresses in the reverse
2N/A * lookup zone are removed. After DNS update, host entries for current
2N/A * hostname will show current IP addresses and pointer entries for current
2N/A * IP addresses will show current hostname.
2N/A * Parameters:
2N/A * fqdn - fully-qualified domain name (in lower case)
2N/A *
2N/A * Returns:
2N/A * -1: some dynamic DNS updates errors
2N/A * 0: successful or DDNS disabled.
2N/A */
2N/Aint
2N/Adyndns_zone_update(const char *fqdn)
2N/A{
2N/A int forw_update_ok, error;
2N/A smb_niciter_t ni;
2N/A int rc;
2N/A char fqhn[MAXHOSTNAMELEN];
2N/A
2N/A if (fqdn == NULL || *fqdn == '\0')
2N/A return (0);
2N/A
2N/A if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
2N/A return (0);
2N/A
2N/A if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2N/A * must be set to lower case.
2N/A */
2N/A (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2N/A
2N/A error = 0;
2N/A forw_update_ok = 0;
2N/A
2N/A /*
2N/A * NULL IP is okay since we are removing all using the hostname.
2N/A */
2N/A if (dyndns_update_nameaddr(DYNDNS_UPDATE_DEL_ALL,
2N/A DYNDNS_ZONE_FWD, fqhn, AF_INET, NULL, 0,
2N/A DYNDNS_CHECK_NONE, fqhn) == 0) {
2N/A forw_update_ok = 1;
2N/A } else {
2N/A error++;
2N/A }
2N/A
2N/A if (smb_config_getbool(SMB_CI_IPV6_ENABLE)) {
2N/A forw_update_ok = 0;
2N/A if (dyndns_update_nameaddr(DYNDNS_UPDATE_DEL_ALL,
2N/A DYNDNS_ZONE_FWD, fqhn, AF_INET6, NULL, 0,
2N/A DYNDNS_CHECK_NONE, fqhn) == 0) {
2N/A forw_update_ok = 1;
2N/A } else {
2N/A error++;
2N/A }
2N/A }
2N/A
2N/A if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
2N/A return (-1);
2N/A
2N/A do {
2N/A if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2N/A continue;
2N/A if (forw_update_ok) {
2N/A rc = dyndns_update_nameaddr(DYNDNS_UPDATE_ADD,
2N/A DYNDNS_ZONE_FWD, fqhn, ni.ni_nic.nic_ip.a_family,
2N/A &ni.ni_nic.nic_ip.au_addr, DYNDNS_TTL,
2N/A DYNDNS_CHECK_NONE, fqhn);
2N/A
2N/A if (rc == -1)
2N/A error++;
2N/A }
2N/A
2N/A /*
2N/A * NULL hostname is okay since we are removing all using the IP.
2N/A */
2N/A rc = dyndns_update_nameaddr(DYNDNS_UPDATE_DEL_ALL,
2N/A DYNDNS_ZONE_REV, NULL, ni.ni_nic.nic_ip.a_family,
2N/A &ni.ni_nic.nic_ip.au_addr, 0, DYNDNS_CHECK_NONE, fqhn);
2N/A if (rc == 0)
2N/A rc = dyndns_update_nameaddr(DYNDNS_UPDATE_ADD,
2N/A DYNDNS_ZONE_REV, fqhn, ni.ni_nic.nic_ip.a_family,
2N/A &ni.ni_nic.nic_ip.au_addr, DYNDNS_TTL,
2N/A DYNDNS_CHECK_NONE, fqhn);
2N/A
2N/A if (rc == -1)
2N/A error++;
2N/A
2N/A } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2N/A
2N/A return ((error == 0) ? 0 : -1);
2N/A}
2N/A
2N/A/*
2N/A * dyndns_zone_clear
2N/A * Clear the rev zone records. Must be called to clear the OLD if list
2N/A * of down records prior to updating the list with new information.
2N/A *
2N/A * Parameters:
2N/A * fqdn - fully-qualified domain name (in lower case)
2N/A * Returns:
2N/A * -1: some dynamic DNS updates errors
2N/A * 0: successful or DDNS disabled.
2N/A */
2N/Aint
2N/Adyndns_zone_clear(const char *fqdn)
2N/A{
2N/A int error;
2N/A smb_niciter_t ni;
2N/A int rc;
2N/A char fqhn[MAXHOSTNAMELEN];
2N/A
2N/A if (fqdn == NULL || *fqdn == '\0')
2N/A return (0);
2N/A
2N/A if (!smb_config_getbool(SMB_CI_DYNDNS_ENABLE))
2N/A return (0);
2N/A
2N/A if (smb_gethostname(fqhn, MAXHOSTNAMELEN, SMB_CASE_LOWER) != 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * To comply with RFC 4120 section 6.2.1, the fully-qualified hostname
2N/A * must be set to lower case.
2N/A */
2N/A (void) snprintf(fqhn, MAXHOSTNAMELEN, "%s.%s", fqhn, fqdn);
2N/A
2N/A error = 0;
2N/A
2N/A if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
2N/A return (-1);
2N/A
2N/A do {
2N/A if (ni.ni_nic.nic_sysflags & IFF_PRIVATE)
2N/A continue;
2N/A /*
2N/A * NULL hostname is okay since we are removing all using the IP.
2N/A */
2N/A rc = dyndns_update_nameaddr(DYNDNS_UPDATE_DEL_ALL,
2N/A DYNDNS_ZONE_REV, NULL, ni.ni_nic.nic_ip.a_family,
2N/A &ni.ni_nic.nic_ip.au_addr, 0, DYNDNS_CHECK_NONE, fqhn);
2N/A if (rc != 0)
2N/A error++;
2N/A
2N/A } while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
2N/A
2N/A return ((error == 0) ? 0 : -1);
2N/A}