#include "winbind_nss_config.h"
#include "winbindd_nss.h"
int winbindd_fd = -1;
static char *excluded_domain;
void free_response(struct winbindd_response *response)
{
if (response)
SAFE_FREE(response->extra_data);
}
void winbind_exclude_domain(const char *domain)
{
SAFE_FREE(excluded_domain);
excluded_domain = strdup(domain);
}
void init_request(struct winbindd_request *request, int request_type)
{
static char *domain_env;
static BOOL initialised;
request->cmd = (enum winbindd_cmd)request_type;
request->pid = getpid();
request->domain[0] = '\0';
if (!initialised) {
initialised = True;
domain_env = getenv(WINBINDD_DOMAIN_ENV);
}
if (domain_env) {
strncpy(request->domain, domain_env,
sizeof(request->domain) - 1);
request->domain[sizeof(request->domain) - 1] = '\0';
}
}
void init_response(struct winbindd_response *response)
{
response->result = WINBINDD_ERROR;
}
void close_sock(void)
{
if (winbindd_fd != -1) {
close(winbindd_fd);
winbindd_fd = -1;
}
}
int winbind_open_pipe_sock(void)
{
struct sockaddr_un sunaddr;
static pid_t our_pid;
struct stat st;
pstring path;
if (our_pid != getpid()) {
close_sock();
our_pid = getpid();
}
if (winbindd_fd != -1) {
return winbindd_fd;
}
if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
return -1;
}
if (!S_ISDIR(st.st_mode) ||
(st.st_uid != 0 && st.st_uid != geteuid())) {
return -1;
}
strncpy(path, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
strncat(path, "/", sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
ZERO_STRUCT(sunaddr);
sunaddr.sun_family = AF_UNIX;
strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
if (lstat(path, &st) == -1) {
return -1;
}
if (!S_ISSOCK(st.st_mode) ||
(st.st_uid != 0 && st.st_uid != geteuid())) {
return -1;
}
if ((winbindd_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
return -1;
}
if (connect(winbindd_fd, (struct sockaddr *)&sunaddr,
sizeof(sunaddr)) == -1) {
close_sock();
return -1;
}
return winbindd_fd;
}
int write_sock(void *buffer, int count)
{
int result, nwritten;
restart:
if (winbind_open_pipe_sock() == -1) {
return -1;
}
nwritten = 0;
while(nwritten < count) {
struct timeval tv;
fd_set r_fds;
FD_ZERO(&r_fds);
FD_SET(winbindd_fd, &r_fds);
ZERO_STRUCT(tv);
if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
close_sock();
return -1;
}
if (!FD_ISSET(winbindd_fd, &r_fds)) {
result = write(winbindd_fd,
(char *)buffer + nwritten,
count - nwritten);
if ((result == -1) || (result == 0)) {
close_sock();
return -1;
}
nwritten += result;
} else {
close_sock();
goto restart;
}
}
return nwritten;
}
static int read_sock(void *buffer, int count)
{
int result = 0, nread = 0;
while(nread < count) {
result = read(winbindd_fd, (char *)buffer + nread,
count - nread);
if ((result == -1) || (result == 0)) {
close_sock();
return -1;
}
nread += result;
}
return result;
}
int read_reply(struct winbindd_response *response)
{
int result1, result2 = 0;
if (!response) {
return -1;
}
if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
== -1) {
return -1;
}
response->extra_data = NULL;
if (response->length > sizeof(struct winbindd_response)) {
int extra_data_len = response->length -
sizeof(struct winbindd_response);
if (!(response->extra_data = malloc(extra_data_len))) {
return -1;
}
if ((result2 = read_sock(response->extra_data, extra_data_len))
== -1) {
free_response(response);
return -1;
}
}
return result1 + result2;
}
NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
{
struct winbindd_request lrequest;
if (getenv(WINBINDD_DONT_ENV)) {
return NSS_STATUS_NOTFOUND;
}
if (excluded_domain &&
strcasecmp(excluded_domain, request->domain) == 0) {
return NSS_STATUS_NOTFOUND;
}
if (!request) {
ZERO_STRUCT(lrequest);
request = &lrequest;
}
init_request(request, req_type);
if (write_sock(request, sizeof(*request)) == -1) {
return NSS_STATUS_UNAVAIL;
}
return NSS_STATUS_SUCCESS;
}
NSS_STATUS winbindd_get_response(struct winbindd_response *response)
{
struct winbindd_response lresponse;
if (!response) {
ZERO_STRUCT(lresponse);
response = &lresponse;
}
init_response(response);
if (read_reply(response) == -1) {
return NSS_STATUS_UNAVAIL;
}
if (response == &lresponse) {
free_response(response);
}
if (response->result != WINBINDD_OK) {
return NSS_STATUS_NOTFOUND;
}
return NSS_STATUS_SUCCESS;
}
NSS_STATUS winbindd_request(int req_type,
struct winbindd_request *request,
struct winbindd_response *response)
{
NSS_STATUS status;
status = winbindd_send_request(req_type, request);
if (status != NSS_STATUS_SUCCESS)
return(status);
return winbindd_get_response(response);
}