fixed a number of bugs and memory leaks in the AIX winbind shim
authorAndrew Tridgell <tridge@samba.org>
Tue, 21 Oct 2003 12:18:08 +0000 (12:18 +0000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 21 Oct 2003 12:18:08 +0000 (12:18 +0000)
source/nsswitch/winbind_nss_aix.c

index 8b5bc7a50ca829125b158a8fab1410db7b3cc119..3d2f01b93c61857db1bf511fe03f8b3468fe0fe1 100644 (file)
@@ -6,6 +6,7 @@
 
    Copyright (C) Tim Potter 2003
    Copyright (C) Steve Roylance 2003
+   Copyright (C) Andrew Tridgell 2003
    
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    Boston, MA  02111-1307, USA.   
 */
 
+/*
+  see 
+  http://publib16.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/kernextc/sec_load_mod.htm
+  for information in the interface that this module implements
+*/
+
 #include <stdlib.h>
 #include <string.h>
 #include <usersec.h>
 #include <errno.h>
+#include <stdarg.h>
 
 #include "winbind_client.h"
 
-#define MAX_GETPWENT_USERS 250
-#define MAX_GETGRENT_USERS 250
-
-static struct passwd *fill_pwent(struct winbindd_pw *pw)
-{
-       struct passwd *result;
-
-       if (!(result = malloc(sizeof(struct passwd))))
-               goto out;
-               
-       ZERO_STRUCTP(result);
-
-       /* User name */
+/*
+  the documentation doesn't say so, but experimentation shows that all
+  of the functions need to return static data, and the area can be
+  freed only when the same function is called again, or the close
+  method is called on the module. Note that this matches the standard
+  behaviour of functions like getpwnam().
+
+  The most puzzling thing about this AIX interface is that it seems to
+  offer no way of providing a user or group enumeration method. You
+  can find out any amount of detail about a user or group once you
+  know the name, but you can't obtain a list of those names. If anyone
+  does find out how to do this then please let me know (yes, I should
+  be able to find out as I work for IBM, and this is an IBM interface,
+  but finding the right person to ask is a mammoth task!)
+
+  tridge@samba.org October 2003
+*/
 
-       if ((result->pw_name = malloc(strlen(pw->pw_name) + 1)) == NULL)
-               goto out;
-       
-       strcpy(result->pw_name, pw->pw_name);
 
-       /* Password */
+/* 
+   each function uses one of the following lists of memory, declared 
+   static in each backend method. This allows the function to destroy
+   the memory when that backend is called next time
+*/
+struct mem_list {
+       struct mem_list *next, *prev;
+       void *p;
+};
 
-       if ((result->pw_passwd = malloc(strlen(pw->pw_passwd) + 1)) == NULL)
-               goto out;
-       
-       strcpy(result->pw_passwd, pw->pw_passwd);
-        
-       /* [ug]id */
 
-       result->pw_uid = pw->pw_uid;
-       result->pw_gid = pw->pw_gid;
+/* allocate some memory on a mem_list */
+static void *list_alloc(struct mem_list **list, size_t size)
+{
+       struct mem_list *m;
+       m = malloc(sizeof(*m));
+       if (!m) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       m->p = malloc(size);
+       if (!m->p) {
+               errno = ENOMEM;
+               free(m);
+               return NULL;
+       }
+       m->next = *list;
+       m->prev = NULL;
+       if (*list) {
+               (*list)->prev = m;
+       }
+       (*list) = m;
+       return m->p;
+}
 
-       /* GECOS */
+/* duplicate a string using list_alloc() */
+static char *list_strdup(struct mem_list **list, const char *s)
+{
+       char *ret = list_alloc(list, strlen(s)+1);
+       if (!ret) return NULL;
+       strcpy(ret, s);
+       return ret;
+}
 
-       if ((result->pw_gecos = malloc(strlen(pw->pw_gecos) + 1)) == NULL)
-               goto out;
+/* destroy a mem_list */
+static void list_destory(struct mem_list **list)
+{
+       struct mem_list *m, *next;
+       for (m=*list; m; m=next) {
+               next = m->next;
+               free(m->p);
+               free(m);
+       }
+       (*list) = NULL;
+}
 
-       strcpy(result->pw_gecos, pw->pw_gecos);
-       
-       /* Home directory */
-       
-       if ((result->pw_dir = malloc(strlen(pw->pw_dir) + 1)) == NULL)
-               goto out;
 
-       strcpy(result->pw_dir, pw->pw_dir);
+#define HANDLE_ERRORS(ret) do { \
+       if ((ret) == NSS_STATUS_NOTFOUND) { \
+               errno = ENOENT; \
+               return NULL; \
+       } else if ((ret) != NSS_STATUS_SUCCESS) { \
+               errno = EIO; \
+               return NULL; \
+       } \
+} while (0)
 
-       /* Logon shell */
-       
-       if ((result->pw_shell = malloc(strlen(pw->pw_shell) + 1)) == NULL)
-               goto out;
-       
-       strcpy(result->pw_shell, pw->pw_shell);
-       
-       return result;
-       
-       /* A memory allocation failed, undo succesfull allocations and
-           return NULL */
-
-out:
-       errno = ENOMEM;
-       SAFE_FREE(result->pw_dir);
-       SAFE_FREE(result->pw_gecos);
-       SAFE_FREE(result->pw_passwd);
-       SAFE_FREE(result->pw_name);
-       SAFE_FREE(result);      
-
-       return NULL;
-}
-
-static BOOL next_token(char **ptr,char *buff,char *sep, size_t bufsize)
+/*
+  fill a struct passwd from a winbindd_pw struct, using memory from a mem_list
+*/
+static struct passwd *fill_pwent(struct mem_list **list, struct winbindd_pw *pw)
 {
-       char *s;
-       BOOL quoted;
-       size_t len=1;
+       struct passwd *result;
 
-       if (!ptr) return(False);
+       if (!(result = list_alloc(list, sizeof(struct passwd)))) {
+               return NULL;
+       }
 
-       s = *ptr;
+       ZERO_STRUCTP(result);
 
-       /* default to simple separators */
-       if (!sep) sep = " \t\n\r";
+       result->pw_uid = pw->pw_uid;
+       result->pw_gid = pw->pw_gid;
 
-       /* 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;
-               }
+       /* strings */
+       if ((result->pw_name =   list_strdup(list, pw->pw_name)) == NULL ||
+           (result->pw_passwd = list_strdup(list, pw->pw_passwd)) == NULL ||
+           (result->pw_gecos =  list_strdup(list, pw->pw_gecos)) == NULL ||
+           (result->pw_dir =    list_strdup(list, pw->pw_dir)) == NULL ||
+           (result->pw_shell =  list_strdup(list, pw->pw_shell)) == NULL) {
+               return NULL;
        }
        
-       *ptr = (*s) ? s+1 : s;  
-       *buff = 0;
-       
-       return(True);
+       return result;
 }
 
