libwbclient: Implement wbcGetgrent and wbcGetgrlist
[samba.git] / source3 / nsswitch / libwbclient / wbc_pwd.c
index 5f7437b18896c22674f6c8f9da61d7f95d6df888..d54a5af4fc5b9fd927ac06eb1beef58b8d2de189 100644 (file)
 
 #include "libwbclient.h"
 
+/** @brief The maximum number of pwent structs to get from winbindd
+ *
+ */
+#define MAX_GETPWENT_USERS 500
+
+/** @brief The maximum number of grent structs to get from winbindd
+ *
+ */
+#define MAX_GETGRENT_GROUPS 500
+
 /**
  *
  **/
@@ -97,11 +107,13 @@ static struct group *copy_group_entry(struct winbindd_gr *g,
                grp->gr_mem[i] = talloc_strdup(grp, mem_p);
                BAIL_ON_PTR_ERROR(grp->gr_mem[i], wbc_status);
 
-               *mem_q = ',';
-               mem_p++;
-               mem_p = mem_q;
+               if (mem_q == NULL) {
+                       i += 1;
+                       break;
+               }
+               mem_p = mem_q + 1;
        }
-       grp->gr_mem[g->num_gr_mem] = NULL;
+       grp->gr_mem[i] = NULL;
 
        wbc_status = WBC_ERR_SUCCESS;
 
@@ -209,16 +221,16 @@ wbcErr wbcGetgrnam(const char *name, struct group **grp)
        struct winbindd_request request;
        struct winbindd_response response;
 
-       if (!name || !grp) {
-               wbc_status = WBC_ERR_INVALID_PARAM;
-               BAIL_ON_WBC_ERROR(wbc_status);
-       }
-
        /* Initialize request */
 
        ZERO_STRUCT(request);
        ZERO_STRUCT(response);
 
+       if (!name || !grp) {
+               wbc_status = WBC_ERR_INVALID_PARAM;
+               BAIL_ON_WBC_ERROR(wbc_status);
+       }
+
        /* dst is already null terminated from the memset above */
 
        strncpy(request.data.groupname, name, sizeof(request.data.groupname)-1);
@@ -228,14 +240,14 @@ wbcErr wbcGetgrnam(const char *name, struct group **grp)
                                        &response);
        BAIL_ON_WBC_ERROR(wbc_status);
 
-       *grp = copy_group_entry(&response.data.gr, 
+       *grp = copy_group_entry(&response.data.gr,
                                (char*)response.extra_data.data);
        BAIL_ON_PTR_ERROR(*grp, wbc_status);
 
  done:
        if (response.extra_data.data)
                free(response.extra_data.data);
-       
+
        return wbc_status;
 }
 
@@ -254,16 +266,16 @@ wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
        struct winbindd_request request;
        struct winbindd_response response;
 
-       if (!grp) {
-               wbc_status = WBC_ERR_INVALID_PARAM;
-               BAIL_ON_WBC_ERROR(wbc_status);
-       }
-
        /* Initialize request */
 
        ZERO_STRUCT(request);
        ZERO_STRUCT(response);
 
+       if (!grp) {
+               wbc_status = WBC_ERR_INVALID_PARAM;
+               BAIL_ON_WBC_ERROR(wbc_status);
+       }
+
        request.data.gid = gid;
 
        wbc_status = wbcRequestResponse(WINBINDD_GETGRGID,
@@ -271,7 +283,7 @@ wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
                                        &response);
        BAIL_ON_WBC_ERROR(wbc_status);
 
-       *grp = copy_group_entry(&response.data.gr, 
+       *grp = copy_group_entry(&response.data.gr,
                                (char*)response.extra_data.data);
        BAIL_ON_PTR_ERROR(*grp, wbc_status);
 
@@ -282,6 +294,21 @@ wbcErr wbcGetgrgid(gid_t gid, struct group **grp)
        return wbc_status;
 }
 
