diff options
-rw-r--r-- | source3/nsswitch/common.c | 282 | ||||
-rw-r--r-- | source3/nsswitch/pam_winbind.c | 388 | ||||
-rw-r--r-- | source3/nsswitch/winbind_nss.c | 465 | ||||
-rw-r--r-- | source3/nsswitch/winbind_nss_config.h | 111 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.c | 663 | ||||
-rw-r--r-- | source3/nsswitch/winbindd.h | 106 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_cache.c | 416 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_group.c | 763 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_idmap.c | 235 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_nss.h | 118 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_pam.c | 97 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_proto.h | 113 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_user.c | 412 | ||||
-rw-r--r-- | source3/nsswitch/winbindd_util.c | 633 |
14 files changed, 4802 insertions, 0 deletions
diff --git a/source3/nsswitch/common.c b/source3/nsswitch/common.c new file mode 100644 index 0000000000..be0b24cc5d --- /dev/null +++ b/source3/nsswitch/common.c @@ -0,0 +1,282 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + winbind client common code + + Copyright (C) Tim Potter 2000 + Copyright (C) Andrew Tridgell 2000 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "ntdom_config.h" +#include "winbindd_ntdom.h" + +/* Global variables. These are effectively the client state information */ + +static int established_socket = -1; /* fd for winbindd socket */ + +/* + * Utility and helper functions + */ + +void init_request(struct winbindd_request *req,int rq_type) +{ + static char *domain_env; + static BOOL initialised; + + req->cmd = rq_type; + req->pid = getpid(); + req->domain[0] = '\0'; + + if (!initialised) { + initialised = True; + domain_env = getenv(WINBINDD_DOMAIN_ENV); + } + + if (domain_env) { + strncpy(req->domain, domain_env, + sizeof(req->domain) - 1); + req->domain[sizeof(req->domain) - 1] = '\0'; + } +} + +/* Close established socket */ + +void close_sock(void) +{ + if (established_socket != -1) { + close(established_socket); + established_socket = -1; + } +} + +/* Connect to winbindd socket */ + +static int open_pipe_sock(void) +{ + struct sockaddr_un sunaddr; + static pid_t our_pid; + struct stat st; + pstring path; + + if (our_pid != getpid()) { + if (established_socket != -1) { + close(established_socket); + } + established_socket = -1; + our_pid = getpid(); + } + + if (established_socket != -1) { + return established_socket; + } + + /* Check permissions on unix socket directory */ + + if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) { + return -1; + } + + if (!S_ISDIR(st.st_mode) || (st.st_uid != 0)) { + return -1; + } + + /* Connect to socket */ + + 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 socket file doesn't exist, don't bother trying to connect with + retry. This is an attempt to make the system usable when the + winbindd daemon is not running. */ + + if (lstat(path, &st) == -1) { + return -1; + } + + /* Check permissions on unix socket file */ + + if (!S_ISSOCK(st.st_mode) || (st.st_uid != 0)) { + return -1; + } + + /* Connect to socket */ + + if ((established_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + return -1; + } + + if (connect(established_socket, (struct sockaddr *)&sunaddr, + sizeof(sunaddr)) == -1) { + close_sock(); + return -1; + } + + return established_socket; +} + +/* Write data to winbindd socket with timeout */ + +int write_sock(void *buffer, int count) +{ + int result, nwritten; + + /* Open connection to winbind daemon */ + + restart: + + if (open_pipe_sock() == -1) { + return -1; + } + + /* Write data to socket */ + + nwritten = 0; + + while(nwritten < count) { + struct timeval tv; + fd_set r_fds; + int selret; + + /* Catch pipe close on other end by checking if a read() call would + not block by calling select(). */ + + FD_ZERO(&r_fds); + FD_SET(established_socket, &r_fds); + ZERO_STRUCT(tv); + + if ((selret = select(established_socket + 1, &r_fds, NULL, NULL, + &tv)) == -1) { + close_sock(); + return -1; /* Select error */ + } + + /* Write should be OK if fd not available for reading */ + + if (!FD_ISSET(established_socket, &r_fds)) { + + /* Do the write */ + + result = write(established_socket, (char *)buffer + nwritten, + count - nwritten); + + if ((result == -1) || (result == 0)) { + + /* Write failed */ + + close_sock(); + return -1; + } + + nwritten += result; + + } else { + + /* Pipe has closed on remote end */ + + close_sock(); + goto restart; + } + } + + return nwritten; +} + +/* Read data from winbindd socket with timeout */ + +static int read_sock(void *buffer, int count) +{ + int result, nread; + + /* Read data from socket */ + + nread = 0; + + while(nread < count) { + + result = read(established_socket, (char *)buffer + nread, + count - nread); + + if ((result == -1) || (result == 0)) { + + /* Read failed. I think the only useful thing we can do here + is just return -1 and fail since the transaction has failed + half way through. */ + + close_sock(); + return -1; + } + + nread += result; + } + + return result; +} + +/* Read reply */ + +int read_reply(struct winbindd_response *response) +{ + int result1, result2; + + if (!response) { + return -1; + } + + /* Read fixed length response */ + + if ((result1 = read_sock(response, sizeof(struct winbindd_response))) + == -1) { + + return -1; + } + + /* Read variable length response */ + + if (response->length > sizeof(struct winbindd_response)) { + int extra_data_len = response->length - + sizeof(struct winbindd_response); + + /* Mallocate memory for extra data */ + + if (!(response->extra_data = malloc(extra_data_len))) { + return -1; + } + + if ((result2 = read_sock(response->extra_data, extra_data_len)) + == -1) { + + return -1; + } + } + + /* Return total amount of data read */ + + return result1 + result2; +} + diff --git a/source3/nsswitch/pam_winbind.c b/source3/nsswitch/pam_winbind.c new file mode 100644 index 0000000000..549d8c9a8a --- /dev/null +++ b/source3/nsswitch/pam_winbind.c @@ -0,0 +1,388 @@ +/* pam_winbind module + + Copyright Andrew Tridgell <tridge@samba.org> 2000 + + largely based on pam_userdb by Christian Gafton <gafton@redhat.com> +*/ + +#include <features.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#define MODULE_NAME "pam_winbind" +#define PAM_SM_AUTH +#define PAM_SM_ACCOUNT +#include <security/pam_modules.h> +#include <security/_pam_macros.h> + +#define PAM_DEBUG_ARG (1<<0) +#define PAM_USE_AUTHTOK_ARG (1<<1) +#define PAM_UNKNOWN_OK_ARG (1<<2) + +#include "ntdom_config.h" +#include "winbindd_ntdom.h" + +/* prototypes from common.c */ +void init_request(struct winbindd_request *req,int rq_type); +int write_sock(void *buffer, int count); +int read_reply(struct winbindd_response *response); + +/* some syslogging */ +static void _pam_log(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTH); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +static int ctrl = 0; + +static int _pam_parse(int argc, const char **argv) +{ + /* step through arguments */ + for (ctrl = 0; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcasecmp(*argv, "use_authtok")) + ctrl |= PAM_USE_AUTHTOK_ARG; + else if (!strcasecmp(*argv, "unknown_ok")) + ctrl |= PAM_UNKNOWN_OK_ARG; + else { + _pam_log(LOG_ERR, "pam_parse: unknown option; %s", *argv); + } + } + + return ctrl; +} + + +/* talk to winbindd */ +static int winbind_request(int req_type, const char *user, const char *pass) +{ + struct winbindd_request request; + struct winbindd_response response; + + ZERO_STRUCT(request); + + strncpy(request.data.auth.user, user, sizeof(request.data.auth.user)-1); + strncpy(request.data.auth.pass, pass, sizeof(request.data.auth.pass)-1); + + /* Fill in request and send down pipe */ + init_request(&request, req_type); + + if (write_sock(&request, sizeof(request)) == -1) { + return -2; + } + + /* Wait for reply */ + if (read_reply(&response) == -1) { + return -2; + } + + /* Copy reply data from socket */ + if (response.result != WINBINDD_OK) { + return 1; + } + + return 0; +} + +/* + * Looks up an user name and checks the password + * + * return values: + * 1 = User not found + * 0 = OK + * -1 = Password incorrect + * -2 = System error + */ +static int user_lookup(const char *user, const char *pass) +{ + return winbind_request(WINBINDD_PAM_AUTH, user, pass); +} + +/* + * Checks if a user has an account + * + * return values: + * 1 = User not found + * 0 = OK + * -1 = System error + */ +static int valid_user(const char *user) +{ + if (getpwnam(user)) return 0; + return 1; +} + +/* --- authentication management functions --- */ + +/* + * dummy conversation function sending exactly one prompt + * and expecting exactly one response from the other party + */ +static int converse(pam_handle_t *pamh, + struct pam_message **message, + struct pam_response **response) +{ + int retval; + struct pam_conv *conv; + + retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv ) ; + if (retval == PAM_SUCCESS) + retval = conv->conv(1, (const struct pam_message **)message, + response, conv->appdata_ptr); + + return retval; /* propagate error status */ +} + + +static char *_pam_delete(register char *xx) +{ + _pam_overwrite(xx); + _pam_drop(xx); + return NULL; +} + +/* + * This is a conversation function to obtain the user's password + */ +static int conversation(pam_handle_t *pamh) +{ + struct pam_message msg[2],*pmsg[2]; + struct pam_response *resp; + int retval; + char * token; + + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_PROMPT_ECHO_OFF; + msg[0].msg = "Password: "; + + /* so call the conversation expecting i responses */ + resp = NULL; + retval = converse(pamh, pmsg, &resp); + + if (resp != NULL) { + char * const item; + /* interpret the response */ + if (retval == PAM_SUCCESS) { /* a good conversation */ + token = x_strdup(resp[0].resp); + if (token == NULL) { + return PAM_AUTHTOK_RECOVER_ERR; + } + } + + /* set the auth token */ + retval = pam_set_item(pamh, PAM_AUTHTOK, token); + token = _pam_delete(token); /* clean it up */ + if ( (retval != PAM_SUCCESS) || + (retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &item)) != PAM_SUCCESS ) { + return retval; + } + + _pam_drop_reply(resp, 1); + } else { + retval = (retval == PAM_SUCCESS) + ? PAM_AUTHTOK_RECOVER_ERR:retval ; + } + + return retval; +} + + +PAM_EXTERN +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const char *username; + const char *password; + int retval = PAM_AUTH_ERR; + + /* parse arguments */ + ctrl = _pam_parse(argc, argv); + + /* Get the username */ + retval = pam_get_user(pamh, &username, NULL); + if ((retval != PAM_SUCCESS) || (!username)) { + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_DEBUG,"can not get the username"); + return PAM_SERVICE_ERR; + } + + if ((ctrl & PAM_USE_AUTHTOK_ARG) == 0) { + /* Converse just to be sure we have the password */ + retval = conversation(pamh); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "could not obtain password for `%s'", + username); + return PAM_CONV_ERR; + } + } + + /* Get the password */ + retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &password); + if (retval != PAM_SUCCESS) { + _pam_log(LOG_ERR, "Could not retrive user's password"); + return PAM_AUTHTOK_ERR; + } + + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_INFO, "Verify user `%s' with password `%s'", + username, password); + + /* Now use the username to look up password */ + retval = user_lookup(username, password); + switch (retval) { + case -2: + /* some sort of system error. The log was already printed */ + return PAM_SERVICE_ERR; + case -1: + /* incorrect password */ + _pam_log(LOG_WARNING, "user `%s' denied access (incorrect password)", username); + return PAM_AUTH_ERR; + case 1: + /* the user does not exist */ + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_NOTICE, "user `%s' not found", + username); + if (ctrl & PAM_UNKNOWN_OK_ARG) { + return PAM_IGNORE; + } + return PAM_USER_UNKNOWN; + case 0: + /* Otherwise, the authentication looked good */ + _pam_log(LOG_NOTICE, "user '%s' granted acces", username); + return PAM_SUCCESS; + default: + /* we don't know anything about this return value */ + _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'", + retval, username); + return PAM_SERVICE_ERR; + } + /* should not be reached */ + return PAM_IGNORE; +} + +PAM_EXTERN +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return PAM_SUCCESS; +} + +/* + * Account management. We want to verify that the account exists + * before returning PAM_SUCCESS + */ +PAM_EXTERN +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const char *username; + int retval = PAM_USER_UNKNOWN; + + /* parse arguments */ + ctrl = _pam_parse(argc, argv); + + /* Get the username */ + retval = pam_get_user(pamh, &username, NULL); + if ((retval != PAM_SUCCESS) || (!username)) { + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_DEBUG,"can not get the username"); + return PAM_SERVICE_ERR; + } + + /* Verify the username */ + retval = valid_user(username); + switch (retval) { + case -1: + /* some sort of system error. The log was already printed */ + return PAM_SERVICE_ERR; + case 1: + /* the user does not exist */ + if (ctrl & PAM_DEBUG_ARG) + _pam_log(LOG_NOTICE, "user `%s' not found", + username); + if (ctrl & PAM_UNKNOWN_OK_ARG) + return PAM_IGNORE; + return PAM_USER_UNKNOWN; + case 0: + /* Otherwise, the authentication looked good */ + _pam_log(LOG_NOTICE, "user '%s' granted acces", username); + return PAM_SUCCESS; + default: + /* we don't know anything about this return value */ + _pam_log(LOG_ERR, "internal module error (retval = %d, user = `%s'", + retval, username); + return PAM_SERVICE_ERR; + } + + /* should not be reached */ + return PAM_IGNORE; +} + + +#ifdef PAM_STATIC + +/* static module data */ + +struct pam_module _pam_userdb_modstruct = { + MODULE_NAME, + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + NULL, + NULL, + NULL, +}; + +#endif + +/* + * Copyright (c) Andrew Tridgell <tridge@samba.org> 2000 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/source3/nsswitch/winbind_nss.c b/source3/nsswitch/winbind_nss.c new file mode 100644 index 0000000000..48dc1882b3 --- /dev/null +++ b/source3/nsswitch/winbind_nss.c @@ -0,0 +1,465 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Windows NT Domain nsswitch module + + Copyright (C) Tim Potter 2000 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include "ntdom_config.h" +#include "winbindd_ntdom.h" + +/* prototypes from common.c */ +void init_request(struct winbindd_request *req,int rq_type); +int write_sock(void *buffer, int count); +int read_reply(struct winbindd_response *response); + + +/* Allocate some space from the nss static buffer. The buffer and buflen + are the pointers passed in by the C library to the _nss_ntdom_* + functions. */ + +static char *get_static(char **buffer, int *buflen, int len) +{ + char *result; + + /* Error check. We return false if things aren't set up right, or + there isn't enough buffer space left. */ + + if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) { + return NULL; + } + + /* Return an index into the static buffer */ + + result = *buffer; + *buffer += len; + *buflen -= len; + + return result; +} + +/* I've copied the strtok() replacement function next_token() from + lib/util_str.c as I really don't want to have to link in any other + objects if I can possibly avoid it. */ + +#ifdef strchr /* Aargh! This points at multibyte_strchr(). )-: */ +#undef strchr +#endif + +static char *last_ptr = NULL; + +BOOL next_token(char **ptr, char *buff, char *sep, size_t bufsize) +{ + char *s; + BOOL quoted; + size_t len=1; + + if (!ptr) ptr = &last_ptr; + if (!ptr) return(False); + + s = *ptr; + + /* default to simple separators */ + if (!sep) sep = " \t\n\r"; + + /* find the first non sep char */ + while(*s && strchr(sep,*s)) s++; + + /* nothing left? */ + if (! *s) return(False); + + /* copy over the token */ + for (quoted = False; + len < bufsize && *s && (quoted || !strchr(sep,*s)); + s++) { + + if (*s == '\"') { + quoted = !quoted; + } else { + len++; + *buff++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *buff = 0; + last_ptr = *ptr; + + return(True); +} + + +/* handle simple types of requests */ +static enum nss_status generic_request(int req_type, + struct winbindd_request *request, + struct winbindd_response *response) +{ + struct winbindd_request lrequest; + struct winbindd_response lresponse; + + if (!response) response = &lresponse; + if (!request) request = &lrequest; + + /* Fill in request and send down pipe */ + init_request(request, req_type); + + if (write_sock(request, sizeof(*request)) == -1) { + return NSS_STATUS_UNAVAIL; + } + + /* Wait for reply */ + if (read_reply(response) == -1) { + return NSS_STATUS_UNAVAIL; + } + + /* Copy reply data from socket */ + if (response->result != WINBINDD_OK) { + return NSS_STATUS_NOTFOUND; + } + + return NSS_STATUS_SUCCESS; +} + +/* Fill a pwent structure from a winbindd_response structure. We use + the static data passed to us by libc to put strings and stuff in. + Return errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of + memory. */ + +static enum nss_status fill_pwent(struct passwd *result, + struct winbindd_response *response, + char **buffer, int *buflen, int *errnop) +{ + struct winbindd_pw *pw = &response->data.pw; + + /* User name */ + + if ((result->pw_name = + get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_name, pw->pw_name); + + /* Password */ + + if ((result->pw_passwd = + get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_passwd, pw->pw_passwd); + + /* [ug]id */ + + result->pw_uid = pw->pw_uid; + result->pw_gid = pw->pw_gid; + + /* GECOS */ + + if ((result->pw_gecos = + get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_gecos, pw->pw_gecos); + + /* Home directory */ + + if ((result->pw_dir = + get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_dir, pw->pw_dir); + + /* Logon shell */ + + if ((result->pw_shell = + get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->pw_shell, pw->pw_shell); + + return NSS_STATUS_SUCCESS; +} + +/* Fill a grent structure from a winbindd_response structure. We use + the static data passed to us by libc to put strings and stuff in. + Return errno = ERANGE and NSS_STATUS_TRYAGAIN if we run out of + memory. */ + +static int fill_grent(struct group *result, + struct winbindd_response *response, + char **buffer, int *buflen, int *errnop) +{ + struct winbindd_gr *gr = &response->data.gr; + fstring name; + int i; + + /* Group name */ + + if ((result->gr_name = + get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->gr_name, gr->gr_name); + + /* Password */ + + if ((result->gr_passwd = + get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy(result->gr_passwd, gr->gr_passwd); + + /* gid */ + + result->gr_gid = gr->gr_gid; + + /* Group membership */ + + if ((gr->num_gr_mem < 0) || !response->extra_data) { + gr->num_gr_mem = 0; + } + + if ((result->gr_mem = + (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * + sizeof(char *))) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + if (gr->num_gr_mem == 0) { + + /* Group is empty */ + + *(result->gr_mem) = NULL; + return NSS_STATUS_SUCCESS; + } + + /* Start looking at extra data */ + + i = 0; + + while(next_token(&response->extra_data, name, ",", sizeof(fstring))) { + + /* Allocate space for member */ + + if (((result->gr_mem)[i] = + get_static(buffer, buflen, strlen(name) + 1)) == NULL) { + + /* Out of memory */ + + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + strcpy((result->gr_mem)[i], name); + i++; + } + + /* Terminate list */ + + (result->gr_mem)[i] = NULL; + + return NSS_STATUS_SUCCESS; +} + +/* + * NSS user functions + */ + +/* Rewind "file pointer" to start of ntdom password database */ + +enum nss_status +_nss_ntdom_setpwent(void) +{ + return generic_request(WINBINDD_SETPWENT, NULL, NULL); +} + +/* Close ntdom password database "file pointer" */ + +enum nss_status +_nss_ntdom_endpwent(void) +{ + return generic_request(WINBINDD_ENDPWENT, NULL, NULL); +} + +/* Fetch the next password entry from ntdom password database */ + +enum nss_status +_nss_ntdom_getpwent_r(struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + + ret = generic_request(WINBINDD_GETPWENT, NULL, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_pwent(result, &response, &buffer, &buflen, errnop); +} + +/* Return passwd struct from uid */ + +enum nss_status +_nss_ntdom_getpwuid_r(uid_t uid, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + struct winbindd_request request; + + request.data.uid = uid; + + ret = generic_request(WINBINDD_GETPWNAM_FROM_UID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_pwent(result, &response, &buffer, &buflen, errnop); +} + +/* Return passwd struct from username */ + +enum nss_status +_nss_ntdom_getpwnam_r(const char *name, struct passwd *result, char *buffer, + size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + struct winbindd_request request; + + strncpy(request.data.username, name, sizeof(request.data.username) - 1); + request.data.username[sizeof(request.data.username) - 1] = '\0'; + + ret = generic_request(WINBINDD_GETPWNAM_FROM_USER, &request, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_pwent(result, &response, &buffer, &buflen, errnop); +} + +/* + * NSS group functions + */ + +/* Rewind "file pointer" to start of ntdom group database */ + +enum nss_status +_nss_ntdom_setgrent(void) +{ + return generic_request(WINBINDD_SETGRENT, NULL, NULL); +} + +/* Close "file pointer" for ntdom group database */ + +enum nss_status +_nss_ntdom_endgrent(void) +{ + return generic_request(WINBINDD_ENDGRENT, NULL, NULL); +} + + + +/* Get next entry from ntdom group database */ + +enum nss_status +_nss_ntdom_getgrent_r(struct group *result, + char *buffer, size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + + ret = generic_request(WINBINDD_GETGRENT, NULL, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_grent(result, &response, &buffer, &buflen, errnop); +} + +/* Return group struct from group name */ + +enum nss_status +_nss_ntdom_getgrnam_r(const char *name, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + struct winbindd_request request; + + strncpy(request.data.groupname, name, sizeof(request.data.groupname)); + request.data.groupname[sizeof(request.data.groupname) - 1] = '\0'; + + ret = generic_request(WINBINDD_GETGRNAM_FROM_GROUP, &request, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_grent(result, &response, &buffer, &buflen, errnop); +} + +/* Return group struct from gid */ + +enum nss_status +_nss_ntdom_getgrgid_r(gid_t gid, + struct group *result, char *buffer, + size_t buflen, int *errnop) +{ + enum nss_status ret; + struct winbindd_response response; + struct winbindd_request request; + + request.data.gid = gid; + + ret = generic_request(WINBINDD_GETGRNAM_FROM_GID, &request, &response); + if (ret != NSS_STATUS_SUCCESS) return ret; + + return fill_grent(result, &response, &buffer, &buflen, errnop); +} diff --git a/source3/nsswitch/winbind_nss_config.h b/source3/nsswitch/winbind_nss_config.h new file mode 100644 index 0000000000..0aac5a786a --- /dev/null +++ b/source3/nsswitch/winbind_nss_config.h @@ -0,0 +1,111 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _NTDOM_CONFIG_H +#define _NTDOM_CONFIG_H + +/* Include header files from data in config.h file */ + +#include <config.h> + +#include <stdio.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_GRP_H +#include <grp.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <pwd.h> +#include <nss.h> + +/* I'm trying really hard not to include anything from smb.h with the + result of some silly looking redeclaration of structures. */ + +#ifndef _PSTRING +#define _PSTRING +#define PSTRING_LEN 1024 +#define FSTRING_LEN 128 +typedef char pstring[PSTRING_LEN]; +typedef char fstring[FSTRING_LEN]; +#endif + +#ifndef _BOOL +#define _BOOL /* So we don't typedef BOOL again in vfs.h */ +#define False (0) +#define True (1) +#define Auto (2) +typedef int BOOL; +#endif + +#if !defined(uint32) +#if (SIZEOF_INT == 4) +#define uint32 unsigned int +#elif (SIZEOF_LONG == 4) +#define uint32 unsigned long +#elif (SIZEOF_SHORT == 4) +#define uint32 unsigned short +#endif +#endif + +#if !defined(uint16) +#if (SIZEOF_SHORT == 4) +#define uint16 __ERROR___CANNOT_DETERMINE_TYPE_FOR_INT16; +#else /* SIZEOF_SHORT != 4 */ +#define uint16 unsigned short +#endif /* SIZEOF_SHORT != 4 */ +#endif + +#ifndef uint8 +#define uint8 unsigned char +#endif + +/* zero a structure */ +#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) + +/* zero a structure given a pointer to the structure */ +#define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } + +#endif diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c new file mode 100644 index 0000000000..e05166e716 --- /dev/null +++ b/source3/nsswitch/winbindd.c @@ -0,0 +1,663 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +/* List of all connected clients */ + +static struct winbindd_cli_state *client_list; + +/* Reload configuration */ + +static BOOL reload_services_file(void) +{ + pstring servicesf = CONFIGFILE; + BOOL ret; + + reopen_logs(); + ret = lp_load(servicesf,False,False,True); + + reopen_logs(); + load_interfaces(); + + return(ret); +} + +/* Print client information */ + +static void do_print_client_info(void) +{ + struct winbindd_cli_state *client; + int i; + + if (client_list == NULL) { + DEBUG(0, ("no clients in list\n")); + return; + } + + DEBUG(0, ("client list is:\n")); + + for (client = client_list, i = 0; client; client = client->next) { + DEBUG(0, ("client %3d: pid = %5d fd = %d read = %4d write = %4d\n", + i, client->pid, client->sock, client->read_buf_len, + client->write_buf_len)); + i++; + } +} + +/* Flush client cache */ + +static void do_flush_caches(void) +{ + /* Clear cached user and group enumation info */ + + winbindd_flush_cache(); +} + +/* Handle the signal by unlinking socket and exiting */ + +static void termination_handler(int signum) +{ + pstring path; + + /* Remove socket file */ + + slprintf(path, sizeof(path), "%s/%s", + WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME); + unlink(path); + + exit(0); +} + +static BOOL print_client_info; + +static void sigusr1_handler(int signum) +{ + BlockSignals(True, SIGUSR1); + print_client_info = True; + BlockSignals(False, SIGUSR1); +} + +static BOOL flush_cache; + +static void sighup_handler(int signum) +{ + BlockSignals(True, SIGHUP); + flush_cache = True; + BlockSignals(False, SIGHUP); +} + +/* Create winbindd socket */ + +static int create_sock(void) +{ + struct sockaddr_un sunaddr; + struct stat st; + int sock; + mode_t old_umask; + pstring path; + + /* Create the socket directory or reuse the existing one */ + + if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) { + + if (errno == ENOENT) { + + /* Create directory */ + + if (mkdir(WINBINDD_SOCKET_DIR, 0755) == -1) { + DEBUG(0, ("error creating socket directory %s: %s\n", + WINBINDD_SOCKET_DIR, sys_errlist[errno])); + return -1; + } + + } else { + + DEBUG(0, ("lstat failed on socket directory %s: %s\n", + WINBINDD_SOCKET_DIR, sys_errlist[errno])); + return -1; + } + + } else { + + /* Check ownership and permission on existing directory */ + + if (!S_ISDIR(st.st_mode)) { + DEBUG(0, ("socket directory %s isn't a directory\n", + WINBINDD_SOCKET_DIR)); + return -1; + } + + if ((st.st_uid != 0) || ((st.st_mode & 0777) != 0755)) { + DEBUG(0, ("invalid permissions on socket directory %s\n", + WINBINDD_SOCKET_DIR)); + return -1; + } + } + + /* Create the socket file */ + + old_umask = umask(0); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sock == -1) { + perror("socket"); + return -1; + } + + slprintf(path, sizeof(path), "%s/%s", + WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME); + + unlink(path); + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1); + + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + DEBUG(0, ("bind failed on winbind socket %s: %s\n", + path, + sys_errlist[errno])); + close(sock); + return -1; + } + + if (listen(sock, 5) == -1) { + DEBUG(0, ("listen failed on winbind socket %s: %s\n", + path, + sys_errlist[errno])); + close(sock); + return -1; + } + + umask(old_umask); + + /* Success! */ + + return sock; +} + +static void process_request(struct winbindd_cli_state *state) +{ + /* Process command */ + + state->response.result = WINBINDD_ERROR; + state->response.length = sizeof(struct winbindd_response); + + DEBUG(3,("processing command %s from pid %d\n", + winbindd_cmd_to_string(state->request.cmd), state->pid)); + + if (!server_state.lsa_handle_open) return; + + switch(state->request.cmd) { + + /* User functions */ + + case WINBINDD_GETPWNAM_FROM_USER: + state->response.result = winbindd_getpwnam_from_user(state); + break; + + case WINBINDD_GETPWNAM_FROM_UID: + state->response.result = winbindd_getpwnam_from_uid(state); + break; + + case WINBINDD_SETPWENT: + state->response.result = winbindd_setpwent(state); + break; + + case WINBINDD_ENDPWENT: + state->response.result = winbindd_endpwent(state); + break; + + case WINBINDD_GETPWENT: + state->response.result = winbindd_getpwent(state); + break; + + /* Group functions */ + + case WINBINDD_GETGRNAM_FROM_GROUP: + state->response.result = winbindd_getgrnam_from_group(state); + break; + + case WINBINDD_GETGRNAM_FROM_GID: + state->response.result = winbindd_getgrnam_from_gid(state); + break; + + case WINBINDD_SETGRENT: + state->response.result = winbindd_setgrent(state); + break; + + case WINBINDD_ENDGRENT: + state->response.result = winbindd_endgrent(state); + break; + + case WINBINDD_GETGRENT: + state->response.result = winbindd_getgrent(state); + break; + + /* pam auth functions */ + case WINBINDD_PAM_AUTH: + state->response.result = winbindd_pam_auth(state); + break; + + /* Oops */ + + default: + DEBUG(0, ("oops - unknown winbindd command %d\n", state->request.cmd)); + break; + } +} + +/* Process a new connection by adding it to the client connection list */ + +static void new_connection(int accept_sock) +{ + struct sockaddr_un sunaddr; + struct winbindd_cli_state *state; + int len, sock; + + /* Accept connection */ + + len = sizeof(sunaddr); + if ((sock = accept(accept_sock, (struct sockaddr *)&sunaddr, &len)) + == -1) { + + return; + } + + DEBUG(6,("accepted socket %d\n", sock)); + + /* Create new connection structure */ + + if ((state = (struct winbindd_cli_state *) + malloc(sizeof(*state))) == NULL) { + + return; + } + + ZERO_STRUCTP(state); + state->sock = sock; + + /* Add to connection list */ + + DLIST_ADD(client_list, state); +} + +/* Remove a client connection from client connection list */ + +static void remove_client(struct winbindd_cli_state *state) +{ + /* It's a dead client - hold a funeral */ + + if (state != NULL) { + + /* Close socket */ + + close(state->sock); + + /* Free any getent state */ + + free_getent_state(state->getpwent_state); + free_getent_state(state->getgrent_state); + + /* Free any extra data */ + + safe_free(state->response.extra_data); + + /* Remove from list and free */ + + DLIST_REMOVE(client_list, state); + free(state); + } +} + +/* Process a complete received packet from a client */ + +static void process_packet(struct winbindd_cli_state *state) +{ + /* Process request */ + + state->pid = state->request.pid; + + process_request(state); + + /* Update client state */ + + state->read_buf_len = 0; + state->write_buf_len = sizeof(state->response); +} + +/* Read some data from a client connection */ + +static void client_read(struct winbindd_cli_state *state) +{ + int n; + + /* Read data */ + + n = read(state->sock, state->read_buf_len + (char *)&state->request, + sizeof(state->request) - state->read_buf_len); + + /* Read failed, kill client */ + + if (n == -1 || n == 0) { + DEBUG(5,("read failed on sock %d, pid %d: %s\n", + state->sock, state->pid, + (n == -1) ? sys_errlist[errno] : "EOF")); + + state->finished = True; + return; + } + + /* Update client state */ + + state->read_buf_len += n; +} + +/* Write some data to a client connection */ + +static void client_write(struct winbindd_cli_state *state) +{ + char *data; + int n; + + /* Write data */ + + if (state->write_extra_data) { + + /* Write extra data */ + + data = (char *)state->response.extra_data + + state->response.length - sizeof(struct winbindd_response) - + state->write_buf_len; + + } else { + + /* Write response structure */ + + data = (char *)&state->response + sizeof(state->response) - + state->write_buf_len; + } + + n = write(state->sock, data, state->write_buf_len); + + /* Write failed, kill cilent */ + + if (n == -1 || n == 0) { + + DEBUG(3,("write failed on sock %d, pid %d: %s\n", + state->sock, state->pid, + (n == -1) ? sys_errlist[errno] : "EOF")); + + state->finished = True; + return; + } + + /* Update client state */ + + state->write_buf_len -= n; + + /* Have we written all data? */ + + if (state->write_buf_len == 0) { + + /* Take care of extra data */ + + if (state->response.length > sizeof(struct winbindd_response)) { + + if (state->write_extra_data) { + + /* Already written extra data - free it */ + + safe_free(state->response.extra_data); + state->response.extra_data = NULL; + state->write_extra_data = False; + + } else { + + /* Start writing extra data */ + + state->write_buf_len = state->response.length - + sizeof(struct winbindd_response); + state->write_extra_data = True; + } + } + } +} + +/* Process incoming clients on accept_sock. We use a tricky non-blocking, + non-forking, non-threaded model which allows us to handle many + simultaneous connections while remaining impervious to many denial of + service attacks. */ + +static void process_loop(int accept_sock) +{ + /* We'll be doing this a lot */ + + while (1) { + struct winbindd_cli_state *state; + fd_set r_fds, w_fds; + int maxfd = accept_sock, selret; + struct timeval timeout; + + /* do any connection establishment that is needed */ + establish_connections(); + + /* Initialise fd lists for select() */ + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + FD_SET(accept_sock, &r_fds); + + timeout.tv_sec = WINBINDD_ESTABLISH_LOOP; + timeout.tv_usec = 0; + + /* Set up client readers and writers */ + + state = client_list; + + while (state) { + /* Dispose of client connection if it is marked as finished */ + + if (state->finished) { + struct winbindd_cli_state *next = state->next; + + remove_client(state); + state = next; + continue; + } + + /* Select requires we know the highest fd used */ + + if (state->sock > maxfd) maxfd = state->sock; + + /* Add fd for reading */ + + if (state->read_buf_len != sizeof(state->request)) { + FD_SET(state->sock, &r_fds); + } + + /* Add fd for writing */ + + if (state->write_buf_len) { + FD_SET(state->sock, &w_fds); + } + + state = state->next; + } + + /* Check signal handling things */ + + if (flush_cache) { + do_flush_caches(); + reload_services_file(); + flush_cache = False; + } + + if (print_client_info) { + do_print_client_info(); + print_client_info = False; + } + + /* Call select */ + + selret = select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout); + + if (selret == 0) continue; + + if ((selret == -1 && errno != EINTR) || selret == 0) { + + /* Select error, something is badly wrong */ + + perror("select"); + exit(1); + } + + /* Create a new connection if accept_sock readable */ + + if (selret > 0) { + + if (FD_ISSET(accept_sock, &r_fds)) { + new_connection(accept_sock); + } + + /* Process activity on client connections */ + + for (state = client_list; state ; state = state->next) { + + /* Data available for reading */ + + if (FD_ISSET(state->sock, &r_fds)) { + + /* Read data */ + + client_read(state); + + /* A request packet might be complete */ + + if (state->read_buf_len == sizeof(state->request)) { + process_packet(state); + } + } + + /* Data available for writing */ + + if (FD_ISSET(state->sock, &w_fds)) { + client_write(state); + } + } + } + } +} + +/* Main function */ + +struct winbindd_state server_state; /* Server state information */ + +int main(int argc, char **argv) +{ + extern pstring global_myname; + extern pstring debugf; + int accept_sock; + BOOL interactive = False; + int opt; + + while ((opt = getopt(argc, argv, "i")) != EOF) { + switch (opt) { + case 'i': + interactive = True; + break; + default: + printf("Unknown option %c (%d)\n", (char)opt, opt); + exit(1); + } + } + + /* Initialise samba/rpc client stuff */ + slprintf(debugf, sizeof(debugf), "%s/log.winbindd", LOGFILEBASE); + setup_logging("winbindd", interactive); + reopen_logs(); + + if (!*global_myname) { + char *p; + + fstrcpy(global_myname, myhostname()); + p = strchr(global_myname, '.'); + if (p) { + *p = 0; + } + } + + TimeInit(); + charset_initialise(); + codepage_initialise(lp_client_code_page()); + + if (!lp_load(CONFIGFILE, True, False, False)) { + DEBUG(0, ("error opening config file\n")); + exit(1); + } + + if (!interactive) { + become_daemon(); + } + load_interfaces(); + + secrets_init(); + + ZERO_STRUCT(server_state); + + /* Winbind daemon initialisation */ + if (!winbindd_param_init()) { + return 1; + } + + if (!winbindd_idmap_init()) { + return 1; + } + + winbindd_cache_init(); + + /* Setup signal handlers */ + + CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */ + CatchSignal(SIGQUIT, termination_handler); + CatchSignal(SIGTERM, termination_handler); + + CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */ + + CatchSignal(SIGUSR1, sigusr1_handler); /* Debugging sigs */ + CatchSignal(SIGHUP, sighup_handler); + + /* Create UNIX domain socket */ + + if ((accept_sock = create_sock()) == -1) { + DEBUG(0, ("failed to create socket\n")); + return 1; + } + + /* Loop waiting for requests */ + + process_loop(accept_sock); + + return 0; +} diff --git a/source3/nsswitch/winbindd.h b/source3/nsswitch/winbindd.h new file mode 100644 index 0000000000..706a1fcc70 --- /dev/null +++ b/source3/nsswitch/winbindd.h @@ -0,0 +1,106 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _WINBINDD_H +#define _WINBINDD_H + +#include "includes.h" +#include "nterr.h" + +#include "winbindd_ntdom.h" + +/* Naughty global stuff */ + +extern int DEBUGLEVEL; + +/* Client state structure */ + +struct winbindd_cli_state { + struct winbindd_cli_state *prev, *next; /* Linked list pointers */ + int sock; /* Open socket from client */ + pid_t pid; /* pid of client */ + int read_buf_len, write_buf_len; /* Indexes in request/response */ + BOOL finished; /* Can delete from list */ + BOOL write_extra_data; /* Write extra_data field */ + struct winbindd_request request; /* Request from client */ + struct winbindd_response response; /* Respose to client */ + struct getent_state *getpwent_state; /* State for getpwent() */ + struct getent_state *getgrent_state; /* State for getgrent() */ +}; + +struct getent_state { + struct getent_state *prev, *next; + struct acct_info *sam_entries; + uint32 sam_entry_index, num_sam_entries; + struct winbindd_domain *domain; + BOOL got_sam_entries; +}; + +/* Server state structure */ + +struct winbindd_state { + /* Netbios name of PDC */ + fstring controller; + + /* User and group id pool */ + uid_t uid_low, uid_high; /* Range of uids to allocate */ + gid_t gid_low, gid_high; /* Range of gids to allocate */ + + /* Cached handle to lsa pipe */ + POLICY_HND lsa_handle; + BOOL lsa_handle_open; + BOOL pwdb_initialised; +}; + +extern struct winbindd_state server_state; /* Server information */ + +/* Structures to hold per domain information */ + +struct winbindd_domain { + + /* Domain information */ + + fstring name; /* Domain name */ + fstring controller; /* NetBIOS name of DC */ + + DOM_SID sid; /* SID for this domain */ + BOOL got_domain_info; /* Got controller and sid */ + + /* Cached handles to samr pipe */ + POLICY_HND sam_handle, sam_dom_handle; + BOOL sam_handle_open, sam_dom_handle_open; + + struct winbindd_domain *prev, *next; /* Linked list info */ +}; + +extern struct winbindd_domain *domain_list; /* List of domains we know */ + +#include "winbindd_proto.h" + +#include "rpc_parse.h" + +#define WINBINDD_ESTABLISH_LOOP 30 +#define DOM_SEQUENCE_NONE ((uint32)-1) + +#endif /* _WINBINDD_H */ diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c new file mode 100644 index 0000000000..adfcadf099 --- /dev/null +++ b/source3/nsswitch/winbindd_cache.c @@ -0,0 +1,416 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon - caching related functions + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +#define CACHE_TYPE_USER "USR" +#define CACHE_TYPE_GROUP "GRP" + +/* Initialise caching system */ + +static TDB_CONTEXT *cache_tdb; + +struct cache_rec { + uint32 seq_num; + time_t mod_time; +}; + +void winbindd_cache_init(void) +{ + /* Open tdb cache */ + unlink(lock_path("winbindd_cache.tdb")); + if (!(cache_tdb = tdb_open(lock_path("winbindd_cache.tdb"), 0, + TDB_NOLOCK, + O_RDWR | O_CREAT, 0600))) { + DEBUG(0, ("Unable to open tdb cache - user and group caching " + "disabled\n")); + } +} + +/* get the domain sequence number, possibly re-fetching */ +static uint32 cached_sequence_number(char *domain_name) +{ + fstring keystr; + TDB_DATA dbuf; + struct cache_rec rec; + time_t t = time(NULL); + + slprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name); + dbuf = tdb_fetch_by_string(cache_tdb, keystr); + if (!dbuf.dptr || dbuf.dsize != sizeof(rec)) { + goto refetch; + } + memcpy(&rec, dbuf.dptr, sizeof(rec)); + free(dbuf.dptr); + + if (t < (rec.mod_time + lp_winbind_cache_time())) { + DEBUG(4,("cached sequence number for %s is %u\n", + domain_name, (unsigned)rec.seq_num)); + return rec.seq_num; + } + + refetch: + rec.seq_num = domain_sequence_number(domain_name); + rec.mod_time = t; + tdb_store_by_string(cache_tdb, keystr, &rec, sizeof(rec)); + + return rec.seq_num; +} + +/* Check whether a seq_num for a cached item has expired */ +static BOOL cache_domain_expired(char *domain_name, uint32 seq_num) +{ + if (cached_sequence_number(domain_name) != seq_num) { + DEBUG(4,("seq %u for %s has expired\n", (unsigned)seq_num, domain_name)); + return True; + } + return False; +} + +static void set_cache_sequence_number(char *domain_name, char *cache_type, char *subkey) +{ + fstring keystr; + slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s", + domain_name, cache_type, subkey?subkey:""); + tdb_store_int(cache_tdb, keystr, cached_sequence_number(domain_name)); +} + +static uint32 get_cache_sequence_number(char *domain_name, char *cache_type, char *subkey) +{ + fstring keystr; + uint32 seq_num; + slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s", + domain_name, cache_type, subkey?subkey:""); + seq_num = (uint32)tdb_get_int(cache_tdb, keystr); + DEBUG(4,("%s is %u\n", keystr, (unsigned)seq_num)); + return seq_num; +} + +/* Fill the user or group cache with supplied data */ +static void fill_cache(char *domain_name, char *cache_type, + struct acct_info *sam_entries, + int num_sam_entries) +{ + fstring keystr; + + if (lp_winbind_cache_time() == 0) return; + + /* Error check */ + if (!sam_entries || (num_sam_entries == 0)) return; + + DEBUG(4, ("filling %s cache for domain %s with %d entries\n", + cache_type, domain_name, num_sam_entries)); + + /* Store data as a mega-huge chunk in the tdb */ + slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, + domain_name); + + tdb_store_by_string(cache_tdb, keystr, + sam_entries, sizeof(struct acct_info) * num_sam_entries); + + /* Stamp cache with current seq number */ + set_cache_sequence_number(domain_name, cache_type, NULL); +} + +/* Fill the user cache with supplied data */ + +void winbindd_fill_user_cache(char *domain_name, + struct acct_info *sam_entries, + int num_sam_entries) +{ + fill_cache(domain_name, CACHE_TYPE_USER, sam_entries, num_sam_entries); +} + +/* Fill the group cache with supplied data */ + +void winbindd_fill_group_cache(char *domain_name, + struct acct_info *sam_entries, + int num_sam_entries) +{ + fill_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, num_sam_entries); +} + +static void fill_cache_entry(char *domain, char *name, void *buf, int len) +{ + fstring keystr; + + /* Create key for store */ + slprintf(keystr, sizeof(keystr), "%s/%s", domain, name); + + DEBUG(4, ("filling cache entry %s\n", keystr)); + + /* Store it */ + tdb_store_by_string(cache_tdb, keystr, buf, len); +} + +/* Fill a user info cache entry */ +void winbindd_fill_user_cache_entry(char *domain, char *user_name, + struct winbindd_pw *pw) +{ + if (lp_winbind_cache_time() == 0) return; + + fill_cache_entry(domain, user_name, pw, sizeof(struct winbindd_pw)); + set_cache_sequence_number(domain, CACHE_TYPE_USER, user_name); +} + +/* Fill a user uid cache entry */ +void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, + struct winbindd_pw *pw) +{ + fstring uidstr; + + if (lp_winbind_cache_time() == 0) return; + + slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + fill_cache_entry(domain, uidstr, pw, sizeof(struct winbindd_pw)); + set_cache_sequence_number(domain, CACHE_TYPE_USER, uidstr); +} + +/* Fill a group info cache entry */ +void winbindd_fill_group_cache_entry(char *domain, char *group_name, + struct winbindd_gr *gr, void *extra_data, + int extra_data_len) +{ + fstring keystr; + + if (lp_winbind_cache_time() == 0) return; + + /* Fill group data */ + fill_cache_entry(domain, group_name, gr, sizeof(struct winbindd_gr)); + + /* Fill extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, group_name); + tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len); + + set_cache_sequence_number(domain, CACHE_TYPE_GROUP, group_name); +} + +/* Fill a group info cache entry */ +void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, + struct winbindd_gr *gr, void *extra_data, + int extra_data_len) +{ + fstring keystr; + fstring gidstr; + + slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); + + if (lp_winbind_cache_time() == 0) return; + + /* Fill group data */ + fill_cache_entry(domain, gidstr, gr, sizeof(struct winbindd_gr)); + + /* Fill extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, gidstr); + tdb_store_by_string(cache_tdb, keystr, extra_data, extra_data_len); + + set_cache_sequence_number(domain, CACHE_TYPE_GROUP, gidstr); +} + +/* Fetch some cached user or group data */ +static BOOL fetch_cache(char *domain_name, char *cache_type, + struct acct_info **sam_entries, int *num_sam_entries) +{ + TDB_DATA data; + fstring keystr; + + if (lp_winbind_cache_time() == 0) return False; + + /* Parameter check */ + if (!sam_entries || !num_sam_entries) { + return False; + } + + /* Check cache data is current */ + if (cache_domain_expired(domain_name, + get_cache_sequence_number(domain_name, cache_type, NULL))) { + return False; + } + + /* Create key */ + slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, + domain_name); + + /* Fetch cache information */ + data = tdb_fetch_by_string(cache_tdb, keystr); + + if (!data.dptr) return False; + + /* Copy across cached data. We can save a memcpy() by directly + assigning the data.dptr to the sam_entries pointer. It will + be freed by the end{pw,gr}ent() function. */ + + *sam_entries = (struct acct_info *)data.dptr; + *num_sam_entries = data.dsize / sizeof(struct acct_info); + + DEBUG(4, ("fetched %d cached %s entries for domain %s\n", + *num_sam_entries, cache_type, domain_name)); + + return True; +} + +/* Return cached entries for a domain. Return false if there are no cached + entries, or the cached information has expired for the domain. */ + +BOOL winbindd_fetch_user_cache(char *domain_name, + struct acct_info **sam_entries, + int *num_entries) +{ + return fetch_cache(domain_name, CACHE_TYPE_USER, sam_entries, + num_entries); +} + +/* Return cached entries for a domain. Return false if there are no cached + entries, or the cached information has expired for the domain. */ + +BOOL winbindd_fetch_group_cache(char *domain_name, + struct acct_info **sam_entries, + int *num_entries) +{ + return fetch_cache(domain_name, CACHE_TYPE_GROUP, sam_entries, + num_entries); +} + +static BOOL fetch_cache_entry(char *domain, char *name, void *buf, int len) +{ + TDB_DATA data; + fstring keystr; + + /* Create key for lookup */ + slprintf(keystr, sizeof(keystr), "%s/%s", domain, name); + + /* Look up cache entry */ + data = tdb_fetch_by_string(cache_tdb, keystr); + if (!data.dptr) return False; + + DEBUG(4, ("returning cached entry for %s/%s\n", domain, name)); + + /* Copy found entry into buffer */ + memcpy((char *)buf, data.dptr, len < data.dsize ? len : data.dsize); + free(data.dptr); + return True; +} + +/* Fetch an individual user cache entry */ +BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, + struct winbindd_pw *pw) +{ + uint32 seq_num; + + if (lp_winbind_cache_time() == 0) return False; + + seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, user); + if (cache_domain_expired(domain_name, seq_num)) return False; + + return fetch_cache_entry(domain_name, user, pw, sizeof(struct winbindd_pw)); +} + +/* Fetch an individual uid cache entry */ +BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, + struct winbindd_pw *pw) +{ + fstring uidstr; + uint32 seq_num; + + if (lp_winbind_cache_time() == 0) return False; + + slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr); + if (cache_domain_expired(domain_name, seq_num)) return False; + + return fetch_cache_entry(domain_name, uidstr, pw, sizeof(struct winbindd_pw)); +} + +/* Fetch an individual group cache entry. This function differs from the + user cache code as we need to store the group membership data. */ + +BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, + struct winbindd_gr *gr, + void **extra_data, int *extra_data_len) +{ + TDB_DATA data; + fstring keystr; + uint32 seq_num; + + if (lp_winbind_cache_time() == 0) return False; + + seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, group); + if (cache_domain_expired(domain_name, seq_num)) return False; + + /* Fetch group data */ + if (!fetch_cache_entry(domain_name, group, gr, sizeof(struct winbindd_gr))) return False; + + /* Fetch extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, group); + data = tdb_fetch_by_string(cache_tdb, keystr); + + if (!data.dptr) return False; + + /* Extra data freed when data has been sent */ + if (extra_data) *extra_data = data.dptr; + if (extra_data_len) *extra_data_len = data.dsize; + + return True; +} + + +/* Fetch an individual gid cache entry. This function differs from the + user cache code as we need to store the group membership data. */ + +BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid, + struct winbindd_gr *gr, + void **extra_data, int *extra_data_len) +{ + TDB_DATA data; + fstring keystr; + fstring gidstr; + uint32 seq_num; + + slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); + + if (lp_winbind_cache_time() == 0) return False; + + seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_GROUP, gidstr); + if (cache_domain_expired(domain_name, seq_num)) return False; + + /* Fetch group data */ + if (!fetch_cache_entry(domain_name, gidstr, gr, sizeof(struct winbindd_gr))) return False; + + /* Fetch extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, gidstr); + data = tdb_fetch_by_string(cache_tdb, keystr); + if (!data.dptr) return False; + + /* Extra data freed when data has been sent */ + if (extra_data) *extra_data = data.dptr; + if (extra_data_len) *extra_data_len = data.dsize; + + return True; +} + +/* Flush cache data - easiest to just reopen the tdb */ +void winbindd_flush_cache(void) +{ + tdb_close(cache_tdb); + winbindd_cache_init(); +} diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c new file mode 100644 index 0000000000..db94bab836 --- /dev/null +++ b/source3/nsswitch/winbindd_group.c @@ -0,0 +1,763 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +/* Fill a grent structure from various other information */ + +static void winbindd_fill_grent(struct winbindd_gr *gr, char *gr_name, + gid_t unix_gid) +{ + /* Fill in uid/gid */ + + gr->gr_gid = unix_gid; + + /* Group name and password */ + + safe_strcpy(gr->gr_name, gr_name, sizeof(gr->gr_name) - 1); + safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); +} + +/* Fill in group membership */ + +struct grent_mem_group { + uint32 rid; + enum SID_NAME_USE name_type; + fstring domain_name; + struct winbindd_domain *domain; + struct grent_mem_group *prev, *next; +}; + +struct grent_mem_list { + fstring name; + struct grent_mem_list *prev, *next; +}; + +/* Name comparison function for qsort() */ + +static int name_comp(struct grent_mem_list *n1, struct grent_mem_list *n2) +{ + /* Silly cases */ + + if (!n1 && !n2) return 0; + if (!n1) return -1; + if (!n2) return 1; + + return strcmp(n1->name, n2->name); +} + +static struct grent_mem_list *sort_groupmem_list(struct grent_mem_list *list, + int num_gr_mem) +{ + struct grent_mem_list *groupmem_array, *temp; + int i; + + /* Allocate and zero an array to hold sorted entries */ + + if ((groupmem_array = malloc(num_gr_mem * + sizeof(struct grent_mem_list))) == NULL) { + return NULL; + } + + memset((char *)groupmem_array, 0, num_gr_mem * + sizeof(struct grent_mem_list)); + + /* Copy list to array */ + + for(temp = list, i = 0; temp && i < num_gr_mem; temp = temp->next, i++) { + fstrcpy(groupmem_array[i].name, temp->name); + } + + /* Sort array */ + + qsort(groupmem_array, num_gr_mem, sizeof(struct grent_mem_list), + name_comp); + + /* Fix up resulting array to a linked list and return it */ + + for(i = 0; i < num_gr_mem; i++) { + + /* Fix up previous link */ + + if (i != 0) { + groupmem_array[i].prev = &groupmem_array[i - 1]; + } + + /* Fix up next link */ + + if (i != (num_gr_mem - 1)) { + groupmem_array[i].next = &groupmem_array[i + 1]; + } + } + + return groupmem_array; +} + +static BOOL winbindd_fill_grent_mem(struct winbindd_domain *domain, + uint32 group_rid, + enum SID_NAME_USE group_name_type, + struct winbindd_response *response) +{ + struct grent_mem_group *done_groups = NULL, *todo_groups = NULL; + struct grent_mem_group *temp_group; + struct grent_mem_list *groupmem_list = NULL; + struct winbindd_gr *gr; + + if (response) { + gr = &response->data.gr; + } else { + return False; + } + + /* Initialise group membership information */ + + gr->num_gr_mem = 0; + + /* Add first group to todo_groups list */ + + if ((temp_group = + (struct grent_mem_group *)malloc(sizeof(*temp_group))) == NULL) { + return False; + } + + ZERO_STRUCTP(temp_group); + + temp_group->rid = group_rid; + temp_group->name_type = group_name_type; + temp_group->domain = domain; + fstrcpy(temp_group->domain_name, domain->name); + + DLIST_ADD(todo_groups, temp_group); + + /* Iterate over all groups to find members of */ + + while(todo_groups != NULL) { + struct grent_mem_group *current_group = todo_groups; + uint32 num_names = 0, *rid_mem = NULL; + enum SID_NAME_USE *name_types = NULL; + + DOM_SID **sids = NULL; + char **names = NULL; + BOOL done_group; + int i; + + /* Check we haven't looked up this group before */ + + done_group = 0; + + for (temp_group = done_groups; temp_group != NULL; + temp_group = temp_group->next) { + + if ((temp_group->rid == current_group->rid) && + (strcmp(temp_group->domain_name, + current_group->domain_name) == 0)) { + + done_group = 1; + } + } + + if (done_group) goto cleanup; + + /* Lookup group membership for the current group */ + + if (current_group->name_type == SID_NAME_DOM_GRP) { + + if (!winbindd_lookup_groupmem(current_group->domain, + current_group->rid, &num_names, + &rid_mem, &names, &name_types)) { + + DEBUG(1, ("fill_grent_mem(): could not lookup membership " + "for group rid %d in domain %s\n", + current_group->rid, + current_group->domain->name)); + + /* Exit if we cannot lookup the membership for the group + this function was called to look at */ + + if (current_group->rid == group_rid) { + return False; + } else { + goto cleanup; + } + } + } + + if (current_group->name_type == SID_NAME_ALIAS) { + + if (!winbindd_lookup_aliasmem(current_group->domain, + current_group->rid, &num_names, + &sids, &names, &name_types)) { + + DEBUG(1, ("fill_grent_mem(): group rid %d not a local group\n", + group_rid)); + + /* Exit if we cannot lookup the membership for the group + this function was called to look at */ + + if (current_group->rid == group_rid) { + return False; + } else { + goto cleanup; + } + } + } + + /* Now for each member of the group, add it to the group list if it + is a user, otherwise push it onto the todo_group list if it is a + group or an alias. */ + + for (i = 0; i < num_names; i++) { + enum SID_NAME_USE name_type; + fstring name_part1, name_part2; + char *name_dom, *name_user, *the_name; + struct winbindd_domain *name_domain; + + /* Lookup name */ + + ZERO_STRUCT(name_part1); + ZERO_STRUCT(name_part2); + + the_name = names[i]; + parse_domain_user(the_name, name_part1, name_part2); + + if (strcmp(name_part1, "") != 0) { + name_dom = name_part1; + name_user = name_part2; + + if ((name_domain = find_domain_from_name(name_dom)) == NULL) { + DEBUG(0, ("unable to look up domain record for domain " + "%s\n", name_dom)); + continue; + } + + } else { + name_dom = current_group->domain->name; + name_user = name_part2; + name_domain = current_group->domain; + } + + if (winbindd_lookup_sid_by_name(name_domain, name_user, NULL, + &name_type) == WINBINDD_OK) { + + /* Check name type */ + + if (name_type == SID_NAME_USER) { + struct grent_mem_list *entry; + + /* Add to group membership list */ + + if ((entry = (struct grent_mem_list *) + malloc(sizeof(*entry))) != NULL) { + + /* Create name */ + slprintf(entry->name, sizeof(entry->name), + "%s/%s", name_dom, name_user); + + /* Add to list */ + + DLIST_ADD(groupmem_list, entry); + gr->num_gr_mem++; + } + + } else { + struct grent_mem_group *todo_group; + DOM_SID todo_sid; + uint32 todo_rid; + + /* Add group to todo list */ + + if (winbindd_lookup_sid_by_name(name_domain, names[i], + &todo_sid, &name_type) + == WINBINDD_OK) { + + /* Fill in group entry */ + + sid_split_rid(&todo_sid, &todo_rid); + + if ((todo_group = (struct grent_mem_group *) + malloc(sizeof(*todo_group))) != NULL) { + + ZERO_STRUCTP(todo_group); + + todo_group->rid = todo_rid; + todo_group->name_type = name_type; + todo_group->domain = name_domain; + + fstrcpy(todo_group->domain_name, name_dom); + + DLIST_ADD(todo_groups, todo_group); + } + } + } + } + } + + cleanup: + + /* Remove group from todo list and add to done_groups list */ + + DLIST_REMOVE(todo_groups, current_group); + DLIST_ADD(done_groups, current_group); + + /* Free memory allocated in winbindd_lookup_{alias,group}mem() */ + + safe_free(name_types); + safe_free(rid_mem); + + free_char_array(num_names, names); + free_sid_array(num_names, sids); + } + + /* Free done groups list */ + + temp_group = done_groups; + + if (temp_group != NULL) { + while (temp_group != NULL) { + struct grent_mem_group *next; + + DLIST_REMOVE(done_groups, temp_group); + next = temp_group->next; + + free(temp_group); + temp_group = next; + } + } + + /* Remove duplicates from group member list. */ + + if (gr->num_gr_mem > 0) { + struct grent_mem_list *sorted_groupmem_list, *temp; + int extra_data_len = 0; + fstring prev_name; + char *head; + + /* Sort list */ + + sorted_groupmem_list = sort_groupmem_list(groupmem_list, + gr->num_gr_mem); + /* Remove duplicates by iteration */ + + fstrcpy(prev_name, ""); + + for(temp = sorted_groupmem_list; temp; temp = temp->next) { + if (strequal(temp->name, prev_name)) { + + /* Got a duplicate name - delete it. Don't panic as we're + only adjusting the prev and next pointers so memory + allocation is not messed up. */ + + DLIST_REMOVE(sorted_groupmem_list, temp); + gr->num_gr_mem--; + + } else { + + /* Got a unique name - count how long it is */ + + extra_data_len += strlen(temp->name) + 1; + } + } + + extra_data_len++; /* Don't forget null a terminator */ + + /* Convert sorted list into extra data field to send back to ntdom + client. Add one to extra_data_len for null termination */ + + if ((response->extra_data = malloc(extra_data_len))) { + + /* Initialise extra data */ + + memset(response->extra_data, 0, extra_data_len); + + head = response->extra_data; + + /* Fill in extra data */ + + for(temp = sorted_groupmem_list; temp; temp = temp->next) { + int len = strlen(temp->name) + 1; + + safe_strcpy(head, temp->name, len); + head[len - 1] = ','; + head += len; + } + + *head = '\0'; + + /* Update response length */ + + response->length = sizeof(struct winbindd_response) + + extra_data_len; + } + + /* Free memory for sorted_groupmem_list. It was allocated as an + array in sort_groupmem_list() so can be freed in one go. */ + + free(sorted_groupmem_list); + + /* Free groupmem_list */ + + temp = groupmem_list; + + while (temp != NULL) { + struct grent_mem_list *next; + + DLIST_REMOVE(groupmem_list, temp); + next = temp->next; + + free(temp); + temp = next; + } + } + + return True; +} + +/* Return a group structure from a group name */ + +enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state *state) +{ + DOM_SID group_sid; + struct winbindd_domain *domain; + enum SID_NAME_USE name_type; + uint32 group_rid; + fstring name_domain, name_group, name; + char *tmp; + gid_t gid; + int extra_data_len; + + /* Parse domain and groupname */ + + memset(name_group, 0, sizeof(fstring)); + + tmp = state->request.data.groupname; + parse_domain_user(tmp, name_domain, name_group); + + /* Reject names that don't have a domain - i.e name_domain contains the + entire name. */ + + if (strequal(name_group, "")) { + return WINBINDD_ERROR; + } + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("getgrname_from_group(): could not get domain sid for " + "domain %s\n", name_domain)); + return WINBINDD_ERROR; + } + + /* Check for cached user entry */ + + if (winbindd_fetch_group_cache_entry(name_domain, name_group, + &state->response.data.gr, + &state->response.extra_data, + &extra_data_len)) { + state->response.length += extra_data_len; + return WINBINDD_OK; + } + + slprintf(name, sizeof(name), "%s\\%s", name_domain, name_group); + + /* Get rid and name type from name */ + + if (!winbindd_lookup_sid_by_name(domain, name, &group_sid, + &name_type)) { + DEBUG(1, ("group %s in domain %s does not exist\n", name_group, + name_domain)); + return WINBINDD_ERROR; + } + + if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) { + DEBUG(1, ("from_group: name '%s' is not a local or domain group: %d\n", + name_group, name_type)); + return WINBINDD_ERROR; + } + + /* Fill in group structure */ + + sid_split_rid(&group_sid, &group_rid); + + if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) { + DEBUG(1, ("error sursing unix gid for sid\n")); + return WINBINDD_ERROR; + } + + winbindd_fill_grent(&state->response.data.gr, + state->request.data.groupname, gid); + + if (!winbindd_fill_grent_mem(domain, group_rid, name_type, + &state->response)) { + return WINBINDD_ERROR; + } + + /* Update cached group info */ + + winbindd_fill_group_cache_entry(name_domain, name_group, + &state->response.data.gr, + state->response.extra_data, + state->response.length - + sizeof(struct winbindd_response)); + + return WINBINDD_OK; +} + +/* Return a group structure from a gid number */ + +enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state + *state) +{ + struct winbindd_domain *domain; + DOM_SID group_sid; + enum SID_NAME_USE name_type; + fstring group_name; + uint32 group_rid; + int extra_data_len; + + /* Get rid from gid */ + if (!winbindd_idmap_get_rid_from_gid(state->request.data.gid, &group_rid, + &domain)) { + DEBUG(1, ("Could not convert gid %d to rid\n", + state->request.data.gid)); + return WINBINDD_ERROR; + } + + /* try a cached entry */ + if (winbindd_fetch_gid_cache_entry(domain->name, state->request.data.gid, + &state->response.data.gr, + &state->response.extra_data, + &extra_data_len)) { + state->response.length += extra_data_len; + return WINBINDD_OK; + } + + /* Get sid from gid */ + + sid_copy(&group_sid, &domain->sid); + sid_append_rid(&group_sid, group_rid); + + if (!winbindd_lookup_name_by_sid(domain, &group_sid, group_name, + &name_type)) { + DEBUG(1, ("Could not lookup sid\n")); + return WINBINDD_ERROR; + } + + string_sub(group_name, "\\", "/", sizeof(fstring)); + + if (!((name_type == SID_NAME_ALIAS) || (name_type == SID_NAME_DOM_GRP))) { + DEBUG(1, ("from_gid: name '%s' is not a local or domain group: %d\n", + group_name, name_type)); + return WINBINDD_ERROR; + } + + /* Fill in group structure */ + + winbindd_fill_grent(&state->response.data.gr, group_name, + state->request.data.gid); + + if (!winbindd_fill_grent_mem(domain, group_rid, name_type, + &state->response)) { + return WINBINDD_ERROR; + } + + /* Update cached group info */ + winbindd_fill_gid_cache_entry(domain->name, state->request.data.gid, + &state->response.data.gr, + state->response.extra_data, + state->response.length - + sizeof(struct winbindd_response)); + + return WINBINDD_OK; +} + +/* + * set/get/endgrent functions + */ + +/* "Rewind" file pointer for group database enumeration */ + +enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) +{ + struct winbindd_domain *tmp; + + if (state == NULL) return WINBINDD_ERROR; + + /* Free old static data if it exists */ + + if (state->getgrent_state != NULL) { + free_getent_state(state->getgrent_state); + state->getgrent_state = NULL; + } + + /* Create sam pipes for each domain we know about */ + + for (tmp = domain_list; tmp != NULL; tmp = tmp->next) { + struct getent_state *domain_state; + + /* Skip domains other than WINBINDD_DOMAIN environment variable */ + + if ((strcmp(state->request.domain, "") != 0) && + (strcmp(state->request.domain, tmp->name) != 0)) { + continue; + } + + /* Create a state record for this domain */ + + if ((domain_state = (struct getent_state *) + malloc(sizeof(struct getent_state))) == NULL) { + + return WINBINDD_ERROR; + } + + ZERO_STRUCTP(domain_state); + + /* Add to list of open domains */ + + domain_state->domain = tmp; + DLIST_ADD(state->getgrent_state, domain_state); + } + + return WINBINDD_OK; +} + +/* Close file pointer to ntdom group database */ + +enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) +{ + if (state == NULL) return WINBINDD_ERROR; + + free_getent_state(state->getgrent_state); + state->getgrent_state = NULL; + + return WINBINDD_OK; +} + +/* Fetch next group entry from netdom database */ + +enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) +{ + if (state == NULL) return WINBINDD_ERROR; + + /* Process the current head of the getent_state list */ + + while(state->getgrent_state != NULL) { + struct getent_state *ent = state->getgrent_state; + + /* Get list of entries if we haven't already got them */ + + if (!ent->got_sam_entries) { + uint32 status, start_ndx = 0, start_ndx2 = 0; + + if (!winbindd_fetch_group_cache(ent->domain->name, + &ent->sam_entries, + &ent->num_sam_entries)) { + + /* Fetch group entries */ + + if (!domain_handles_open(ent->domain)) goto cleanup; + + /* Enumerate domain groups */ + + do { + status = + samr_enum_dom_groups(&ent->domain->sam_dom_handle, + &start_ndx, 0x100000, + &ent->sam_entries, + &ent->num_sam_entries); + } while (status == STATUS_MORE_ENTRIES); + + /* Enumerate domain aliases */ + + do { + status = + samr_enum_dom_aliases(&ent->domain->sam_dom_handle, + &start_ndx2, 0x100000, + &ent->sam_entries, + &ent->num_sam_entries); + } while (status == STATUS_MORE_ENTRIES); + + /* Fill cache with received entries */ + + winbindd_fill_group_cache(ent->domain->name, ent->sam_entries, + ent->num_sam_entries); + } + + ent->got_sam_entries = True; + } + + /* Send back a group */ + + while (ent->sam_entry_index < ent->num_sam_entries) { + enum winbindd_result result; + fstring domain_group_name; + char *group_name = (ent->sam_entries) + [ent->sam_entry_index].acct_name; + + /* Prepend domain to name */ + + slprintf(domain_group_name, sizeof(domain_group_name), + "%s/%s", ent->domain->name, group_name); + + /* Get group entry from group name */ + + fstrcpy(state->request.data.groupname, domain_group_name); + result = winbindd_getgrnam_from_group(state); + + ent->sam_entry_index++; + + if (result == WINBINDD_OK) { + return result; + } + + /* Try next group */ + + DEBUG(1, ("could not getgrnam_from_group for group name %s\n", + domain_group_name)); + } + + /* We've exhausted all users for this pipe - close it down and + start on the next one. */ + + cleanup: + + /* Free mallocated memory for sam entries. The data stored here + may have been allocated from the cache. */ + + if (ent->sam_entries != NULL) free(ent->sam_entries); + ent->sam_entries = NULL; + + /* Free state information for this domain */ + + { + struct getent_state *old_ent; + + old_ent = state->getgrent_state; + DLIST_REMOVE(state->getgrent_state, state->getgrent_state); + free(old_ent); + } + } + + /* Out of pipes so we're done */ + + return WINBINDD_ERROR; +} diff --git a/source3/nsswitch/winbindd_idmap.c b/source3/nsswitch/winbindd_idmap.c new file mode 100644 index 0000000000..cd3fd2caab --- /dev/null +++ b/source3/nsswitch/winbindd_idmap.c @@ -0,0 +1,235 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon - user related function + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +/* High water mark keys */ + +#define HWM_GROUP "GROUP HWM" +#define HWM_USER "USER HWM" + +/* Globals */ + +static TDB_CONTEXT *idmap_tdb; + +/* Allocate either a user or group id from the pool */ + +static BOOL allocate_id(int *id, BOOL isgroup) +{ + int hwm; + + /* Get current high water mark */ + + if ((hwm = tdb_get_int(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER)) == -1) { + return False; + } + + /* Return next available uid in list */ + + if ((isgroup && (hwm > server_state.gid_high)) || + (!isgroup && (hwm > server_state.uid_high))) { + DEBUG(0, ("winbind %sid range full!\n", isgroup ? "g" : "u")); + return False; + } + + if (id) { + *id = hwm; + } + + hwm++; + + /* Store new high water mark */ + + tdb_store_int(idmap_tdb, isgroup ? HWM_GROUP : HWM_USER, hwm); + + return True; +} + +/* Get an id from a rid */ + +static BOOL get_id_from_rid(char *domain_name, uint32 rid, int *id, + BOOL isgroup) +{ + TDB_DATA data, key; + fstring keystr; + BOOL result; + + /* Check if rid is present in database */ + + slprintf(keystr, sizeof(keystr), "%s/%d", domain_name, rid); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + fstring scanstr; + int the_id; + + /* Parse and return existing uid */ + + fstrcpy(scanstr, isgroup ? "GID" : "UID"); + fstrcat(scanstr, " %d"); + + if (sscanf(data.dptr, scanstr, &the_id) == 1) { + + /* Store uid */ + + if (id) { + *id = the_id; + } + + result = True; + } + + free(data.dptr); + + } else { + + /* Allocate a new id for this rid */ + + if (id && allocate_id(id, isgroup)) { + fstring keystr2; + + /* Store new id */ + + slprintf(keystr2, sizeof(keystr2), "%s %d", isgroup ? "GID" : + "UID", *id); + + data.dptr = keystr2; + data.dsize = strlen(keystr2) + 1; + + tdb_store(idmap_tdb, key, data, TDB_REPLACE); + tdb_store(idmap_tdb, data, key, TDB_REPLACE); + + result = True; + } + } + + return result; +} + +/* Get a uid from a user rid */ + +BOOL winbindd_idmap_get_uid_from_rid(char *domain_name, uint32 user_rid, + uid_t *uid) +{ + return get_id_from_rid(domain_name, user_rid, uid, False); +} + +/* Get a gid from a group rid */ + +BOOL winbindd_idmap_get_gid_from_rid(char *domain_name, uint32 group_rid, + gid_t *gid) +{ + return get_id_from_rid(domain_name, group_rid, gid, True); +} + +BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain, + BOOL isgroup) +{ + TDB_DATA key, data; + fstring keystr; + BOOL result = False; + + slprintf(keystr, sizeof(keystr), "%s %d", isgroup ? "GID" : "UID", id); + + key.dptr = keystr; + key.dsize = strlen(keystr) + 1; + + data = tdb_fetch(idmap_tdb, key); + + if (data.dptr) { + char *p = data.dptr; + fstring domain_name; + uint32 the_rid; + + if (next_token(&p, domain_name, "/", sizeof(fstring))) { + + the_rid = atoi(p); + + if (rid) { + *rid = the_rid; + } + + if (domain) { + *domain = find_domain_from_name(domain_name); + } + + result = True; + } + + free(data.dptr); + } + + return result; +} + +/* Get a user rid from a uid */ + +BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid, + struct winbindd_domain **domain) +{ + return get_rid_from_id((int)uid, user_rid, domain, False); +} + +/* Get a group rid from a gid */ + +BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, + struct winbindd_domain **domain) +{ + return get_rid_from_id((int)gid, group_rid, domain, True); +} + +/* Initialise idmap database */ + +BOOL winbindd_idmap_init(void) +{ + /* Open tdb cache */ + + if (!(idmap_tdb = tdb_open(lock_path("winbindd_idmap.tdb"), 0, + TDB_NOLOCK | TDB_NOMMAP, + O_RDWR | O_CREAT, 0600))) { + DEBUG(0, ("Unable to open idmap database\n")); + return False; + } + + /* Create high water marks for group and user id */ + + if (tdb_get_int(idmap_tdb, HWM_USER) == -1) { + if (tdb_store_int(idmap_tdb, HWM_USER, server_state.uid_low) == -1) { + DEBUG(0, ("Unable to initialise user hwm in idmap database\n")); + return False; + } + } + + if (tdb_get_int(idmap_tdb, HWM_GROUP) == -1) { + if (tdb_store_int(idmap_tdb, HWM_GROUP, server_state.gid_low) == -1) { + DEBUG(0, ("Unable to initialise group hwm in idmap database\n")); + return False; + } + } + + return True; +} diff --git a/source3/nsswitch/winbindd_nss.h b/source3/nsswitch/winbindd_nss.h new file mode 100644 index 0000000000..0cf16e9666 --- /dev/null +++ b/source3/nsswitch/winbindd_nss.h @@ -0,0 +1,118 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef _WINBINDD_NTDOM_H +#define _WINBINDD_NTDOM_H + +#define WINBINDD_SOCKET_NAME "pipe" /* Name of PF_UNIX socket */ +#define WINBINDD_SOCKET_DIR "/tmp/.winbindd" /* Name of PF_UNIX dir */ +#define WINBINDD_DOMAIN_ENV "WINBINDD_DOMAIN" /* Environment variable */ + +/* Socket commands */ + +enum winbindd_cmd { + WINBINDD_GETPWNAM_FROM_USER, /* getpwnam stuff */ + WINBINDD_GETPWNAM_FROM_UID, + WINBINDD_GETGRNAM_FROM_GROUP, /* getgrnam stuff */ + WINBINDD_GETGRNAM_FROM_GID, + WINBINDD_SETPWENT, /* get/set/endpwent */ + WINBINDD_ENDPWENT, + WINBINDD_GETPWENT, + WINBINDD_SETGRENT, /* get/set/endgrent */ + WINBINDD_ENDGRENT, + WINBINDD_GETGRENT, + WINBINDD_PAM_AUTH +}; + +/* Winbind request structure */ + +struct winbindd_request { + enum winbindd_cmd cmd; /* Winbindd command to execute */ + pid_t pid; /* pid of calling process */ + + union { + fstring username; /* getpwnam() */ + fstring groupname; /* getgrnam() */ + uid_t uid; /* getpwuid() */ + gid_t gid; /* getgrgid() */ + struct { + /* the following is used by pam_winbind */ + fstring user; + fstring pass; + } auth; + } data; + fstring domain; /* {set,get,end}{pw,gr}ent() */ +}; + +/* Response values */ + +enum winbindd_result { + WINBINDD_ERROR, + WINBINDD_OK +}; + +/* Winbind response structure */ + +struct winbindd_response { + + /* Header information */ + + int length; /* Length of response */ + enum winbindd_result result; /* Result code */ + + /* Fixed length return data */ + + union { + + /* getpwnam, getpwuid, getpwent */ + + struct winbindd_pw { + fstring pw_name; + fstring pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + fstring pw_gecos; + fstring pw_dir; + fstring pw_shell; + int pwent_ndx; + } pw; + + /* getgrnam, getgrgid, getgrent */ + + struct winbindd_gr { + fstring gr_name; + fstring gr_passwd; + gid_t gr_gid; + int num_gr_mem; + int grent_ndx; + } gr; + + } data; + + /* Variable length return data */ + + void *extra_data; /* getgrnam, getgrgid, getgrent */ +}; + +#endif diff --git a/source3/nsswitch/winbindd_pam.c b/source3/nsswitch/winbindd_pam.c new file mode 100644 index 0000000000..011fbedb97 --- /dev/null +++ b/source3/nsswitch/winbindd_pam.c @@ -0,0 +1,97 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + + Winbind daemon - pam auuth funcions + + Copyright (C) Andrew Tridgell 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" +/************************************************************************ +form a key for fetching a domain trust password +************************************************************************/ +static char *trust_keystr(char *domain) +{ + static fstring keystr; + slprintf(keystr,sizeof(keystr),"%s/%s", SECRETS_MACHINE_ACCT_PASS, domain); + return keystr; +} + +/************************************************************************ + Routine to get the trust account password for a domain. + The user of this function must have locked the trust password file. +************************************************************************/ +static BOOL _get_trust_account_password(char *domain, unsigned char *ret_pwd, time_t *pass_last_set_time) +{ + struct machine_acct_pass *pass; + size_t size; + + if (!(pass = secrets_fetch(trust_keystr(domain), &size)) || + size != sizeof(*pass)) return False; + + if (pass_last_set_time) *pass_last_set_time = pass->mod_time; + memcpy(ret_pwd, pass->hash, 16); + free(pass); + return True; +} + + +/* Return a password structure from a username. Specify whether cached data + can be returned. */ + +enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) +{ + NET_USER_INFO_3 info3; + uchar ntpw[16]; + uchar lmpw[16]; + uchar trust_passwd[16]; + uint32 status; + fstring server; + fstring name_domain, name_user; + extern pstring global_myname; + + DEBUG(1,("winbindd_pam_auth user=%s\n", + state->request.data.auth.user)); + + /* Parse domain and username */ + parse_domain_user(state->request.data.auth.user, name_domain, name_user); + + /* don't allow the null domain */ + if (strcmp(name_domain,"") == 0) return WINBINDD_ERROR; + + ZERO_STRUCT(info3); + + if (!_get_trust_account_password(name_domain, trust_passwd, NULL)) return WINBINDD_ERROR; + + nt_lm_owf_gen(state->request.data.auth.pass, ntpw, lmpw); + + slprintf(server, sizeof(server), "\\\\%s", server_state.controller); + + status = domain_client_validate_backend(server, + name_user, name_domain, + global_myname, SEC_CHAN_WKSTA, + trust_passwd, + NULL, + lmpw, sizeof(lmpw), + ntpw, sizeof(ntpw), &info3); + + if (status != NT_STATUS_NOPROBLEMO) return WINBINDD_ERROR; + + return WINBINDD_OK; +} + diff --git a/source3/nsswitch/winbindd_proto.h b/source3/nsswitch/winbindd_proto.h new file mode 100644 index 0000000000..774478fea5 --- /dev/null +++ b/source3/nsswitch/winbindd_proto.h @@ -0,0 +1,113 @@ +#ifndef _WINBINDD_PROTO_H_ +#define _WINBINDD_PROTO_H_ +/* This file is automatically generated with "make proto". DO NOT EDIT */ + + +/*The following definitions come from nsswitch/winbindd.c */ + +int main(int argc, char **argv); + +/*The following definitions come from nsswitch/winbindd_cache.c */ + +void winbindd_cache_init(void); +void winbindd_fill_user_cache(char *domain_name, + struct acct_info *sam_entries, + int num_sam_entries); +void winbindd_fill_group_cache(char *domain_name, + struct acct_info *sam_entries, + int num_sam_entries); +void winbindd_fill_user_cache_entry(char *domain, char *user_name, + struct winbindd_pw *pw); +void winbindd_fill_uid_cache_entry(char *domain, uid_t uid, + struct winbindd_pw *pw); +void winbindd_fill_group_cache_entry(char *domain, char *group_name, + struct winbindd_gr *gr, void *extra_data, + int extra_data_len); +void winbindd_fill_gid_cache_entry(char *domain, gid_t gid, + struct winbindd_gr *gr, void *extra_data, + int extra_data_len); +BOOL winbindd_fetch_user_cache(char *domain_name, + struct acct_info **sam_entries, + int *num_entries); +BOOL winbindd_fetch_group_cache(char *domain_name, + struct acct_info **sam_entries, + int *num_entries); +BOOL winbindd_fetch_user_cache_entry(char *domain_name, char *user, + struct winbindd_pw *pw); +BOOL winbindd_fetch_uid_cache_entry(char *domain_name, uid_t uid, + struct winbindd_pw *pw); +BOOL winbindd_fetch_group_cache_entry(char *domain_name, char *group, + struct winbindd_gr *gr, + void **extra_data, int *extra_data_len); +BOOL winbindd_fetch_gid_cache_entry(char *domain_name, gid_t gid, + struct winbindd_gr *gr, + void **extra_data, int *extra_data_len); +void winbindd_flush_cache(void); + +/*The following definitions come from nsswitch/winbindd_group.c */ + +enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state *state); +enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state + *state); +enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state); +enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state); +enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state); + +/*The following definitions come from nsswitch/winbindd_idmap.c */ + +BOOL winbindd_idmap_get_uid_from_rid(char *domain_name, uint32 user_rid, + uid_t *uid); +BOOL winbindd_idmap_get_gid_from_rid(char *domain_name, uint32 group_rid, + gid_t *gid); +BOOL get_rid_from_id(int id, uint32 *rid, struct winbindd_domain **domain, + BOOL isgroup); +BOOL winbindd_idmap_get_rid_from_uid(uid_t uid, uint32 *user_rid, + struct winbindd_domain **domain); +BOOL winbindd_idmap_get_rid_from_gid(gid_t gid, uint32 *group_rid, + struct winbindd_domain **domain); +BOOL winbindd_idmap_init(void); + +/*The following definitions come from nsswitch/winbindd_pam.c */ + +enum winbindd_result winbindd_pam_auth(struct winbindd_cli_state *state) ; + +/*The following definitions come from nsswitch/winbindd_user.c */ + +enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state) ; +enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state + *state); +enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state); +enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state); +enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state); + +/*The following definitions come from nsswitch/winbindd_util.c */ + +BOOL domain_handles_open(struct winbindd_domain *domain); +void establish_connections(void) ; +BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain); +BOOL get_domain_info(struct winbindd_domain *domain); +BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain, + char *name, DOM_SID *sid, + enum SID_NAME_USE *type); +BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain, + DOM_SID *sid, char *name, + enum SID_NAME_USE *type); +BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain, + uint32 user_rid, SAM_USERINFO_CTR *user_info); +BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain, + uint32 group_rid, GROUP_INFO_CTR *info); +BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain, + uint32 group_rid, uint32 *num_names, + uint32 **rid_mem, char ***names, + enum SID_NAME_USE **name_types); +int winbindd_lookup_aliasmem(struct winbindd_domain *domain, + uint32 alias_rid, uint32 *num_names, + DOM_SID ***sids, char ***names, + enum SID_NAME_USE **name_types); +struct winbindd_domain *find_domain_from_name(char *domain_name); +void free_getent_state(struct getent_state *state); +BOOL winbindd_param_init(void); +char *winbindd_cmd_to_string(enum winbindd_cmd cmd); +void parse_domain_user(char *domuser, fstring domain, fstring user); +uint32 domain_sequence_number(char *domain_name); +#endif /* _WINBINDD_PROTO_H_ */ diff --git a/source3/nsswitch/winbindd_user.c b/source3/nsswitch/winbindd_user.c new file mode 100644 index 0000000000..3e7cba0118 --- /dev/null +++ b/source3/nsswitch/winbindd_user.c @@ -0,0 +1,412 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon - user related function + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" + +/* Fill a pwent structure with information we have obtained */ + +static void winbindd_fill_pwent(struct winbindd_pw *pw, char *name, + uid_t unix_uid, gid_t unix_gid, + char *full_name) +{ + pstring homedir; + fstring name_domain, name_user; + + if (!pw || !name) { + return; + } + + /* Fill in uid/gid */ + + pw->pw_uid = unix_uid; + pw->pw_gid = unix_gid; + + /* Username */ + + safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1); + + /* Full name (gecos) */ + + safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1); + + /* Home directory and shell - use template config parameters. The + defaults are /tmp for the home directory and /bin/false for shell. */ + + parse_domain_user(name, name_domain, name_user); + + pstrcpy(homedir, lp_template_homedir()); + + pstring_sub(homedir, "%U", name_user); + pstring_sub(homedir, "%D", name_domain); + + safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1); + + safe_strcpy(pw->pw_shell, lp_template_shell(), sizeof(pw->pw_shell) - 1); + + /* Password - set to "x" as we can't generate anything useful here. + Authentication can be done using the pam_ntdom module. */ + + safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1); +} + +/* Return a password structure from a username. Specify whether cached data + can be returned. */ + +enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state) +{ + uint32 name_type, user_rid, group_rid; + SAM_USERINFO_CTR user_info; + DOM_SID user_sid; + fstring name_domain, name_user, name, gecos_name; + struct winbindd_domain *domain; + uid_t uid; + gid_t gid; + + /* Parse domain and username */ + parse_domain_user(state->request.data.username, name_domain, name_user); + + /* Reject names that don't have a domain - i.e name_domain contains the + entire name. */ + + if (strequal(name_domain, "")) { + return WINBINDD_ERROR; + } + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("could not find domain entry for domain %s\n", name_domain)); + return WINBINDD_ERROR; + } + + /* Check for cached user entry */ + + if (winbindd_fetch_user_cache_entry(name_domain, name_user, + &state->response.data.pw)) { + return WINBINDD_OK; + } + + slprintf(name,sizeof(name),"%s\\%s", name_domain, name_user); + + /* Get rid and name type from name */ + /* the following costs 1 packet */ + if (!winbindd_lookup_sid_by_name(domain, name, &user_sid, &name_type)) { + DEBUG(1, ("user '%s' does not exist\n", name_user)); + return WINBINDD_ERROR; + } + + if (name_type != SID_NAME_USER) { + DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, name_type)); + return WINBINDD_ERROR; + } + + /* Get some user info. Split the user rid from the sid obtained from + the winbind_lookup_by_name() call and use it in a + winbind_lookup_userinfo() */ + + sid_split_rid(&user_sid, &user_rid); + + /* the following costs 3 packets */ + if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) { + DEBUG(1, ("pwnam_from_user(): error getting user info for user '%s'\n", + name_user)); + return WINBINDD_ERROR; + } + + group_rid = user_info.info.id21->group_rid; + unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name, + sizeof(gecos_name) - 1); + + free_samr_userinfo_ctr(&user_info); + + /* Resolve the uid number */ + + if (!winbindd_idmap_get_uid_from_rid(domain->name, user_rid, &uid)) { + DEBUG(1, ("error getting user id for user %s\n", name_user)); + return WINBINDD_ERROR; + } + + /* Resolve the gid number */ + + if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) { + DEBUG(1, ("error getting group id for user %s\n", name_user)); + return WINBINDD_ERROR; + } + + /* Now take all this information and fill in a passwd structure */ + + winbindd_fill_pwent(&state->response.data.pw, + state->request.data.username, uid, gid, + gecos_name); + + winbindd_fill_user_cache_entry(name_domain, name_user, + &state->response.data.pw); + + return WINBINDD_OK; +} + +/* Return a password structure given a uid number */ + +enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state + *state) +{ + DOM_SID user_sid; + struct winbindd_domain *domain; + uint32 user_rid, group_rid; + fstring user_name, gecos_name; + enum SID_NAME_USE name_type; + SAM_USERINFO_CTR user_info; + gid_t gid; + + /* Get rid from uid */ + if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, &user_rid, + &domain)) { + DEBUG(1, ("Could not convert uid %d to rid\n", + state->request.data.uid)); + return WINBINDD_ERROR; + } + + /* Check for cached uid entry */ + if (winbindd_fetch_uid_cache_entry(domain->name, state->request.data.uid, + &state->response.data.pw)) { + return WINBINDD_OK; + } + + + /* Get name and name type from rid */ + + sid_copy(&user_sid, &domain->sid); + sid_append_rid(&user_sid, user_rid); + + if (!winbindd_lookup_name_by_sid(domain, &user_sid, user_name, + &name_type)) { + fstring temp; + + sid_to_string(temp, &user_sid); + DEBUG(1, ("Could not lookup sid %s\n", temp)); + return WINBINDD_ERROR; + } + + string_sub(user_name, "\\", "/", sizeof(fstring)); + + /* Get some user info */ + + if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) { + DEBUG(1, ("pwnam_from_uid(): error getting user info for user '%s'\n", + user_name)); + return WINBINDD_ERROR; + } + + group_rid = user_info.info.id21->group_rid; + unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name, + sizeof(gecos_name) - 1); + + free_samr_userinfo_ctr(&user_info); + + /* Resolve gid number */ + + if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) { + DEBUG(1, ("error getting group id for user %s\n", user_name)); + return WINBINDD_ERROR; + } + + /* Fill in password structure */ + + winbindd_fill_pwent(&state->response.data.pw, user_name, + state->request.data.uid, gid, gecos_name); + + winbindd_fill_uid_cache_entry(domain->name, state->request.data.uid, + &state->response.data.pw); + + return WINBINDD_OK; +} + +/* + * set/get/endpwent functions + */ + +/* Rewind file pointer for ntdom passwd database */ + +enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state) +{ + struct winbindd_domain *tmp; + + if (state == NULL) return WINBINDD_ERROR; + + /* Free old static data if it exists */ + + if (state->getpwent_state != NULL) { + free_getent_state(state->getpwent_state); + state->getpwent_state = NULL; + } + + /* Create sam pipes for each domain we know about */ + + for(tmp = domain_list; tmp != NULL; tmp = tmp->next) { + struct getent_state *domain_state; + + /* Skip domains other than WINBINDD_DOMAIN environment variable */ + + if ((strcmp(state->request.domain, "") != 0) && + (strcmp(state->request.domain, tmp->name) != 0)) { + continue; + } + + /* Create a state record for this domain */ + + if ((domain_state = (struct getent_state *) + malloc(sizeof(struct getent_state))) == NULL) { + + return WINBINDD_ERROR; + } + + ZERO_STRUCTP(domain_state); + domain_state->domain = tmp; + + /* Add to list of open domains */ + + DLIST_ADD(state->getpwent_state, domain_state) + } + + return WINBINDD_OK; +} + +/* Close file pointer to ntdom passwd database */ + +enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state) +{ + if (state == NULL) return WINBINDD_ERROR; + + free_getent_state(state->getpwent_state); + state->getpwent_state = NULL; + + return WINBINDD_OK; +} + +/* Fetch next passwd entry from ntdom database */ + +enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state) +{ + if (state == NULL) return WINBINDD_ERROR; + + /* Process the current head of the getent_state list */ + + while(state->getpwent_state != NULL) { + struct getent_state *ent = state->getpwent_state; + + /* Get list of user entries for this pipe */ + + if (!ent->got_sam_entries) { + uint32 status, start_ndx = 0; + + /* Look in cache for entries, else get them direct */ + + if (!winbindd_fetch_user_cache(ent->domain->name, + &ent->sam_entries, + &ent->num_sam_entries)) { + + /* Fetch the user entries */ + + if (!domain_handles_open(ent->domain)) goto cleanup; + + do { + status = + samr_enum_dom_users( + &ent->domain->sam_dom_handle, &start_ndx, 0, 0, + 0x10000, &ent->sam_entries, &ent->num_sam_entries); + } while (status == STATUS_MORE_ENTRIES); + + /* Fill cache with received entries */ + + winbindd_fill_user_cache(ent->domain->name, ent->sam_entries, + ent->num_sam_entries); + } + + ent->got_sam_entries = True; + } + + /* Send back a user */ + + while (ent->sam_entry_index < ent->num_sam_entries) { + enum winbindd_result result; + fstring domain_user_name; + char *user_name = (ent->sam_entries) + [ent->sam_entry_index].acct_name; + + /* Don't bother with machine accounts */ + + if (user_name[strlen(user_name) - 1] == '$') { + ent->sam_entry_index++; + continue; + } + + /* Prepend domain to name */ + + slprintf(domain_user_name, sizeof(domain_user_name), + "%s/%s", ent->domain->name, user_name); + + /* Get passwd entry from user name */ + + fstrcpy(state->request.data.username, domain_user_name); + result = winbindd_getpwnam_from_user(state); + + ent->sam_entry_index++; + + /* Return if user lookup worked */ + + if (result == WINBINDD_OK) { + return result; + } + + /* Try next user */ + + DEBUG(1, ("could not getpwnam_from_user for username %s\n", + domain_user_name)); + } + + /* We've exhausted all users for this pipe - close it down and + start on the next one. */ + + cleanup: + + /* Free mallocated memory for sam entries. The data stored here + may have been allocated from the cache. */ + + if (ent->sam_entries != NULL) free(ent->sam_entries); + ent->sam_entries = NULL; + + /* Free state information for this domain */ + + { + struct getent_state *old_ent; + + old_ent = state->getpwent_state; + DLIST_REMOVE(state->getpwent_state, state->getpwent_state); + free(old_ent); + } + } + + /* Out of pipes so we're done */ + + return WINBINDD_ERROR; +} diff --git a/source3/nsswitch/winbindd_util.c b/source3/nsswitch/winbindd_util.c new file mode 100644 index 0000000000..33150d1a62 --- /dev/null +++ b/source3/nsswitch/winbindd_util.c @@ -0,0 +1,633 @@ +/* + Unix SMB/Netbios implementation. + Version 2.0 + + Winbind daemon for ntdom nss module + + Copyright (C) Tim Potter 2000 + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "winbindd.h" +#include "sids.h" + +BOOL domain_handles_open(struct winbindd_domain *domain) +{ + return domain->sam_handle_open && + domain->sam_dom_handle_open; +} + +static BOOL resolve_dc_name(char *domain_name, fstring domain_controller) +{ + struct in_addr ip; + extern pstring global_myname; + + /* if its our primary domain and password server is not '*' then use the + password server parameter */ + if (strcmp(domain_name,lp_workgroup()) == 0 && + strcmp(lp_passwordserver(),"*") != 0) { + fstrcpy(domain_controller, lp_passwordserver()); + return True; + } + + if (!resolve_name(domain_name, &ip, 0x1B)) return False; + + return lookup_pdc_name(global_myname, domain_name, &ip, domain_controller); +} + + +static struct winbindd_domain *add_trusted_domain(char *domain_name) +{ + struct winbindd_domain *domain; + + DEBUG(1, ("adding trusted domain %s\n", domain_name)); + + /* Create new domain entry */ + + if ((domain = (struct winbindd_domain *)malloc(sizeof(*domain))) == NULL) { + return NULL; + } + + /* Fill in fields */ + + ZERO_STRUCTP(domain); + + if (domain_name) { + fstrcpy(domain->name, domain_name); + } + + /* Link to domain list */ + + DLIST_ADD(domain_list, domain); + + return domain; +} + +/* Look up global info for the winbind daemon */ +static BOOL get_trusted_domains(void) +{ + uint32 enum_ctx = 0; + uint32 num_doms = 0; + char **domains = NULL; + DOM_SID **sids = NULL; + BOOL result; + int i; + + DEBUG(1, ("getting trusted domain list\n")); + + /* Add our workgroup - keep handle to look up trusted domains */ + if (!add_trusted_domain(lp_workgroup())) { + DEBUG(0, ("could not add record for domain %s\n", lp_workgroup())); + return False; + } + + /* Enumerate list of trusted domains */ + result = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx, + &num_doms, &domains, &sids); + + if (!result || !domains) return False; + + /* Add each domain to the trusted domain list */ + for(i = 0; i < num_doms; i++) { + if (!add_trusted_domain(domains[i])) { + DEBUG(0, ("could not add record for domain %s\n", domains[i])); + result = False; + } + } + + /* Free memory */ + free_char_array(num_doms, domains); + free_sid_array(num_doms, sids); + + return True; +} + + +/* Open sam and sam domain handles to a domain and cache the results */ +static BOOL open_sam_handles(struct winbindd_domain *domain) +{ + /* Get domain info */ + if (!domain->got_domain_info) { + domain->got_domain_info = get_domain_info(domain); + if (!domain->got_domain_info) return False; + } + + /* Open sam handle if it isn't already open */ + if (!domain->sam_handle_open) { + domain->sam_handle_open = + samr_connect(domain->controller, SEC_RIGHTS_MAXIMUM_ALLOWED, + &domain->sam_handle); + if (!domain->sam_handle_open) return False; + } + + /* Open sam domain handle if it isn't already open */ + if (!domain->sam_dom_handle_open) { + domain->sam_dom_handle_open = + samr_open_domain(&domain->sam_handle, + SEC_RIGHTS_MAXIMUM_ALLOWED, &domain->sid, + &domain->sam_dom_handle); + if (!domain->sam_dom_handle_open) return False; + } + + return True; +} + +static void winbindd_kill_connections(void) +{ + struct winbindd_domain *domain; + + DEBUG(1,("killing winbindd connections\n")); + + server_state.pwdb_initialised = False; + server_state.lsa_handle_open = False; + lsa_close(&server_state.lsa_handle); + + for (domain=domain_list; domain; domain=domain->next) { + if (domain->sam_dom_handle_open) { + samr_close(&domain->sam_dom_handle); + domain->sam_dom_handle_open = False; + } + if (domain->sam_handle_open) { + samr_close(&domain->sam_handle); + domain->sam_handle_open = False; + } + DLIST_REMOVE(domain_list, domain); + free(domain); + } +} + +void establish_connections(void) +{ + struct winbindd_domain *domain; + static time_t lastt; + time_t t; + + t = time(NULL); + if (t - lastt < WINBINDD_ESTABLISH_LOOP) return; + lastt = t; + + /* maybe the connection died - if so then close up and restart */ + if (server_state.pwdb_initialised && + server_state.lsa_handle_open && + !rpc_hnd_ok(&server_state.lsa_handle)) { + winbindd_kill_connections(); + } + + if (!server_state.pwdb_initialised) { + fstrcpy(server_state.controller, lp_passwordserver()); + if (strcmp(server_state.controller,"*") == 0) { + if (!resolve_dc_name(lp_workgroup(), server_state.controller)) { + return; + } + } + + server_state.pwdb_initialised = pwdb_initialise(False); + if (!server_state.pwdb_initialised) return; + } + + /* Open lsa handle if it isn't already open */ + if (!server_state.lsa_handle_open) { + server_state.lsa_handle_open = + lsa_open_policy(server_state.controller, &server_state.lsa_handle, + False, SEC_RIGHTS_MAXIMUM_ALLOWED); + if (!server_state.lsa_handle_open) return; + + /* now we can talk to the server we can get some info */ + get_trusted_domains(); + } + + for (domain=domain_list; domain; domain=domain->next) { + if (!domain_handles_open(domain)) { + open_sam_handles(domain); + } + } +} + + +/* Connect to a domain controller using get_any_dc_name() to discover + the domain name and sid */ +BOOL lookup_domain_sid(char *domain_name, struct winbindd_domain *domain) +{ + fstring level5_dom; + BOOL res; + uint32 enum_ctx = 0; + uint32 num_doms = 0; + char **domains = NULL; + DOM_SID **sids = NULL; + + if (domain == NULL) { + return False; + } + + DEBUG(1, ("looking up sid for domain %s\n", domain_name)); + + /* Get controller name for domain */ + if (!resolve_dc_name(domain_name, domain->controller)) { + return False; + } + + if (strequal(domain->controller, server_state.controller)) { + /* Do a level 5 query info policy */ + return lsa_query_info_pol(&server_state.lsa_handle, 0x05, + level5_dom, &domain->sid); + } + + /* Use lsaenumdomains to get sid for this domain */ + + res = lsa_enum_trust_dom(&server_state.lsa_handle, &enum_ctx, + &num_doms, &domains, &sids); + + /* Look for domain name */ + + if (res && domains && sids) { + int found = False; + int i; + + for(i = 0; i < num_doms; i++) { + if (strequal(domain_name, domains[i])) { + sid_copy(&domain->sid, sids[i]); + found = True; + break; + } + } + + res = found; + } + + /* Free memory */ + + free_char_array(num_doms, domains); + free_sid_array(num_doms, sids); + + return res; +} + + +/* Lookup domain controller and sid for a domain */ + +BOOL get_domain_info(struct winbindd_domain *domain) +{ + fstring sid_str; + + DEBUG(1, ("Getting domain info for domain %s\n", domain->name)); + + /* Lookup domain sid */ + if (!lookup_domain_sid(domain->name, domain)) { + DEBUG(0, ("could not find sid for domain %s\n", domain->name)); + return False; + } + + /* Lookup OK */ + + domain->got_domain_info = 1; + + sid_to_string(sid_str, &domain->sid); + DEBUG(1, ("found sid %s for domain %s\n", sid_str, domain->name)); + + return True; +} + +/* Lookup a sid in a domain from a name */ + +BOOL winbindd_lookup_sid_by_name(struct winbindd_domain *domain, + char *name, DOM_SID *sid, + enum SID_NAME_USE *type) +{ + int num_sids = 0, num_names = 1; + DOM_SID *sids = NULL; + uint32 *types = NULL; + BOOL res; + + /* Don't bother with machine accounts */ + + if (name[strlen(name) - 1] == '$') { + return False; + } + + /* Lookup name */ + + res = lsa_lookup_names(&server_state.lsa_handle, num_names, (char **)&name, + &sids, &types, &num_sids); + + /* Return rid and type if lookup successful */ + + if (res) { + + /* Return sid */ + + if ((sid != NULL) && (sids != NULL)) { + sid_copy(sid, &sids[0]); + } + + /* Return name type */ + + if ((type != NULL) && (types != NULL)) { + *type = types[0]; + } + } + + /* Free memory */ + + if (types != NULL) free(types); + if (sids != NULL) free(sids); + + return res; +} + +/* Lookup a name in a domain from a sid */ + +BOOL winbindd_lookup_name_by_sid(struct winbindd_domain *domain, + DOM_SID *sid, char *name, + enum SID_NAME_USE *type) +{ + int num_sids = 1, num_names = 0; + uint32 *types = NULL; + char **names; + BOOL res; + + /* Lookup name */ + res = lsa_lookup_sids(&server_state.lsa_handle, num_sids, &sid, &names, + &types, &num_names); + + /* Return name and type if successful */ + + if (res) { + + /* Return name */ + + if ((names != NULL) && (name != NULL)) { + fstrcpy(name, names[0]); + } + + /* Return name type */ + + if ((type != NULL) && (types != NULL)) { + *type = types[0]; + } + } + + /* Free memory */ + + safe_free(types); + free_char_array(num_names, names); + + return res; +} + +/* Lookup user information from a rid */ + +BOOL winbindd_lookup_userinfo(struct winbindd_domain *domain, + uint32 user_rid, SAM_USERINFO_CTR *user_info) +{ + if (!domain_handles_open(domain)) return False; + + return get_samr_query_userinfo(&domain->sam_dom_handle, 0x15, user_rid, user_info); +} + +/* Lookup group information from a rid */ + +BOOL winbindd_lookup_groupinfo(struct winbindd_domain *domain, + uint32 group_rid, GROUP_INFO_CTR *info) +{ + if (!domain_handles_open(domain)) return False; + + return get_samr_query_groupinfo(&domain->sam_dom_handle, 1, group_rid, info); +} + +/* Lookup group membership given a rid */ + +BOOL winbindd_lookup_groupmem(struct winbindd_domain *domain, + uint32 group_rid, uint32 *num_names, + uint32 **rid_mem, char ***names, + enum SID_NAME_USE **name_types) +{ + if (!domain_handles_open(domain)) return False; + + return sam_query_groupmem(&domain->sam_dom_handle, group_rid, num_names, + rid_mem, names, name_types); +} + +/* Lookup alias membership given a rid */ + +int winbindd_lookup_aliasmem(struct winbindd_domain *domain, + uint32 alias_rid, uint32 *num_names, + DOM_SID ***sids, char ***names, + enum SID_NAME_USE **name_types) +{ + /* Open sam handles */ + if (!domain_handles_open(domain)) return False; + + return sam_query_aliasmem(domain->controller, + &domain->sam_dom_handle, alias_rid, num_names, + sids, names, name_types); +} + +/* Globals for domain list stuff */ + +struct winbindd_domain *domain_list = NULL; + +/* Given a domain name, return the struct winbindd domain info for it */ + +struct winbindd_domain *find_domain_from_name(char *domain_name) +{ + struct winbindd_domain *tmp; + + /* Search through list */ + for (tmp = domain_list; tmp != NULL; tmp = tmp->next) { + if (strcmp(domain_name, tmp->name) == 0) { + if (!tmp->got_domain_info) return NULL; + return tmp; + } + } + + /* Not found */ + return NULL; +} + +/* Free state information held for {set,get,end}{pw,gr}ent() functions */ + +void free_getent_state(struct getent_state *state) +{ + struct getent_state *temp; + + /* Iterate over state list */ + + temp = state; + + while(temp != NULL) { + struct getent_state *next; + + /* Free sam entries then list entry */ + + safe_free(state->sam_entries); + DLIST_REMOVE(state, state); + next = temp->next; + + free(temp); + temp = next; + } +} + +/* Parse list of arguments to winbind uid or winbind gid parameters */ + +static BOOL parse_id_list(char *paramstr, BOOL is_user) +{ + uid_t id_low, id_high = 0; + + /* Parse entry */ + + if (sscanf(paramstr, "%u-%u", &id_low, &id_high) != 2) { + DEBUG(0, ("winbid %s parameter invalid\n", + is_user ? "uid" : "gid")); + return False; + } + + /* Store id info */ + + if (is_user) { + server_state.uid_low = id_low; + server_state.uid_high = id_high; + } else { + server_state.gid_low = id_low; + server_state.gid_high = id_high; + } + + return True; +} + +/* Initialise trusted domain info */ + +BOOL winbindd_param_init(void) +{ + /* Parse winbind uid and winbind_gid parameters */ + + if (!(parse_id_list(lp_winbind_uid(), True) && + parse_id_list(lp_winbind_gid(), False))) { + return False; + } + + /* Check for reversed uid and gid ranges */ + + if (server_state.uid_low > server_state.uid_high) { + DEBUG(0, ("uid range invalid\n")); + return False; + } + + if (server_state.gid_low > server_state.gid_high) { + DEBUG(0, ("gid range for invalid\n")); + return False; + } + + return True; +} + +/* Convert a enum winbindd_cmd to a string */ + +char *winbindd_cmd_to_string(enum winbindd_cmd cmd) +{ + char *result; + + switch (cmd) { + + case WINBINDD_GETPWNAM_FROM_USER: + result = "getpwnam from user"; + break; + + case WINBINDD_GETPWNAM_FROM_UID: + result = "getpwnam from uid"; + break; + + case WINBINDD_GETGRNAM_FROM_GROUP: + result = "getgrnam from group"; + break; + + case WINBINDD_GETGRNAM_FROM_GID: + result = "getgrnam from gid"; + break; + + case WINBINDD_SETPWENT: + result = "setpwent"; + break; + + case WINBINDD_ENDPWENT: + result = "endpwent"; + break; + + case WINBINDD_GETPWENT: + result = "getpwent"; + break; + + case WINBINDD_SETGRENT: + result = "setgrent"; + break; + + case WINBINDD_ENDGRENT: + result = "endgrent"; + break; + + case WINBINDD_GETGRENT: + result = "getgrent"; + break; + + case WINBINDD_PAM_AUTH: + result = "pam_auth"; + break; + + default: + result = "invalid command"; + break; + } + + return result; +}; + + +/* parse a string of the form DOMAIN/user into a domain and a user */ +void parse_domain_user(char *domuser, fstring domain, fstring user) +{ + char *p; + p = strchr(domuser,'/'); + if (!p) p = strchr(domuser,'\\'); + if (!p) { + fstrcpy(domain,""); + fstrcpy(user, domuser); + return; + } + + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; +} + +/* find the sequence number for a domain */ +uint32 domain_sequence_number(char *domain_name) +{ + struct winbindd_domain *domain; + SAM_UNK_CTR ctr; + + domain = find_domain_from_name(domain_name); + if (!domain) return DOM_SEQUENCE_NONE; + + if (!samr_query_dom_info(&domain->sam_dom_handle, 2, &ctr)) { + DEBUG(2,("domain sequence query failed\n")); + return DOM_SEQUENCE_NONE; + } + + DEBUG(4,("got domain sequence number for %s of %u\n", + domain_name, (unsigned)ctr.info.inf2.seq_num)); + + return ctr.info.inf2.seq_num; +} |