merge some of the nsswitch code from tng to head
authorAndrew Tridgell <tridge@samba.org>
Wed, 25 Apr 2001 05:47:50 +0000 (05:47 +0000)
committerAndrew Tridgell <tridge@samba.org>
Wed, 25 Apr 2001 05:47:50 +0000 (05:47 +0000)
the libnss_winbind.so from head now works with
the winbindd from tng

source/Makefile.in
source/nsswitch/wb_common.c
source/nsswitch/wbinfo.c
source/nsswitch/winbind_nss.c
source/nsswitch/winbind_nss_config.h

index d3c0c185a0cc117bb39087d5ce7bde25e380d5a4..e8c649ac66901860c93ddf664b3143f62d7527dd 100644 (file)
@@ -384,9 +384,9 @@ WINBINDD_OBJ = \
                $(LIBNMB_OBJ) $(PARAM_OBJ) $(UBIQX_OBJ) $(LIB_OBJ) \
                $(NSSWINS_OBJ) $(SIDDB_OBJ) $(LIBSMB_OBJ)
 
-WBINFO_OBJ = nsswitch/wbinfo.o nsswitch/wb_common.o
+WBINFO_OBJ = nsswitch/wbinfo.o
 
-WINBIND_NSS_OBJ = nsswitch/winbind.o nsswitch/wb_common.o
+WINBIND_NSS_OBJ = nsswitch/winbind_nss.o nsswitch/wb_common.o
 
 WINBIND_NSS_PICOBJS = $(WINBIND_NSS_OBJ:.o=.po)
 
index 98a4b6758bc01a826f33cb2f8244b6348efa9543..4040e1cff2cc2aa597e6596f3f8b230d1cf7157e 100644 (file)
@@ -37,7 +37,7 @@ void init_request(struct winbindd_request *request, int request_type)
         static char *domain_env;
         static BOOL initialised;
 
-       request->cmd = (enum winbindd_cmd)request_type;
+       request->cmd = request_type;
        request->pid = getpid();
        request->domain[0] = '\0';
 
@@ -59,7 +59,7 @@ void init_response(struct winbindd_response *response)
 {
        /* Initialise return value */
 
-       response->result = (enum winbindd_result)NSS_STATUS_UNAVAIL;
+       response->result = WINBINDD_ERROR;
 }
 
 /* Close established socket */
@@ -141,6 +141,7 @@ static int open_pipe_sock(void)
        if (connect(established_socket, (struct sockaddr *)&sunaddr, 
                    sizeof(sunaddr)) == -1) {
                close_sock();
+               established_socket = -1;
                return -1;
        }
         
@@ -304,7 +305,7 @@ void free_response(struct winbindd_response *response)
 
 /* Handle simple types of requests */
 