+/** @brief Number of cached passwd structs
+ *
+ */
+static uint32_t pw_cache_size;
+
+/** @brief Position of the pwent context
+ *
+ */
+static uint32_t pw_cache_idx;
+
+/** @brief Winbindd response containing the passwd structs
+ *
+ */
+static struct winbindd_response pw_response;
+
 /** @brief Reset the passwd iterator
  *
  * @return #wbcErr
@@ -291,6 +318,15 @@ wbcErr wbcSetpwent(void)
 {
        wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
 
+       if (pw_cache_size > 0) {
+               pw_cache_idx = pw_cache_size = 0;
+               if (pw_response.extra_data.data) {
+                       free(pw_response.extra_data.data);
+               }
+       }
+
+       ZERO_STRUCT(pw_response);
+
        wbc_status = wbcRequestResponse(WINBINDD_SETPWENT,
                                        NULL, NULL);
        BAIL_ON_WBC_ERROR(wbc_status);
@@ -308,6 +344,13 @@ wbcErr wbcEndpwent(void)
 {
        wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
 
+       if (pw_cache_size > 0) {
+               pw_cache_idx = pw_cache_size = 0;
+               if (pw_response.extra_data.data) {
+                       free(pw_response.extra_data.data);
+               }
+       }
+
        wbc_status = wbcRequestResponse(WINBINDD_ENDPWENT,
                                        NULL, NULL);
        BAIL_ON_WBC_ERROR(wbc_status);
@@ -318,16 +361,70 @@ wbcErr wbcEndpwent(void)
 
 /** @brief Return the next struct passwd* entry from the pwent iterator
  *
- * @param **pwd       Pointer to resulting struct group* from the query.
+ * @param **pwd       Pointer to resulting struct passwd* from the query.
  *
  * @return #wbcErr
  **/
 
 wbcErr wbcGetpwent(struct passwd **pwd)
 {
-       return WBC_ERR_NOT_IMPLEMENTED;
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct winbindd_request request;
+       struct winbindd_pw *wb_pw;
+
+       /* If there's a cached result, return that. */
+       if (pw_cache_idx < pw_cache_size) {
+               goto return_result;
+       }
+
+       /* Otherwise, query winbindd for some entries. */
+
+       pw_cache_idx = 0;
+
+       if (pw_response.extra_data.data) {
+               free(pw_response.extra_data.data);
+               ZERO_STRUCT(pw_response);
+       }
+
+       ZERO_STRUCT(request);
+       request.data.num_entries = MAX_GETPWENT_USERS;
+
+       wbc_status = wbcRequestResponse(WINBINDD_GETPWENT, &request,
+                                       &pw_response);
+
+       BAIL_ON_WBC_ERROR(wbc_status);
+
+       pw_cache_size = pw_response.data.num_entries;
+
+return_result:
+
+       wb_pw = (struct winbindd_pw *) pw_response.extra_data.data;
+
+       *pwd = copy_passwd_entry(&wb_pw[pw_cache_idx]);
+
+       BAIL_ON_PTR_ERROR(*pwd, wbc_status);
+
+       pw_cache_idx++;
+
+done:
+       return wbc_status;
 }
 
