From 42b5b381871dd935aeda34669a2c03a05a63f5f0 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Thu, 4 Mar 2010 03:05:06 +0300 Subject: s4:winbind Implement logic for getgroups to work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is called by the system everytime we do a id user or when we do wbinfo -r Signed-off-by: Matthias Dieter Wallnöfer --- source4/winbind/config.mk | 1 + source4/winbind/wb_cmd_getgroups.c | 215 +++++++++++++++++++++++++++++++++++++ source4/winbind/wb_samba3_cmd.c | 74 ++++++++++--- 3 files changed, 273 insertions(+), 17 deletions(-) create mode 100644 source4/winbind/wb_cmd_getgroups.c diff --git a/source4/winbind/config.mk b/source4/winbind/config.mk index 45164d349a..0bee89c93b 100644 --- a/source4/winbind/config.mk +++ b/source4/winbind/config.mk @@ -52,6 +52,7 @@ WINBIND_OBJ_FILES = $(addprefix $(winbindsrcdir)/, \ wb_cmd_getpwent.o \ wb_cmd_getgrent.o \ wb_cmd_setgrent.o \ + wb_cmd_getgroups.o \ wb_pam_auth.o \ wb_sam_logon.o) diff --git a/source4/winbind/wb_cmd_getgroups.c b/source4/winbind/wb_cmd_getgroups.c new file mode 100644 index 0000000000..de70b0b911 --- /dev/null +++ b/source4/winbind/wb_cmd_getgroups.c @@ -0,0 +1,215 @@ +/* + Unix SMB/CIFS implementation. + + Backend for getgroups + + Copyright (C) Matthieu Patou 2010 + + 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 3 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, see . +*/ + +#include "includes.h" +#include "libcli/composite/composite.h" +#include "winbind/wb_server.h" +#include "smbd/service_task.h" +#include "libcli/security/dom_sid.h" + +struct cmd_getgroups_state { + struct composite_context *ctx; + struct wbsrv_service *service; + char* username; + uint32_t num_groups; + uint32_t current_group; + struct dom_sid **sids; + + gid_t *gids; +}; + +/* The idea is to get the groups for a user + We receive one user from this we search for his uid + From the uid we search for his SID + From the SID we search for the list of groups + And with the list of groups we search for each group its gid +*/ +static void cmd_getgroups_recv_pwnam(struct composite_context *ctx); +static void wb_getgroups_uid2sid_recv(struct composite_context *ctx); +static void wb_getgroups_userdomsgroups_recv(struct composite_context *ctx); +static void cmd_getgroups_recv_gid(struct composite_context *ctx); + +/* + Ask for the uid from the username +*/ +struct composite_context *wb_cmd_getgroups_send(TALLOC_CTX *mem_ctx, + struct wbsrv_service *service, + const char* username) +{ + struct composite_context *ctx, *result; + struct cmd_getgroups_state *state; + + DEBUG(5, ("wb_cmd_getgroups_send called\n")); + + result = composite_create(mem_ctx, service->task->event_ctx); + if (!result) return NULL; + + state = talloc(mem_ctx, struct cmd_getgroups_state); + if (composite_nomem(state, result)) return result; + + state->ctx = result; + result->private_data = state; + state->service = service; + state->num_groups = 0; + + state->username = talloc_strdup(state,username); + if (composite_nomem(ctx, result)) return result; + + ctx = wb_cmd_getpwnam_send(state, service, username); + if (composite_nomem(ctx, result)) return result; + + composite_continue(result, ctx, cmd_getgroups_recv_pwnam, state); + return result; +} + +/* + Receive the uid and send request for SID +*/ +static void cmd_getgroups_recv_pwnam(struct composite_context *ctx) +{ + struct composite_context *res; + struct cmd_getgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getgroups_state); + struct winbindd_pw *pw; + struct wbsrv_service *service = state->service; + + DEBUG(5, ("cmd_getgroups_recv_pwnam called\n")); + + state->ctx->status = wb_cmd_getpwnam_recv(ctx, state, &pw); + if (composite_is_ok(state->ctx)) { + res = wb_uid2sid_send(state, service, pw->pw_uid); + NT_STATUS_HAVE_NO_MEMORY(res); + DEBUG(6, ("cmd_getgroups_recv_pwnam uid %d\n",pw->pw_uid)); + + composite_continue(ctx, res, wb_getgroups_uid2sid_recv, state); + } +} + +/* + Receive the SID and request groups through the userdomgroups helper +*/ +static void wb_getgroups_uid2sid_recv(struct composite_context *ctx) +{ + struct composite_context *res; + struct cmd_getgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getgroups_state); + NTSTATUS status; + struct dom_sid *sid; + char *sid_str; + + DEBUG(5, ("wb_getgroups_uid2sid_recv called\n")); + + status = wb_uid2sid_recv(ctx, state, &sid); + if(NT_STATUS_IS_OK(status)) { + sid_str = dom_sid_string(state, sid); + + /* If the conversion failed, bail out with a failure. */ + if (sid_str != NULL) { + DEBUG(7, ("wb_getgroups_uid2sid_recv SID = %s\n",sid_str)); + /* Ok got the SID now get the groups */ + res = wb_cmd_userdomgroups_send(state, state->service, sid); + NT_STATUS_HAVE_NO_MEMORY(res); + + composite_continue(ctx, res, wb_getgroups_userdomsgroups_recv, state); + } else { + composite_error(state->ctx, NT_STATUS_UNSUCCESSFUL); + } + } +} + +/* + Receive groups and search for uid for the first group +*/ +static void wb_getgroups_userdomsgroups_recv(struct composite_context *ctx) { + struct cmd_getgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getgroups_state); + int num_sids; + struct dom_sid **sids; + + DEBUG(5, ("wb_getgroups_userdomsgroups_recv called\n")); + state->ctx->status = wb_cmd_userdomgroups_recv(ctx,state,&num_sids,&sids); + if (!composite_is_ok(state->ctx)) return; + + DEBUG(5, ("wb_getgroups_userdomsgroups_recv %d groups\n",num_sids)); + + state->sids=sids; + state->num_groups=num_sids; + state->current_group=0; + + if(num_sids > 0) { + state->gids = talloc_array(state, struct gid_t *, state->num_groups); + ctx = wb_sid2gid_send(state, state->service, state->sids[state->current_group]); + composite_continue(state->ctx, ctx, cmd_getgroups_recv_gid, state); + } else { + composite_done(state->ctx); + } +} + +/* + Receive and uid the previous searched group and request the uid for the next one +*/ +static void cmd_getgroups_recv_gid(struct composite_context *ctx) +{ + struct cmd_getgroups_state *state = + talloc_get_type(ctx->async.private_data, + struct cmd_getgroups_state); + gid_t gid; + char* sid_str; + + DEBUG(5, ("cmd_getgroups_recv_gid called\n")); + + state->ctx->status = wb_sid2gid_recv(ctx, &gid); + if(!composite_is_ok(state->ctx)) return; + + state->gids[state->current_group] = gid; + DEBUG(5, ("cmd_getgroups_recv_gid group %d \n",state->current_group)); + + state->current_group++; + if(state->current_group < state->num_groups ) { + ctx = wb_sid2gid_send(state, state->service, state->sids[state->current_group]); + composite_continue(state->ctx, ctx, cmd_getgroups_recv_gid, state); + } else { + composite_done(state->ctx); + } +} + +/* + Return list of uids when finished +*/ +NTSTATUS wb_cmd_getgroups_recv(struct composite_context *ctx,TALLOC_CTX *mem_ctx,gid_t **groups,uint32_t *num_groups) +{ + NTSTATUS status = composite_wait(ctx); + + DEBUG(5, ("wb_cmd_getgroups_recv called\n")); + + if (NT_STATUS_IS_OK(status)) { + struct cmd_getgroups_state *state = + talloc_get_type(ctx->private_data, + struct cmd_getgroups_state); + *groups = talloc_steal(mem_ctx, state->gids); + *num_groups = state->num_groups; + } + talloc_free(ctx); + return status; +} diff --git a/source4/winbind/wb_samba3_cmd.c b/source4/winbind/wb_samba3_cmd.c index 2c846c4f15..99980d27a7 100644 --- a/source4/winbind/wb_samba3_cmd.c +++ b/source4/winbind/wb_samba3_cmd.c @@ -1183,31 +1183,51 @@ static void getgrgid_recv(struct composite_context *ctx) wbsrv_samba3_async_epilogue(status, s3call); } +static void getgroups_recv(struct composite_context *ctx); + NTSTATUS wbsrv_samba3_getgroups(struct wbsrv_samba3_call *s3call) { + struct composite_context *ctx; + struct wbsrv_service *service = s3call->wbconn->listen_socket->service; + DEBUG(5, ("wbsrv_samba3_getgroups called\n")); - s3call->response.result = WINBINDD_ERROR; + /* S3 code do the same so why not ... */ + s3call->request.data.username[sizeof(s3call->request.data.username)-1]='\0'; + ctx = wb_cmd_getgroups_send(s3call, service, s3call->request.data.username); + NT_STATUS_HAVE_NO_MEMORY(ctx); + + ctx->async.fn = getgroups_recv; + ctx->async.private_data = s3call; + s3call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC; return NT_STATUS_OK; } -static void setgrent_recv(struct composite_context *ctx) +static void getgroups_recv(struct composite_context *ctx) { struct wbsrv_samba3_call *s3call = talloc_get_type(ctx->async.private_data, struct wbsrv_samba3_call); + gid_t *gids; + uint32_t num_groups; NTSTATUS status; - struct wbsrv_grent *grent; + DEBUG(5, ("getgroups_recv called\n")); - DEBUG(5, ("setpwent_recv called\n")); - - status = wb_cmd_setgrent_recv(ctx, s3call->wbconn, &grent); + status = wb_cmd_getgroups_recv(ctx, s3call, &gids, &num_groups); if (NT_STATUS_IS_OK(status)) { - s3call->wbconn->protocol_private_data = grent; + uint32_t extra_len = sizeof(gid_t) * num_groups; + + s3call->response.data.num_entries = num_groups; + s3call->response.extra_data.data = gids; + s3call->response.length += extra_len; + } else { + s3call->response.result = WINBINDD_ERROR; } wbsrv_samba3_async_epilogue(status, s3call); } +static void setgrent_recv(struct composite_context *ctx); + NTSTATUS wbsrv_samba3_setgrent(struct wbsrv_samba3_call *s3call) { struct composite_context *ctx; @@ -1224,29 +1244,26 @@ NTSTATUS wbsrv_samba3_setgrent(struct wbsrv_samba3_call *s3call) return NT_STATUS_OK; } -static void getgrent_recv(struct composite_context *ctx) +static void setgrent_recv(struct composite_context *ctx) { struct wbsrv_samba3_call *s3call = talloc_get_type(ctx->async.private_data, struct wbsrv_samba3_call); NTSTATUS status; - struct winbindd_gr *gr; - uint32_t num_groups; + struct wbsrv_grent *grent; - DEBUG(5, ("getgrent_recv called\n")); + DEBUG(5, ("setpwent_recv called\n")); - status = wb_cmd_getgrent_recv(ctx, s3call, &gr, &num_groups); + status = wb_cmd_setgrent_recv(ctx, s3call->wbconn, &grent); if (NT_STATUS_IS_OK(status)) { - uint32_t extra_len = sizeof(struct winbindd_gr) * num_groups; - - s3call->response.data.num_entries = num_groups; - s3call->response.extra_data.data = gr; - s3call->response.length += extra_len; + s3call->wbconn->protocol_private_data = grent; } wbsrv_samba3_async_epilogue(status, s3call); } +static void getgrent_recv(struct composite_context *ctx); + NTSTATUS wbsrv_samba3_getgrent(struct wbsrv_samba3_call *s3call) { struct composite_context *ctx; @@ -1271,6 +1288,29 @@ NTSTATUS wbsrv_samba3_getgrent(struct wbsrv_samba3_call *s3call) return NT_STATUS_OK; } +static void getgrent_recv(struct composite_context *ctx) +{ + struct wbsrv_samba3_call *s3call = + talloc_get_type(ctx->async.private_data, + struct wbsrv_samba3_call); + NTSTATUS status; + struct winbindd_gr *gr; + uint32_t num_groups; + + DEBUG(5, ("getgrent_recv called\n")); + + status = wb_cmd_getgrent_recv(ctx, s3call, &gr, &num_groups); + if (NT_STATUS_IS_OK(status)) { + uint32_t extra_len = sizeof(struct winbindd_gr) * num_groups; + + s3call->response.data.num_entries = num_groups; + s3call->response.extra_data.data = gr; + s3call->response.length += extra_len; + } + + wbsrv_samba3_async_epilogue(status, s3call); +} + NTSTATUS wbsrv_samba3_endgrent(struct wbsrv_samba3_call *s3call) { DEBUG(5, ("wbsrv_samba3_endgrent called\n")); -- cgit