-enum nss_status winbindd_request(int req_type, 
+NSS_STATUS winbindd_request(int req_type, 
                                 struct winbindd_request *request,
                                 struct winbindd_response *response)
 {
index cff2b8b69a0566cfd2f86f34a5d37c811fc9a2a6..1a51a5b6106bf2019e2851ad79f269c8ddb0c3e1 100644 (file)
@@ -101,10 +101,11 @@ static BOOL wbinfo_check_secret(void)
 
         if (result) {
 
-                if (response.data.num_entries) {
+                if (response.data.num_entries == 0) {
                         printf("Secret is good\n");
                 } else {
-                        printf("Secret is bad\n");
+                        printf("Secret is bad\n0x%08x\n", 
+                              response.data.num_entries);
                 }
 
                 return True;
@@ -447,8 +448,8 @@ int main(int argc, char **argv)
                                return 1;
                        }
                        break;
-
-                       /* Invalid option */
+                               
+                      /* Invalid option */
 
                default:
                        usage();
index 78485aa05e3620f82e7ac691556573853ac4bcf2..04b576a7a53660cfc79ced1d785f2a822c72c2f2 100644 (file)
 #include "winbind_nss_config.h"
 #include "winbindd_nss.h"
 
-/* prototypes from common.c */
+/* Prototypes from common.c */
+
 void init_request(struct winbindd_request *req,int rq_type);
+NSS_STATUS winbindd_request(int req_type, 
+                                struct winbindd_request *request,
+                                struct winbindd_response *response);
 int write_sock(void *buffer, int count);
 int read_reply(struct winbindd_response *response);
-
+void free_response(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_*
@@ -37,429 +41,810 @@ int read_reply(struct winbindd_response *response);
 
 static char *get_static(char **buffer, int *buflen, int len)
 {
-    char *result;
+       char *result;
 
-    /* Error check.  We return false if things aren't set up right, or
-       there isn't enough buffer space left. */
+       /* 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;
+       }
 
-    if ((buffer == NULL) || (buflen == NULL) || (*buflen < len)) {
-        return NULL;
-    }
+       /* Some architectures, like Sparc, need pointers aligned on 
+          boundaries */
+#if _ALIGNMENT_REQUIRED
+       {
+               int mod = len % _MAX_ALIGNMENT;
+               if(mod != 0)
+                       len += _MAX_ALIGNMENT - mod;
+       }
+#endif
 
-    /* Return an index into the static buffer */
+       /* Return an index into the static buffer */
 
-    result = *buffer;
-    *buffer += len;
-    *buflen -= len;
+       result = *buffer;
+       *buffer += len;
+       *buflen -= len;
 
-    return result;
+       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;
+       char *s;
+       BOOL quoted;
+       size_t len=1;
     
-    if (!ptr) ptr = &last_ptr;
-    if (!ptr) return(False);
+       if (!ptr) ptr = &last_ptr;
+       if (!ptr) return(False);
     
-    s = *ptr;
+       s = *ptr;
     
-    /* default to simple separators */
-    if (!sep) sep = " \t\n\r";
+       /* default to simple separators */
+       if (!sep) sep = " \t\n\r";
     
-    /* find the first non sep char */
-    while(*s && strchr(sep,*s)) s++;
+       /* find the first non sep char */
+       while(*s && strchr(sep,*s)) s++;
     
-    /* nothing left? */
-    if (! *s) return(False);
+       /* 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;
-        }
-    }
+       /* 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;
+       *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;
+       return(True);
 }
 
 /* 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. */
+   Return 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)
+static NSS_STATUS fill_pwent(struct passwd *result,
+                                 struct winbindd_pw *pw,
+                                 char **buffer, int *buflen)
 {
-    struct winbindd_pw *pw = &response->data.pw;
-
-    /* User name */
+       /* User name */
 
-    if ((result->pw_name = 
-         get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
+       if ((result->pw_name = 
+            get_static(buffer, buflen, strlen(pw->pw_name) + 1)) == NULL) {
 
-        /* Out of memory */
+               /* Out of memory */
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    strcpy(result->pw_name, pw->pw_name);
+       strcpy(result->pw_name, pw->pw_name);
 
-    /* Password */
+       /* Password */
 
-    if ((result->pw_passwd = 
-         get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
+       if ((result->pw_passwd = 
+            get_static(buffer, buflen, strlen(pw->pw_passwd) + 1)) == NULL) {
 
-        /* Out of memory */
+               /* Out of memory */
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    strcpy(result->pw_passwd, pw->pw_passwd);
+       strcpy(result->pw_passwd, pw->pw_passwd);
         
-    /* [ug]id */
-
-    result->pw_uid = pw->pw_uid;
-    result->pw_gid = pw->pw_gid;
-
-    /* GECOS */
+       /* [ug]id */
 
-    if ((result->pw_gecos = 
-         get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
+       result->pw_uid = pw->pw_uid;
+       result->pw_gid = pw->pw_gid;
 
-        /* Out of memory */
+       /* GECOS */
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
+       if ((result->pw_gecos = 
+            get_static(buffer, buflen, strlen(pw->pw_gecos) + 1)) == NULL) {
 
-    strcpy(result->pw_gecos, pw->pw_gecos);
+               /* Out of memory */
 
-    /* Home directory */
-
-    if ((result->pw_dir = 
-         get_static(buffer, buflen, strlen(pw->pw_dir) + 1)) == NULL) {
-
-        /* Out of memory */
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-        *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) {
 
-    strcpy(result->pw_dir, pw->pw_dir);
+               /* Out of memory */
 
-    /* Logon shell */
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    if ((result->pw_shell = 
-         get_static(buffer, buflen, strlen(pw->pw_shell) + 1)) == NULL) {
+       strcpy(result->pw_dir, pw->pw_dir);
 
-        /* Out of memory */
+       /* 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;
-    }
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    strcpy(result->pw_shell, pw->pw_shell);
+       strcpy(result->pw_shell, pw->pw_shell);
 
-    return NSS_STATUS_SUCCESS;
+       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. */
+   Return 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)
+static int fill_grent(struct group *result, struct winbindd_gr *gr,
+                     char *gr_mem, char **buffer, int *buflen)
 {
-    struct winbindd_gr *gr = &response->data.gr;
-    fstring name;
-    int i;
+       fstring name;
+       int i;
 
-    /* Group name */
+       /* Group name */
 
-    if ((result->gr_name =
-         get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
+       if ((result->gr_name =
+            get_static(buffer, buflen, strlen(gr->gr_name) + 1)) == NULL) {
 
-        /* Out of memory */
+               /* Out of memory */
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
-
-    strcpy(result->gr_name, gr->gr_name);
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    /* Password */
+       strcpy(result->gr_name, gr->gr_name);
 
-    if ((result->gr_passwd =
-         get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
+       /* Password */
 
-        /* Out of memory */
+       if ((result->gr_passwd =
+            get_static(buffer, buflen, strlen(gr->gr_passwd) + 1)) == NULL) {
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
+               /* Out of memory */
+               
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    strcpy(result->gr_passwd, gr->gr_passwd);
+       strcpy(result->gr_passwd, gr->gr_passwd);
 
-    /* gid */
+       /* gid */
 
-    result->gr_gid = gr->gr_gid;
+       result->gr_gid = gr->gr_gid;
 
-    /* Group membership */
+       /* Group membership */
 
-    if ((gr->num_gr_mem < 0) || !response->extra_data) {
-        gr->num_gr_mem = 0;
-    }
+       if ((gr->num_gr_mem < 0) || !gr_mem) {
+               gr->num_gr_mem = 0;
+       }
 
-    if ((result->gr_mem = 
-         (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * 
-                             sizeof(char *))) == NULL) {
+       if ((result->gr_mem = 
+            (char **)get_static(buffer, buflen, (gr->num_gr_mem + 1) * 
+                                sizeof(char *))) == NULL) {
 
-        /* Out of memory */
+               /* Out of memory */
 
-        *errnop = ERANGE;
-        return NSS_STATUS_TRYAGAIN;
-    }
+               return NSS_STATUS_TRYAGAIN;
+       }
 
-    if (gr->num_gr_mem == 0) {
+       if (gr->num_gr_mem == 0) {
 
-        /* Group is empty */
+               /* Group is empty */
 
-        *(result->gr_mem) = NULL;
-        return NSS_STATUS_SUCCESS;
-    }
+               *(result->gr_mem) = NULL;
+               return NSS_STATUS_SUCCESS;
+       }
 
-    /* Start looking at extra data */
+       /* Start looking at extra data */
 
-    i = 0;
+       i = 0;
 
-    while(next_token(&response->extra_data, name, ",", sizeof(fstring))) {
+       while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
         
-        /* Allocate space for member */
+               /* Allocate space for member */
         
-        if (((result->gr_mem)[i] = 
-             get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
+               if (((result->gr_mem)[i] = 
+                    get_static(buffer, buflen, strlen(name) + 1)) == NULL) {
             
-            /* Out of memory */
+                       /* Out of memory */
             
-            *errnop = ERANGE;
-            return NSS_STATUS_TRYAGAIN;
-        }        
+                       return NSS_STATUS_TRYAGAIN;
+               }        
         
-        strcpy((result->gr_mem)[i], name);
-        i++;
-    }
+               strcpy((result->gr_mem)[i], name);
+               i++;
+       }
 
-    /* Terminate list */
+       /* Terminate list */
 
-    (result->gr_mem)[i] = NULL;
-    
-    return NSS_STATUS_SUCCESS;
+       (result->gr_mem)[i] = NULL;
+
+       return NSS_STATUS_SUCCESS;
 }
 
 /*
  * NSS user functions
  */
 
+static struct winbindd_response getpwent_response;
+
+static int ndx_pw_cache;                 /* Current index into pwd cache */
+static int num_pw_cache;                 /* Current size of pwd cache */
+
 /* Rewind "file pointer" to start of ntdom password database */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_setpwent(void)
 {
-       return generic_request(WINBINDD_SETPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: setpwent\n", getpid());
+#endif
+
+       if (num_pw_cache > 0) {
+               ndx_pw_cache = num_pw_cache = 0;
+               free_response(&getpwent_response);
+       }
+
+       return winbindd_request(WINBINDD_SETPWENT, NULL, NULL);
 }
 
 /* Close ntdom password database "file pointer" */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_endpwent(void)
 {
-       return generic_request(WINBINDD_ENDPWENT, NULL, NULL);
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: endpwent\n", getpid());
+#endif
+
+       if (num_pw_cache > 0) {
+               ndx_pw_cache = num_pw_cache = 0;
+               free_response(&getpwent_response);
+       }
+
+       return winbindd_request(WINBINDD_ENDPWENT, NULL, NULL);
 }
 
 /* Fetch the next password entry from ntdom password database */
 
-enum nss_status
+#define MAX_GETPWENT_USERS 250
+
+NSS_STATUS
 _nss_winbind_getpwent_r(struct passwd *result, char *buffer, 
-                      size_t buflen, int *errnop)
+                       size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       struct winbindd_request request;
+       static int called_again;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getpwent\n", getpid());
+#endif
+
+       /* Return an entry from the cache if we have one, or if we are
+          called again because we exceeded our static buffer.  */
+
+       if ((ndx_pw_cache < num_pw_cache) || called_again) {
+               goto return_result;
+       }
+
+       /* Else call winbindd to get a bunch of entries */
+       
+       if (num_pw_cache > 0) {
+               free_response(&getpwent_response);
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(getpwent_response);
+
+       request.data.num_entries = MAX_GETPWENT_USERS;
+
+       ret = winbindd_request(WINBINDD_GETPWENT, &request, 
+                              &getpwent_response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               struct winbindd_pw *pw_cache;
+
+               /* Fill cache */
+
+               ndx_pw_cache = 0;
+               num_pw_cache = getpwent_response.data.num_entries;
 
-       ret = generic_request(WINBINDD_GETPWENT, NULL, &response);
-       if (ret != NSS_STATUS_SUCCESS) return ret;
+               /* Return a result */
 
-       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+       return_result:
+
+               pw_cache = getpwent_response.extra_data;
+
+               /* Check data is valid */
+
+               if (pw_cache == NULL) {
+                       return NSS_STATUS_NOTFOUND;
+               }
+
+               ret = fill_pwent(result, &pw_cache[ndx_pw_cache],
+                                &buffer, &buflen);
+               
+               /* Out of memory - try again */
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       called_again = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               *errnop = errno = 0;
+               called_again = False;
+               ndx_pw_cache++;
+
+               /* If we've finished with this lot of results free cache */
+
+               if (ndx_pw_cache == num_pw_cache) {
+                       ndx_pw_cache = num_pw_cache = 0;
+                       free_response(&getpwent_response);
+               }
+       }
+
+       return ret;
 }
 
 /* Return passwd struct from uid */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
-                      size_t buflen, int *errnop)
+                       size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       static struct winbindd_response response;
        struct winbindd_request request;
+       static int keep_response=0;
+
+       /* If our static buffer needs to be expanded we are called again */
+       if (!keep_response) {
 
-       request.data.uid = uid;
+               /* Call for the first time */
 
-       ret = generic_request(WINBINDD_GETPWNAM_FROM_UID, &request, &response);
-       if (ret != NSS_STATUS_SUCCESS) return ret;
+               ZERO_STRUCT(response);
+               ZERO_STRUCT(request);
 
-       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+               request.data.uid = uid;
+
+               ret = winbindd_request(WINBINDD_GETPWNAM_FROM_UID, &request, 
+                                      &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_pwent(result, &response.data.pw, 
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = errno = 0;
+       }
+
+       free_response(&response);
+       return ret;
 }
 
 /* Return passwd struct from username */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_getpwnam_r(const char *name, struct passwd *result, char *buffer,
-                      size_t buflen, int *errnop)
+                       size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       static struct winbindd_response response;
        struct winbindd_request request;
+       static int keep_response;
 
-       strncpy(request.data.username, name, sizeof(request.data.username) - 1);
-       request.data.username[sizeof(request.data.username) - 1] = '\0';
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getpwnam %s\n", getpid(), name);
+#endif
 
-       ret = generic_request(WINBINDD_GETPWNAM_FROM_USER, &request, &response);
-       if (ret != NSS_STATUS_SUCCESS) return ret;
+       /* If our static buffer needs to be expanded we are called again */
 
-       return fill_pwent(result, &response, &buffer, &buflen, errnop);
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(response);
+               ZERO_STRUCT(request);
+
+               strncpy(request.data.username, name, 
+                       sizeof(request.data.username) - 1);
+               request.data.username
+                       [sizeof(request.data.username) - 1] = '\0';
+
+               ret = winbindd_request(WINBINDD_GETPWNAM_FROM_USER, &request, 
+                                      &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_pwent(result, &response.data.pw, &buffer,
+                                        &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_pwent(result, &response.data.pw, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = errno = 0;
+       }
+
+       free_response(&response);
+       return ret;
 }
 
 /*
  * NSS group functions
  */
 
+static struct winbindd_response getgrent_response;
+
+static int ndx_gr_cache;                 /* Current index into grp cache */
+static int num_gr_cache;                 /* Current size of grp cache */
+
 /* Rewind "file pointer" to start of ntdom group database */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_setgrent(void)
 {
-       return generic_request(WINBINDD_SETGRENT, NULL, NULL);
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: setgrent\n", getpid());
+#endif
+
+       if (num_gr_cache > 0) {
+               ndx_gr_cache = num_gr_cache = 0;
+               free_response(&getgrent_response);
+       }
+
+       return winbindd_request(WINBINDD_SETGRENT, NULL, NULL);
 }
 
 /* Close "file pointer" for ntdom group database */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_endgrent(void)
 {
-       return generic_request(WINBINDD_ENDGRENT, NULL, NULL);
-}
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: endgrent\n", getpid());
+#endif
 
+       if (num_gr_cache > 0) {
+               ndx_gr_cache = num_gr_cache = 0;
+               free_response(&getgrent_response);
+       }
 
+       return winbindd_request(WINBINDD_ENDGRENT, NULL, NULL);
+}
 
 /* Get next entry from ntdom group database */
 
-enum nss_status
+#define MAX_GETGRENT_USERS 250
+
+NSS_STATUS
 _nss_winbind_getgrent_r(struct group *result,
-                      char *buffer, size_t buflen, int *errnop)
+                       char *buffer, size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       static struct winbindd_request request;
+       static int called_again;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrent\n", getpid());
+#endif
+
+       /* Return an entry from the cache if we have one, or if we are
+          called again because we exceeded our static buffer.  */
+
+       if ((ndx_gr_cache < num_gr_cache) || called_again) {
+               goto return_result;
+       }
+
+       /* Else call winbindd to get a bunch of entries */
+       
+       if (num_gr_cache > 0) {
+               free_response(&getgrent_response);
+       }
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(getgrent_response);
+
+       request.data.num_entries = MAX_GETGRENT_USERS;
+
+       ret = winbindd_request(WINBINDD_GETGRENT, &request, 
+                              &getgrent_response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               struct winbindd_gr *gr_cache;
+               int mem_ofs;
+
+               /* Fill cache */
+
+               ndx_gr_cache = 0;
+               num_gr_cache = getgrent_response.data.num_entries;
+
+               /* Return a result */
+
+       return_result:
+
+               gr_cache = getgrent_response.extra_data;
+
+               /* Check data is valid */
+
+               if (gr_cache == NULL) {
+                       return NSS_STATUS_NOTFOUND;
+               }
+
+               /* Fill group membership.  The offset into the extra data
+                  for the group membership is the reported offset plus the
+                  size of all the winbindd_gr records returned. */
+
+               mem_ofs = gr_cache[ndx_gr_cache].gr_mem_ofs +
+                       num_gr_cache * sizeof(struct winbindd_gr);
+
+               ret = fill_grent(result, &gr_cache[ndx_gr_cache],
+                                (char *)(getgrent_response.extra_data +
+                                         mem_ofs), &buffer, &buflen);
+               
+               /* Out of memory - try again */
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       called_again = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               *errnop = 0;
+               called_again = False;
+               ndx_gr_cache++;
 
-       ret = generic_request(WINBINDD_GETGRENT, NULL, &response);
-       if (ret != NSS_STATUS_SUCCESS) return ret;
+               /* If we've finished with this lot of results free cache */
 
-       return fill_grent(result, &response, &buffer, &buflen, errnop);
+               if (ndx_gr_cache == num_gr_cache) {
+                       ndx_gr_cache = num_gr_cache = 0;
+                       free_response(&getgrent_response);
+               }
+       }
+
+       return ret;
 }
 
 /* Return group struct from group name */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_getgrnam_r(const char *name,
-                      struct group *result, char *buffer,
-                      size_t buflen, int *errnop)
+                       struct group *result, char *buffer,
+                       size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       static struct winbindd_response response;
        struct winbindd_request request;
+       static int keep_response;
+       
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrnam %s\n", getpid(), name);
+#endif
 
-       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;
+       /* If our static buffer needs to be expanded we are called again */
+       
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(request);
+               ZERO_STRUCT(response);
+
+               strncpy(request.data.groupname, name, 
+                       sizeof(request.data.groupname));
+               request.data.groupname
+                       [sizeof(request.data.groupname) - 1] = '\0';
+
+               ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GROUP, 
+                                      &request, &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+                       ret = fill_grent(result, &response.data.gr, 
+                                        response.extra_data,
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+               
+               /* We've been called again */
+               
+               ret = fill_grent(result, &response.data.gr, 
+                                response.extra_data, &buffer, &buflen);
+               
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = 0;
+       }
 
-       return fill_grent(result, &response, &buffer, &buflen, errnop);
+       free_response(&response);
+       return ret;
 }
 
 /* Return group struct from gid */
 
-enum nss_status
+NSS_STATUS
 _nss_winbind_getgrgid_r(gid_t gid,
-                      struct group *result, char *buffer,
-                      size_t buflen, int *errnop)
+                       struct group *result, char *buffer,
+                       size_t buflen, int *errnop)
 {
-       enum nss_status ret;
-       struct winbindd_response response;
+       NSS_STATUS ret;
+       static struct winbindd_response response;
+       struct winbindd_request request;
+       static int keep_response;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: getgrgid %d\n", getpid(), gid);
+#endif
+
+       /* If our static buffer needs to be expanded we are called again */
+
+       if (!keep_response) {
+
+               /* Call for the first time */
+
+               ZERO_STRUCT(request);
+               ZERO_STRUCT(response);
+
+               request.data.gid = gid;
+
+               ret = winbindd_request(WINBINDD_GETGRNAM_FROM_GID, &request, 
+                                      &response);
+
+               if (ret == NSS_STATUS_SUCCESS) {
+
+                       ret = fill_grent(result, &response.data.gr, 
+                                        response.extra_data, 
+                                        &buffer, &buflen);
+
+                       if (ret == NSS_STATUS_TRYAGAIN) {
+                               keep_response = True;
+                               *errnop = errno = ERANGE;
+                               return ret;
+                       }
+               }
+
+       } else {
+
+               /* We've been called again */
+
+               ret = fill_grent(result, &response.data.gr, 
+                                response.extra_data, &buffer, &buflen);
+
+               if (ret == NSS_STATUS_TRYAGAIN) {
+                       keep_response = True;
+                       *errnop = errno = ERANGE;
+                       return ret;
+               }
+
+               keep_response = False;
+               *errnop = 0;
+       }
+
+       free_response(&response);
+       return ret;
+}
+
+/* Initialise supplementary groups */
+
+NSS_STATUS
+_nss_winbind_initgroups(char *user, gid_t group, long int *start,
+                       long int *size, gid_t *groups, long int limit,
+                       int *errnop)
+{
+       NSS_STATUS ret;
        struct winbindd_request request;
+       struct winbindd_response response;
+       int i;
+
+#ifdef DEBUG_NSS
+       fprintf(stderr, "[%5d]: initgroups %s (%d)\n", getpid(),
+               user, group);
+#endif
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
 
-       request.data.gid = gid;
+       strncpy(request.data.username, user,
+               sizeof(request.data.username) - 1);
 
-       ret = generic_request(WINBINDD_GETGRNAM_FROM_GID, &request, &response);
-       if (ret != NSS_STATUS_SUCCESS) return ret;
+       ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
+
+       if (ret == NSS_STATUS_SUCCESS) {
+               int num_gids = response.data.num_entries;
+               gid_t *gid_list = (gid_t *)response.extra_data;
+
+               /* Copy group list to client */
+
+               for (i = 0; i < num_gids; i++) {
+
+                       /* Skip primary group */
+
+                       if (gid_list[i] == group) continue;
+
+                       /* Add to buffer */
+
+                       if (*start == *size && limit <= 0) {
+                               groups = realloc(
+                                       groups, 2 * (*size) * sizeof(*groups));
+                               if (!groups) goto done;
+                               *size *= 2;
+                       }
+
+                       groups[*start] = gid_list[i];
+                       *start += 1;
+
+                       /* Filled buffer? */
+
+                       if (*start == limit) goto done;
+               }
+       }
+       
+       /* Back to your regularly scheduled programming */
 
-       return fill_grent(result, &response, &buffer, &buflen, errnop);
+ done:
+       return ret;
 }
index c663842a815371d5c76854ce0192a6a618b93dd9..5c09a5dd1bcf8f1adc515fb9ffc822d83f4e8c07 100644 (file)
 #include <errno.h>
 #include <pwd.h>
 
-#ifdef HAVE_NSS_H
+#ifdef HAVE_NSS_COMMON_H
+/* Sun Solaris */
+
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+typedef nss_status_t NSS_STATUS;
+
+#define NSS_STATUS_SUCCESS     NSS_SUCCESS
+#define NSS_STATUS_NOTFOUND    NSS_NOTFOUND
+#define NSS_STATUS_UNAVAIL     NSS_UNAVAIL
+#define NSS_STATUS_TRYAGAIN    NSS_TRYAGAIN
+
+#elif HAVE_NSS_H
+/* GNU */
+
 #include <nss.h>
-#else
-/* Minimal needed to compile.. */
-enum nss_status {
-NSS_STATUS_SUCCESS,
-NSS_STATUS_NOTFOUND,
-NSS_STATUS_UNAVAIL
-};
+
+typedef enum nss_status NSS_STATUS;
+
+#else /* Nothing's defined. Neither gnu nor sun */
+
+typedef enum
+{
+  NSS_STATUS_SUCCESS,
+  NSS_STATUS_NOTFOUND,
+  NSS_STATUS_UNAVAIL,
+  NSS_STATUS_TRYAGAIN
+} NSS_STATUS;
+
 #endif
 
+/* Declarations for functions in winbind_nss.c
+   needed in winbind_nss_solaris.c (solaris wrapper to nss) */
+
+NSS_STATUS _nss_winbind_setpwent(void);
+NSS_STATUS _nss_winbind_endpwent(void);
+NSS_STATUS _nss_winbind_getpwent_r(struct passwd* result, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwuid_r(uid_t, struct passwd*, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getpwnam_r(const char* name, struct passwd* result,
+                                  char* buffer, size_t buflen, int* errnop);
+
+NSS_STATUS _nss_winbind_setgrent(void);
+NSS_STATUS _nss_winbind_endgrent(void);
+NSS_STATUS _nss_winbind_getgrent_r(struct group* result, char* buffer,
+                                  size_t buflen, int* errnop);
+NSS_STATUS _nss_winbind_getgrnam_r(const char *name,
+                                  struct group *result, char *buffer,
+                                  size_t buflen, int *errnop);
+NSS_STATUS _nss_winbind_getgrgid_r(gid_t gid,
+                                  struct group *result, char *buffer,
+                                  size_t buflen, int *errnop);
+
 /* I'm trying really hard not to include anything from smb.h with the
    result of some silly looking redeclaration of structures. */
 
@@ -127,6 +172,7 @@ typedef int BOOL;
 
 /* zero a structure given a pointer to the structure */
 #define ZERO_STRUCTP(x) { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); }
+    
 /* Some systems (SCO) treat UNIX domain sockets as FIFOs */
 
 #ifndef S_IFSOCK