-static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
+
+/*
+  fill a struct group from a winbindd_pw struct, using memory from a mem_list
+*/
+static struct group *fill_grent(struct mem_list **list, struct winbindd_gr *gr, char *gr_mem)
 {
-       fstring name;
        int i;
        char *tst;
        struct group *result;
-       
-       if (!(result = malloc(sizeof(struct group))))
-               goto out;
+       char *name, *p;
 
-       ZERO_STRUCTP(result);
-
-       /* Group name */
-
-       if ((result->gr_name = malloc(strlen(gr->gr_name) + 1)) == NULL)
-               goto out;
-
-       strcpy(result->gr_name, gr->gr_name);
-
-       /* Password */
-
-       if ((result->gr_passwd = malloc(strlen(gr->gr_passwd) + 1)) == NULL)
-               goto out;
-
-       strcpy(result->gr_passwd, gr->gr_passwd);
+       if (!(result = list_alloc(list, sizeof(struct group)))) {
+               return NULL;
+       }
 
-       /* gid */
+       ZERO_STRUCTP(result);
 
        result->gr_gid = gr->gr_gid;
 
-       /* Group membership */
+       /* Group name */
+       if ((result->gr_name = list_strdup(list, gr->gr_name)) == NULL ||
+           (result->gr_passwd = list_strdup(list, gr->gr_passwd)) == NULL) {
+               return NULL;
+       }
 
+       /* Group membership */
        if ((gr->num_gr_mem < 0) || !gr_mem) {
                gr->num_gr_mem = 0;
        }
        
        if (gr->num_gr_mem == 0) {
-
-               /* Group is empty */
-               
-               *(result->gr_mem) = NULL;
+               /* Group is empty */            
                return result;
        }
        
-       if ((tst = malloc(((gr->num_gr_mem + 1) * sizeof(char *)))) == NULL)
-               goto out;
+       tst = list_alloc(list, (gr->num_gr_mem + 1) * sizeof(char *));
+       if (!tst) {
+               return NULL;
+       }
                
        result->gr_mem = (char **)tst;
 
        /* Start looking at extra data */
-
-       i = 0;
-
-       while(next_token((char **)&gr_mem, name, ",", sizeof(fstring))) {
-        
-               /* Allocate space for member */
-        
-               if (((result->gr_mem)[i] = 
-                    malloc(strlen(name) + 1)) == NULL) {
-                       for ( i -= 1; i >= 0; i--)
-                               SAFE_FREE((result->gr_mem)[i]);
-                       goto out;
-
-               }        
-        
-               strcpy((result->gr_mem)[i], name);
+       i=0;
+       for (name = strtok_r(gr_mem, ",", &p); 
+            name; 
+            name = strtok_r(NULL, ",", &p)) {
+               if (i >= gr->num_gr_mem) {
+                       return NULL;
+               }
+               (result->gr_mem)[i] = list_strdup(list, name);
+               if ((result->gr_mem)[i] == NULL) {
+                       return NULL;
+               }
                i++;
        }
 
@@ -206,140 +213,123 @@ static struct group *fill_grent(struct winbindd_gr *gr, char *gr_mem)
        (result->gr_mem)[i] = NULL;
 
        return result;
-       
-       /* A memory allocation failed, undo succesfull allocations and
-           return NULL */
-
-out:
-       errno = ENOMEM;
-       SAFE_FREE(tst);
-       SAFE_FREE(result->gr_passwd);
-       SAFE_FREE(result->gr_name);
-       SAFE_FREE(result);
-
-       return NULL;
 }
 
 
 
-static struct group *
-wb_aix_getgrgid (gid_t gid)
+/* take a group id and return a filled struct group */ 
+static struct group *wb_aix_getgrgid(gid_t gid)
 {
-/* take a group id and return a filled struct group */
-       
+       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
+       struct group *grp;
        NSS_STATUS ret;
 
+       list_destory(&list);
+
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
        
        request.data.gid = gid;
 
        ret = winbindd_request(WINBINDD_GETGRGID, &request, &response);
-       
-       if (ret == NSS_STATUS_SUCCESS) {
-               return fill_grent(&response.data.gr, response.extra_data);
-       } else if (ret == NSS_STATUS_NOTFOUND) {
-               errno = ENOENT;
-       } else {
-               errno = EIO;
-       }
-       
-       return NULL;    
+
+       HANDLE_ERRORS(ret);
+
+       grp = fill_grent(&list, &response.data.gr, response.extra_data);
+
+       free_response(&response);
+
+       return grp;
 }
 
-static struct group *
-wb_aix_getgrnam (const char *name)
-{
 /* take a group name and return a filled struct group */
-
+static struct group *wb_aix_getgrnam(const char *name)
+{
+       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
-       
+       struct group *grp;
+
+       list_destory(&list);
+
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
 
-       strncpy(request.data.groupname, name, 
-               sizeof(request.data.groupname));
-       request.data.groupname
-               [sizeof(request.data.groupname) - 1] = '\0';
+       if (strlen(name)+1 > sizeof(request.data.groupname)) {
+               errno = EINVAL;
+               return NULL;
+       }
+       strcpy(request.data.groupname, name);
 
        ret = winbindd_request(WINBINDD_GETGRNAM, &request, &response);
        
-       if (ret == NSS_STATUS_SUCCESS) {
-               return fill_grent(&response.data.gr, response.extra_data);
-       } else if (ret == NSS_STATUS_NOTFOUND) {
-               errno = ENOENT;
-       } else {
-               errno = EIO;
-       }
-       
-       return NULL;
+       HANDLE_ERRORS(ret);
+
+       grp = fill_grent(&list, &response.data.gr, response.extra_data);
+
+       free_response(&response);
+
+       return grp;
 }
 
-static char *
-wb_aix_getgrset (const char *user)
+
+/* take a username and return a string containing a comma-separated
+   list of group id numbers to which the user belongs */
+static char *wb_aix_getgrset(char *user)
 {
-/*     take a username and return a string containing a comma-separated list of 
-       group id numbers to which the user belongs */
-       
+       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
+       int i, idx;
+       char *tmpbuf;
+       int num_gids;
+       gid_t *gid_list;
 
-       strncpy(request.data.username, user, 
-               sizeof(request.data.username) - 1);
-       request.data.username
-               [sizeof(request.data.username) - 1] = '\0';
+       list_destory(&list);
+
+       if (strlen(user)+1 > sizeof(request.data.username)) {
+               errno = EINVAL;
+               return NULL;
+       }
+       strcpy(request.data.username, user);
 
        ret = winbindd_request(WINBINDD_GETGROUPS, &request, &response);
-       if (ret == NSS_STATUS_SUCCESS ) {
-               int i, idx = 0;
-               char *tmpbuf, *result;
-               
-               int num_gids = response.data.num_entries;
-               gid_t *gid_list = (gid_t *)response.extra_data;
 
+       HANDLE_ERRORS(ret);
+
+       num_gids = response.data.num_entries;
+       gid_list = (gid_t *)response.extra_data;
                
-               /* allocate a space large enough to contruct the string */
-               if (!(tmpbuf = malloc(num_gids*12))) {
-                       errno = ENOMEM;
-                       return NULL;
-               }
-               idx += sprintf(tmpbuf, "%d", gid_list[0]);
-               for (i = 1; i < num_gids; i++) {
-                       tmpbuf[idx++] = ',';
-                       idx += sprintf(tmpbuf+idx, "%d", gid_list[i]);  
-               }
-               tmpbuf[idx] = '\0';
-               if (!(result = malloc(idx+1))) {
-                       /*      allocate a string the right size to return, but
-                               if that fails may as well return our working buffer
-                               because it contains the same thing */
-                       return tmpbuf;
-               }
-               strcpy(result, tmpbuf);
-               SAFE_FREE(tmpbuf);
-               return result;
-       } else if (ret == NSS_STATUS_NOTFOUND) {
-               errno = ENOENT;
-       } else {
-               errno = EIO;
+       /* allocate a space large enough to contruct the string */
+       tmpbuf = list_alloc(&list, num_gids*12);
+       if (!tmpbuf) {
+               return NULL;
        }
-       
-       return NULL;
+
+       for (idx=i=0; i < num_gids-1; i++) {
+               idx += sprintf(tmpbuf+idx, "%u,", gid_list[i]); 
+       }
+       idx += sprintf(tmpbuf+idx, "%u", gid_list[i]);  
+
+       free_response(&response);
+
+       return tmpbuf;
 }
 
-static struct passwd *
-wb_aix_getpwuid (uid_t uid)
+
+/* take a uid and return a filled struct passwd */     
+static struct passwd *wb_aix_getpwuid(uid_t uid)
 {
-/* take a uid and return a filled struct passwd */
-       
+       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
+
+       list_destory(&list);
        
        ZERO_STRUCT(response);
        ZERO_STRUCT(request);
@@ -347,55 +337,46 @@ wb_aix_getpwuid (uid_t uid)
        request.data.uid = uid;
        
        ret = winbindd_request(WINBINDD_GETPWUID, &request, &response);
-               
-       if (ret == NSS_STATUS_SUCCESS) {
-               return fill_pwent(&response.data.pw);
-       } else if (ret == NSS_STATUS_NOTFOUND ) {
-               errno = ENOENT;
-       } else {
-               errno = EIO;
-       }
-       
-       return NULL;
+
+       HANDLE_ERRORS(ret);
+
+       return fill_pwent(&list, &response.data.pw);
 }
 
-static struct passwd *
-wb_aix_getpwnam (const char *name)
-{
-/* take a username and return a filled struct passwd */
 
+/* take a username and return a filled struct passwd */
+static struct passwd *wb_aix_getpwnam(const char *name)
+{
+       static struct mem_list *list;
        struct winbindd_response response;
        struct winbindd_request request;
        NSS_STATUS ret;
+
+       list_destory(&list);
        
        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';
+       if (strlen(name)+1 > sizeof(request.data.username)) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       strcpy(request.data.username, name);
 
        ret = winbindd_request(WINBINDD_GETPWNAM, &request, &response);
+
+       HANDLE_ERRORS(ret);
        
-       if (ret == NSS_STATUS_SUCCESS ) {
-               return fill_pwent(&response.data.pw);
-       } else if (ret == NSS_STATUS_NOTFOUND) {
-               errno = ENOENT;
-       } else {
-               errno = EIO;    
-       }
-       
-       return NULL;
+       return fill_pwent(&list, &response.data.pw);
 }
 
-int
-wb_aix_init (struct secmethod_table *methods)
+int wb_aix_init(struct secmethod_table *methods)
 {
-       memset(methods, 0, sizeof(*methods));
+       ZERO_STRUCTP(methods);
 
        /* identification methods, this is the minimum requried for a
-       working module */
+          working module */
     
        methods->method_getgrgid = wb_aix_getgrgid;
        methods->method_getgrnam = wb_aix_getgrnam;
@@ -405,3 +386,4 @@ wb_aix_init (struct secmethod_table *methods)
 
        return AUTH_SUCCESS;
 }
+