+/** @brief Number of cached group structs
+ *
+ */
+static uint32_t gr_cache_size;
+
+/** @brief Position of the grent context
+ *
+ */
+static uint32_t gr_cache_idx;
+
+/** @brief Winbindd response containing the group structs
+ *
+ */
+static struct winbindd_response gr_response;
+
 /** @brief Reset the group iterator
  *
  * @return #wbcErr
@@ -337,6 +434,15 @@ wbcErr wbcSetgrent(void)
 {
        wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
 
+       if (gr_cache_size > 0) {
+               gr_cache_idx = gr_cache_size = 0;
+               if (gr_response.extra_data.data) {
+                       free(gr_response.extra_data.data);
+               }
+       }
+
+       ZERO_STRUCT(gr_response);
+
        wbc_status = wbcRequestResponse(WINBINDD_SETGRENT,
                                        NULL, NULL);
        BAIL_ON_WBC_ERROR(wbc_status);
@@ -354,6 +460,13 @@ wbcErr wbcEndgrent(void)
 {
        wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
 
+       if (gr_cache_size > 0) {
+               gr_cache_idx = gr_cache_size = 0;
+               if (gr_response.extra_data.data) {
+                       free(gr_response.extra_data.data);
+               }
+       }
+
        wbc_status = wbcRequestResponse(WINBINDD_ENDGRENT,
                                        NULL, NULL);
        BAIL_ON_WBC_ERROR(wbc_status);
@@ -362,7 +475,7 @@ wbcErr wbcEndgrent(void)
        return wbc_status;
 }
 
-/** @brief Return the next struct passwd* entry from the pwent iterator
+/** @brief Return the next struct group* entry from the pwent iterator
  *
  * @param **grp       Pointer to resulting struct group* from the query.
  *
@@ -371,6 +484,163 @@ wbcErr wbcEndgrent(void)
 
 wbcErr wbcGetgrent(struct group **grp)
 {
-       return WBC_ERR_NOT_IMPLEMENTED;
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct winbindd_request request;
+       struct winbindd_gr *wb_gr;
+       uint32_t mem_ofs;
+
+       /* If there's a cached result, return that. */
+       if (gr_cache_idx < gr_cache_size) {
+               goto return_result;
+       }
+
+       /* Otherwise, query winbindd for some entries. */
+
+       gr_cache_idx = 0;
+
+       if (gr_response.extra_data.data) {
+               free(gr_response.extra_data.data);
+               ZERO_STRUCT(gr_response);
+       }
+
+       ZERO_STRUCT(request);
+       request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+       wbc_status = wbcRequestResponse(WINBINDD_GETGRENT, &request,
+                                       &gr_response);
+
+       BAIL_ON_WBC_ERROR(wbc_status);
+
+       gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+       wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+       mem_ofs = wb_gr[gr_cache_idx].gr_mem_ofs +
+                 gr_cache_size * sizeof(struct winbindd_gr);
+
+       *grp = copy_group_entry(&wb_gr[gr_cache_idx],
+                               ((char *)gr_response.extra_data.data)+mem_ofs);
+
+       BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+       gr_cache_idx++;
+
+done:
+       return wbc_status;
+}
+
+/** @brief Return the next struct group* entry from the pwent iterator
+ *
+ * This is similar to #wbcGetgrent, just that the member list is empty
+ *
+ * @param **grp       Pointer to resulting struct group* from the query.
+ *
+ * @return #wbcErr
+ **/
+
+wbcErr wbcGetgrlist(struct group **grp)
+{
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct winbindd_request request;
+       struct winbindd_gr *wb_gr;
+
+       /* If there's a cached result, return that. */
+       if (gr_cache_idx < gr_cache_size) {
+               goto return_result;
+       }
+
+       /* Otherwise, query winbindd for some entries. */
+
+       gr_cache_idx = 0;
+
+       if (gr_response.extra_data.data) {
+               free(gr_response.extra_data.data);
+               ZERO_STRUCT(gr_response);
+       }
+
+       ZERO_STRUCT(request);
+       request.data.num_entries = MAX_GETGRENT_GROUPS;
+
+       wbc_status = wbcRequestResponse(WINBINDD_GETGRLST, &request,
+                                       &gr_response);
+
+       BAIL_ON_WBC_ERROR(wbc_status);
+
+       gr_cache_size = gr_response.data.num_entries;
+
+return_result:
+
+       wb_gr = (struct winbindd_gr *) gr_response.extra_data.data;
+
+       *grp = copy_group_entry(&wb_gr[gr_cache_idx], NULL);
+
+       BAIL_ON_PTR_ERROR(*grp, wbc_status);
+
+       gr_cache_idx++;
+
+done:
+       return wbc_status;
 }
 
+/** @brief Return the unix group array belonging to the given user
+ *
+ * @param *account       The given user name
+ * @param *num_groups    Number of elements returned in the groups array
+ * @param **_groups      Pointer to resulting gid_t array.
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcGetGroups(const char *account,
+                   uint32_t *num_groups,
+                   gid_t **_groups)
+{
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct winbindd_request request;
+       struct winbindd_response response;
+       uint32_t i;
+       gid_t *groups = NULL;
+
+       /* Initialize request */
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       if (!account) {
+               wbc_status = WBC_ERR_INVALID_PARAM;
+               BAIL_ON_WBC_ERROR(wbc_status);
+       }
+
+       /* Send request */
+
+       strncpy(request.data.username, account, sizeof(request.data.username)-1);
+
+       wbc_status = wbcRequestResponse(WINBINDD_GETGROUPS,
+                                       &request,
+                                       &response);
+       BAIL_ON_WBC_ERROR(wbc_status);
+
+       groups = talloc_array(NULL, gid_t, response.data.num_entries);
+       BAIL_ON_PTR_ERROR(groups, wbc_status);
+
+       for (i = 0; i < response.data.num_entries; i++) {
+               groups[i] = ((gid_t *)response.extra_data.data)[i];
+       }
+
+       *num_groups = response.data.num_entries;
+       *_groups = groups;
+       groups = NULL;
+
+       wbc_status = WBC_ERR_SUCCESS;
+
+ done:
+       if (response.extra_data.data) {
+               free(response.extra_data.data);
+       }
+       if (groups) {
+               talloc_free(groups);
+       }
+
+       return wbc_status;
+}