#include "winbindd.h"
struct winbindd_cm_conn {
struct winbindd_cm_conn *prev, *next;
fstring domain;
fstring controller;
fstring pipe_name;
struct cli_state *cli;
POLICY_HND pol;
};
struct winbindd_cm_conn *cm_conns = NULL;
#define GET_DC_NAME_CACHE_TIMEOUT 30
struct get_dc_name_cache {
fstring domain_name;
fstring srv_name;
time_t lookup_time;
struct get_dc_name_cache *prev, *next;
};
static BOOL cm_get_dc_name(char *domain, fstring srv_name)
{
static struct get_dc_name_cache *get_dc_name_cache;
struct get_dc_name_cache *dcc;
struct in_addr *ip_list, dc_ip;
int count, i;
for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
if (!strequal(domain, dcc->domain_name))
continue;
if ((time(NULL) - dcc->lookup_time) >
GET_DC_NAME_CACHE_TIMEOUT) {
DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
DLIST_REMOVE(get_dc_name_cache, dcc);
SAFE_FREE(dcc);
break;
}
if (dcc->srv_name[0]) {
DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
fstrcpy(srv_name, dcc->srv_name);
return True;
} else {
DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
return False;
}
}
DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
if (!(dcc = (struct get_dc_name_cache *)
malloc(sizeof(struct get_dc_name_cache))))
return False;
ZERO_STRUCTP(dcc);
fstrcpy(dcc->domain_name, domain);
dcc->lookup_time = time(NULL);
DLIST_ADD(get_dc_name_cache, dcc);
if (!get_dc_list(False, domain, &ip_list, &count)) {
DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
return False;
}
for (i = 0; i < count; i++) {
if(is_local_net(ip_list[i]))
goto got_ip;
}
if (count == 0) {
DEBUG(3, ("No domain controllers for domain %s\n", domain));
return False;
}
i = (sys_random() % count);
got_ip:
dc_ip = ip_list[i];
SAFE_FREE(ip_list);
if (!name_status_find(domain, 0x1c, 0x20, dc_ip, srv_name)) {
DEBUG(3, ("Error looking up DC name for %s in domain %s\n", inet_ntoa(dc_ip), domain));
return False;
}
fstrcpy(dcc->srv_name, srv_name);
return True;
}
void cm_init_creds(struct ntuser_creds *creds)
{
char *username, *password;
ZERO_STRUCTP(creds);
creds->pwd.null_pwd = True;
username = secrets_fetch(SECRETS_AUTH_USER, NULL);
password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
if (username && *username) {
pwd_set_cleartext(&creds->pwd, password);
pwd_make_lm_nt_16(&creds->pwd, password);
fstrcpy(creds->user_name, username);
fstrcpy(creds->domain, lp_workgroup());
DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
creds->user_name));
} else
DEBUG(3, ("IPC$ connections done anonymously\n"));
}
#define OPEN_CONNECTION_CACHE_TIMEOUT 30
struct open_connection_cache {
fstring domain_name;
fstring controller;
time_t lookup_time;
struct open_connection_cache *prev, *next;
};
static BOOL cm_open_connection(char *domain, char *pipe_name,
struct winbindd_cm_conn *new_conn)
{
static struct open_connection_cache *open_connection_cache;
struct open_connection_cache *occ;
struct nmb_name calling, called;
extern pstring global_myname;
fstring dest_host;
struct in_addr dest_ip;
BOOL result = False;
struct ntuser_creds creds;
fstrcpy(new_conn->domain, domain);
fstrcpy(new_conn->pipe_name, pipe_name);
if (!cm_get_dc_name(domain, new_conn->controller))
goto done;
for (occ = open_connection_cache; occ; occ = occ->next) {
if (!(strequal(domain, occ->domain_name) &&
strequal(new_conn->controller, occ->controller)))
continue;
if ((time(NULL) - occ->lookup_time) >
OPEN_CONNECTION_CACHE_TIMEOUT) {
DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
DLIST_REMOVE(open_connection_cache, occ);
SAFE_FREE(occ);
break;
}
DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
goto done;
}
if (!(new_conn->cli = cli_initialise(NULL)))
goto done;
if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
goto done;
make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
cm_init_creds(&creds);
cli_init_creds(new_conn->cli, &creds);
if (!cli_establish_connection(new_conn->cli, new_conn->controller,
&dest_ip, &calling, &called, "IPC$",
"IPC", False, True))
goto done;
if (!cli_nt_session_open (new_conn->cli, pipe_name))
goto done;
result = True;
done:
if (!result) {
if (!(occ = (struct open_connection_cache *)
malloc(sizeof(struct open_connection_cache))))
return False;
ZERO_STRUCTP(occ);
fstrcpy(occ->domain_name, domain);
fstrcpy(occ->controller, new_conn->controller);
occ->lookup_time = time(NULL);
DLIST_ADD(open_connection_cache, occ);
}
if (!result && new_conn->cli)
cli_shutdown(new_conn->cli);
return result;
}
static BOOL connection_ok(struct winbindd_cm_conn *conn)
{
if (!conn->cli->initialised)
return False;
if (conn->cli->fd == -1)
return False;
return True;
}
CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
{
struct winbindd_cm_conn *conn;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
NTSTATUS result;
static CLI_POLICY_HND hnd;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_LSARPC)) {
if (!connection_ok(conn)) {
DLIST_REMOVE(cm_conns, conn);
return NULL;
}
goto ok;
}
}
if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
return NULL;
ZERO_STRUCTP(conn);
if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
return NULL;
}
result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
des_access, &conn->pol);
if (!NT_STATUS_IS_OK(result))
return NULL;
DLIST_ADD(cm_conns, conn);
ok:
hnd.pol = conn->pol;
hnd.cli = conn->cli;
return &hnd;
}
CLI_POLICY_HND *cm_get_sam_handle(char *domain)
{
struct winbindd_cm_conn *conn;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
NTSTATUS result;
static CLI_POLICY_HND hnd;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
if (!connection_ok(conn)) {
DLIST_REMOVE(cm_conns, conn);
return NULL;
}
goto ok;
}
}
if (!(conn = (struct winbindd_cm_conn *)
malloc(sizeof(struct winbindd_cm_conn))))
return NULL;
ZERO_STRUCTP(conn);
if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
return NULL;
}
result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
des_access, &conn->pol);
if (!NT_STATUS_IS_OK(result))
return NULL;
DLIST_ADD(cm_conns, conn);
ok:
hnd.pol = conn->pol;
hnd.cli = conn->cli;
return &hnd;
}
#if 0
CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
{
struct winbindd_cm_conn *conn, *basic_conn = NULL;
static CLI_POLICY_HND hnd;
NTSTATUS result;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
if (!connection_ok(conn)) {
DLIST_REMOVE(cm_conns, conn);
return NULL;
}
goto ok;
}
}
if (!cm_get_sam_handle(domain))
return False;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
basic_conn = conn;
}
if (!(conn = (struct winbindd_cm_conn *)
malloc(sizeof(struct winbindd_cm_conn))))
return NULL;
ZERO_STRUCTP(conn);
fstrcpy(conn->domain, basic_conn->domain);
fstrcpy(conn->controller, basic_conn->controller);
fstrcpy(conn->pipe_name, basic_conn->pipe_name);
conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
conn->cli = basic_conn->cli;
result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
&basic_conn->pol, des_access,
domain_sid, &conn->pol);
if (!NT_STATUS_IS_OK(result))
return NULL;
DLIST_ADD(cm_conns, conn);
ok:
hnd.pol = conn->pol;
hnd.cli = conn->cli;
return &hnd;
}
CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
uint32 user_rid)
{
struct winbindd_cm_conn *conn, *basic_conn = NULL;
static CLI_POLICY_HND hnd;
NTSTATUS result;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
conn->pipe_data.samr.rid == user_rid) {
if (!connection_ok(conn)) {
DLIST_REMOVE(cm_conns, conn);
return NULL;
}
goto ok;
}
}
if (!cm_get_sam_dom_handle(domain, domain_sid))
return NULL;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
basic_conn = conn;
}
if (!basic_conn) {
DEBUG(0, ("No domain sam handle was created!\n"));
return NULL;
}
if (!(conn = (struct winbindd_cm_conn *)
malloc(sizeof(struct winbindd_cm_conn))))
return NULL;
ZERO_STRUCTP(conn);
fstrcpy(conn->domain, basic_conn->domain);
fstrcpy(conn->controller, basic_conn->controller);
fstrcpy(conn->pipe_name, basic_conn->pipe_name);
conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
conn->cli = basic_conn->cli;
conn->pipe_data.samr.rid = user_rid;
result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
&basic_conn->pol, des_access, user_rid,
&conn->pol);
if (!NT_STATUS_IS_OK(result))
return NULL;
DLIST_ADD(cm_conns, conn);
ok:
hnd.pol = conn->pol;
hnd.cli = conn->cli;
return &hnd;
}
CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
uint32 group_rid)
{
struct winbindd_cm_conn *conn, *basic_conn = NULL;
static CLI_POLICY_HND hnd;
NTSTATUS result;
uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
conn->pipe_data.samr.rid == group_rid) {
if (!connection_ok(conn)) {
DLIST_REMOVE(cm_conns, conn);
return NULL;
}
goto ok;
}
}
if (!cm_get_sam_dom_handle(domain, domain_sid))
return NULL;
for (conn = cm_conns; conn; conn = conn->next) {
if (strequal(conn->domain, domain) &&
strequal(conn->pipe_name, PIPE_SAMR) &&
conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
basic_conn = conn;
}
if (!basic_conn) {
DEBUG(0, ("No domain sam handle was created!\n"));
return NULL;
}
if (!(conn = (struct winbindd_cm_conn *)
malloc(sizeof(struct winbindd_cm_conn))))
return NULL;
ZERO_STRUCTP(conn);
fstrcpy(conn->domain, basic_conn->domain);
fstrcpy(conn->controller, basic_conn->controller);
fstrcpy(conn->pipe_name, basic_conn->pipe_name);
conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
conn->cli = basic_conn->cli;
conn->pipe_data.samr.rid = group_rid;
result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
&basic_conn->pol, des_access, group_rid,
&conn->pol);
if (!NT_STATUS_IS_OK(result))
return NULL;
DLIST_ADD(cm_conns, conn);
ok:
hnd.pol = conn->pol;
hnd.cli = conn->cli;
return &hnd;
}
#endif
NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
struct cli_state **cli)
{
struct winbindd_cm_conn conn;
NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
ZERO_STRUCT(conn);
if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
DEBUG(3, ("Could not open a connection to %s\n", domain));
return result;
}
result = cli_nt_setup_creds(conn.cli, trust_passwd);
if (!NT_STATUS_IS_OK(result)) {
DEBUG(0, ("error connecting to domain password server: %s\n",
get_nt_error_msg(result)));
cli_shutdown(conn.cli);
return result;
}
if (cli)
*cli = conn.cli;
return result;
}
static void dump_conn_list(void)
{
struct winbindd_cm_conn *con;
DEBUG(0, ("\tDomain Controller Pipe\n"));
for(con = cm_conns; con; con = con->next) {
char *msg;
if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
DEBUG(0, ("Error: not enough memory!\n"));
} else {
DEBUG(0, ("%s\n", msg));
SAFE_FREE(msg);
}
}
}
void winbindd_cm_status(void)
{
DEBUG(0, ("winbindd connection manager status:\n"));
if (cm_conns)
dump_conn_list();
else
DEBUG(0, ("\tNo active connections\n"));
}