#include <stdlib.h>
#include <syslog.h>
#include <alloca.h>
#include <arpa/inet.h>
#include <resolv.h>
#include <smbsrv/mailslot.h>
#include <smbsrv/libsmbns.h>
#include <smbns_browser.h>
#include <smbns_netbios.h>
static void smb_netlogon_query(struct name_entry *server, char *mailbox,
char *domain);
static void smb_netlogon_samlogon(struct name_entry *, char *,
char *, smb_sid_t *);
static void smb_netlogon_send(struct name_entry *name, char *domain,
unsigned char *buffer, int count);
static void smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr);
static int smb_better_dc(uint32_t cur_ip, uint32_t new_ip);
extern smb_ntdomain_t ntdomain_info;
extern mutex_t ntdomain_mtx;
extern cond_t ntdomain_cv;
void
smb_netlogon_request(struct name_entry *server, char *domain)
{
smb_domain_t di;
smb_sid_t *sid = NULL;
int protocol = NETLOGON_PROTO_NETLOGON;
if (domain == NULL || *domain == '\0')
return;
(void) mutex_lock(&ntdomain_mtx);
(void) strlcpy(ntdomain_info.n_domain, domain,
sizeof (ntdomain_info.n_domain));
(void) mutex_unlock(&ntdomain_mtx);
smb_config_getdomaininfo(di.di_nbname, NULL, di.di_sid, NULL, NULL);
if (smb_strcasecmp(di.di_nbname, domain, 0) == 0) {
if ((sid = smb_sid_fromstr(di.di_sid)) != NULL)
protocol = NETLOGON_PROTO_SAMLOGON;
}
if (protocol == NETLOGON_PROTO_SAMLOGON)
smb_netlogon_samlogon(server, MAILSLOT_NETLOGON_SAMLOGON_RDC,
domain, sid);
else
smb_netlogon_query(server, MAILSLOT_NETLOGON_RDC, domain);
smb_sid_free(sid);
}
void
smb_netlogon_receive(struct datagram *datagram,
char *mailbox,
unsigned char *data,
int datalen)
{
struct netlogon_opt {
char *mailslot;
void (*handler)();
} netlogon_opt[] = {
{ MAILSLOT_NETLOGON_RDC, smb_netlogon_rdc_rsp },
{ MAILSLOT_NETLOGON_SAMLOGON_RDC, smb_netlogon_rdc_rsp },
};
smb_msgbuf_t mb;
unsigned short opcode;
char src_name[SMB_PI_MAX_HOST];
smb_wchar_t unicode_src_name[SMB_PI_MAX_HOST];
uint32_t src_ipaddr;
char *junk;
char *primary;
char *domain;
int i;
char ipstr[16];
int rc;
src_ipaddr = datagram->src.addr_list.sin.sin_addr.s_addr;
(void) oemtoucs(unicode_src_name, (char *)datagram->src.name,
SMB_PI_MAX_HOST, OEM_CPG_850);
(void) smb_wcstombs(src_name, unicode_src_name, SMB_PI_MAX_HOST);
(void) trim_whitespace(src_name);
(void) inet_ntop(AF_INET, (const void *)(&src_ipaddr), ipstr,
sizeof (ipstr));
syslog(LOG_DEBUG, "NetLogonReceive: src=%s [%s], mbx=%s",
src_name, ipstr, mailbox);
smb_msgbuf_init(&mb, data, datalen, 0);
if (smb_msgbuf_decode(&mb, "w", &opcode) < 0) {
syslog(LOG_ERR, "NetLogonReceive: decode error");
smb_msgbuf_term(&mb);
return;
}
switch (opcode) {
case LOGON_PRIMARY_RESPONSE:
rc = smb_msgbuf_decode(&mb, "sUU", &junk, &primary, &domain);
if (rc < 0) {
syslog(LOG_ERR,
"NetLogonResponse: opcode %d decode error",
opcode);
smb_msgbuf_term(&mb);
return;
}
break;
case LOGON_SAM_LOGON_RESPONSE:
case LOGON_SAM_USER_UNKNOWN:
rc = smb_msgbuf_decode(&mb, "UUU", &primary, &junk, &domain);
if (rc < 0) {
syslog(LOG_ERR,
"NetLogonResponse: opcode %d decode error",
opcode);
smb_msgbuf_term(&mb);
return;
}
primary += strspn(primary, "\\");
break;
default:
syslog(LOG_DEBUG, "NetLogonReceive: opcode 0x%04x", opcode);
smb_msgbuf_term(&mb);
return;
}
if (domain == NULL || primary == NULL) {
syslog(LOG_ERR, "NetLogonResponse: malformed packet");
smb_msgbuf_term(&mb);
return;
}
syslog(LOG_DEBUG, "DC Offer Domain=%s PDC=%s From=%s",
domain, primary, src_name);
(void) mutex_lock(&ntdomain_mtx);
if (strcasecmp(domain, ntdomain_info.n_domain)) {
syslog(LOG_DEBUG, "NetLogonResponse: other domain "
"%s, requested %s", domain, ntdomain_info.n_domain);
smb_msgbuf_term(&mb);
(void) mutex_unlock(&ntdomain_mtx);
return;
}
(void) mutex_unlock(&ntdomain_mtx);
for (i = 0; i < sizeof (netlogon_opt)/sizeof (netlogon_opt[0]); ++i) {
if (strcasecmp(netlogon_opt[i].mailslot, mailbox) == 0) {
syslog(LOG_DEBUG, "NetLogonReceive: %s", mailbox);
(*netlogon_opt[i].handler)(primary, src_ipaddr);
smb_msgbuf_term(&mb);
return;
}
}
syslog(LOG_DEBUG, "NetLogonReceive[%s]: unknown mailslot", mailbox);
smb_msgbuf_term(&mb);
}
static void
smb_netlogon_query(struct name_entry *server,
char *mailbox,
char *domain)
{
smb_msgbuf_t mb;
int offset, announce_len, data_length, name_lengths;
unsigned char buffer[MAX_DATAGRAM_LENGTH];
char hostname[NETBIOS_NAME_SZ];
if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0)
return;
name_lengths = strlen(mailbox)+1+strlen(hostname)+1;
data_length = sizeof (short) + name_lengths + (name_lengths & 1) +
smb_wcequiv_strlen(hostname) + 2 + sizeof (long) + sizeof (short) +
sizeof (short);
offset = smb_browser_load_transact_header(buffer,
sizeof (buffer), data_length, ONE_WAY_TRANSACTION,
MAILSLOT_NETLOGON);
if (offset < 0)
return;
smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0);
announce_len = smb_msgbuf_encode(&mb, "wssUlww",
(short)LOGON_PRIMARY_QUERY,
hostname,
mailbox,
hostname,
0x1,
0xffff,
0xffff);
if (announce_len <= 0) {
smb_msgbuf_term(&mb);
syslog(LOG_ERR, "NetLogonQuery: encode error");
return;
}
smb_netlogon_send(server, domain, buffer, offset + announce_len);
smb_msgbuf_term(&mb);
}
static void
smb_netlogon_samlogon(struct name_entry *server,
char *mailbox,
char *domain,
smb_sid_t *domain_sid)
{
smb_msgbuf_t mb;
unsigned domain_sid_len;
char *username;
unsigned char buffer[MAX_DATAGRAM_LENGTH];
int offset;
int announce_len;
int data_length;
int name_length;
char hostname[NETBIOS_NAME_SZ];
syslog(LOG_DEBUG, "NetLogonSamLogonReq: %s", domain);
if (smb_getnetbiosname(hostname, sizeof (hostname)) != 0)
return;
name_length = strlen(hostname) + 2;
username = alloca(name_length);
(void) snprintf(username, name_length, "%s$", hostname);
domain_sid_len = smb_sid_len(domain_sid);
name_length = strlen(mailbox)+1;
data_length = sizeof (short)
+ sizeof (short)
+ smb_wcequiv_strlen(hostname) + 2
+ smb_wcequiv_strlen(username) + 2
+ name_length
+ sizeof (long)
+ sizeof (long)
+ domain_sid_len + 3
+ sizeof (long)
+ sizeof (short)
+ sizeof (short);
offset = smb_browser_load_transact_header(buffer,
sizeof (buffer), data_length, ONE_WAY_TRANSACTION,
MAILSLOT_NTLOGON);
if (offset < 0) {
syslog(LOG_ERR, "NetLogonSamLogonReq: header error");
return;
}
smb_msgbuf_init(&mb, buffer + offset, sizeof (buffer) - offset, 0);
announce_len = smb_msgbuf_encode(&mb, "wwUUsll3.#clww",
(short)LOGON_SAM_LOGON_REQUEST,
0,
hostname,
username,
mailbox,
0x00000080,
domain_sid_len,
domain_sid_len, domain_sid,
0x00000001,
0xffff,
0xffff);
if (announce_len <= 0) {
syslog(LOG_ERR, "NetLogonSamLogonReq: encode error");
smb_msgbuf_term(&mb);
return;
}
smb_netlogon_send(server, domain, buffer, offset + announce_len);
smb_msgbuf_term(&mb);
}
static void
smb_netlogon_send(struct name_entry *name,
char *domain,
unsigned char *buffer,
int count)
{
static char suffix[] = { 0x1B, 0x1C };
struct name_entry dname;
struct name_entry *dest;
struct name_entry *dest_dup;
int i;
for (i = 0; i < sizeof (suffix)/sizeof (suffix[0]); i++) {
smb_init_name_struct((unsigned char *)domain, suffix[i],
0, 0, 0, 0, 0, &dname);
syslog(LOG_DEBUG, "SmbNetlogonSend");
smb_netbios_name_logf(&dname);
if ((dest = smb_name_find_name(&dname)) != 0) {
dest_dup = smb_netbios_name_dup(dest, 1);
smb_name_unlock_name(dest);
if (dest_dup) {
(void) smb_netbios_datagram_send(name,
dest_dup, buffer, count);
free(dest_dup);
}
} else {
syslog(LOG_DEBUG,
"SmbNetlogonSend: could not find %s<0x%X>",
domain, suffix[i]);
}
}
}
static void
smb_netlogon_rdc_rsp(char *src_name, uint32_t src_ipaddr)
{
static int initialized = 0;
uint32_t ipaddr;
uint32_t prefer_ipaddr;
char ipstr[INET_ADDRSTRLEN];
char srcip[INET_ADDRSTRLEN];
int rc;
(void) inet_ntop(AF_INET, &src_ipaddr, srcip, INET_ADDRSTRLEN);
rc = smb_config_getstr(SMB_CI_DOMAIN_SRV, ipstr, INET_ADDRSTRLEN);
if (rc == SMBD_SMF_OK) {
rc = inet_pton(AF_INET, ipstr, &prefer_ipaddr);
if (rc == 0)
prefer_ipaddr = 0;
if (!initialized) {
syslog(LOG_DEBUG, "SMB DC Preference: %s", ipstr);
initialized = 1;
}
}
(void) mutex_lock(&ntdomain_mtx);
syslog(LOG_DEBUG, "DC Offer [%s]: %s [%s]",
ntdomain_info.n_domain, src_name, srcip);
if (ntdomain_info.n_ipaddr != 0) {
if (prefer_ipaddr != 0 &&
prefer_ipaddr == ntdomain_info.n_ipaddr) {
syslog(LOG_DEBUG, "DC for %s: %s [%s]",
ntdomain_info.n_domain, src_name, srcip);
(void) mutex_unlock(&ntdomain_mtx);
return;
}
ipaddr = ntdomain_info.n_ipaddr;
} else
ipaddr = 0;
if (smb_better_dc(ipaddr, src_ipaddr) ||
(prefer_ipaddr != 0 && prefer_ipaddr == src_ipaddr)) {
(void) strlcpy(ntdomain_info.n_name, src_name,
SMB_PI_MAX_DOMAIN);
ntdomain_info.n_ipaddr = src_ipaddr;
(void) cond_broadcast(&ntdomain_cv);
syslog(LOG_DEBUG, "DC discovered for %s: %s [%s]",
ntdomain_info.n_domain, src_name, srcip);
}
(void) mutex_unlock(&ntdomain_mtx);
}
static int
smb_better_dc(uint32_t cur_ip, uint32_t new_ip)
{
smb_inaddr_t ipaddr;
if (cur_ip == 0)
return (1);
ipaddr.a_family = AF_INET;
ipaddr.a_ipv4 = cur_ip;
if (smb_nic_is_same_subnet(&ipaddr))
return (0);
ipaddr.a_family = AF_INET;
ipaddr.a_ipv4 = new_ip;
if (smb_nic_is_same_subnet(&ipaddr))
return (1);
return (0);
}