2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) by Tim Potter 2000-2002
7 Copyright (C) Andrew Tridgell 2002
8 Copyright (C) Jelmer Vernooij 2003
9 Copyright (C) Volker Lendecke 2004
10 Copyright (C) Andrew Bartlett 2010
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "../../nsswitch/libwbclient/wbc_async.h"
31 * This is the main event loop of winbind requests. It goes through a
32 * state-machine of 3 read/write requests, 4 if you have extra data to send.
34 * An idle winbind client has a read request of 4 bytes outstanding,
35 * finalizing function is request_len_recv, checking the length. request_recv
36 * then processes the packet. The processing function then at some point has
37 * to call request_finished which schedules sending the response.
40 static void winbind_client_request_read(struct tevent_req *req);
41 static void winbind_client_response_written(struct tevent_req *req);
43 void request_finished(struct winbindd_cli_state *state)
45 struct tevent_req *req;
47 TALLOC_FREE(state->request);
49 req = wb_resp_write_send(state, winbind_event_context(),
50 state->out_queue, state->sock,
53 DEBUG(10,("request_finished[%d:%s]: wb_resp_write_send() failed\n",
54 (int)state->pid, state->cmd_name));
55 winbindd_remove_client(state);
58 tevent_req_set_callback(req, winbind_client_response_written, state);
61 static void winbind_client_response_written(struct tevent_req *req)
63 struct winbindd_cli_state *state = tevent_req_callback_data(
64 req, struct winbindd_cli_state);
68 ret = wb_resp_write_recv(req, &err);
73 DEBUG(2, ("Could not write response[%d:%s] to client: %s\n",
74 (int)state->pid, state->cmd_name, strerror(err)));
75 winbindd_remove_client(state);
79 DEBUG(10,("winbind_client_response_written[%d:%s]: delivered response "
80 "to client\n", (int)state->pid, state->cmd_name));
82 TALLOC_FREE(state->mem_ctx);
83 state->response = NULL;
84 state->cmd_name = "no request";
85 state->recv_fn = NULL;
87 req = wb_req_read_send(state, winbind_event_context(), state->sock,
88 WINBINDD_MAX_EXTRA_DATA);
90 winbindd_remove_client(state);
93 tevent_req_set_callback(req, winbind_client_request_read, state);
96 /* Process a new connection by adding it to the client connection list */
98 static void new_connection(int listen_sock, bool privileged)
100 struct sockaddr_un sunaddr;
101 struct winbindd_cli_state *state;
102 struct tevent_req *req;
106 /* Accept connection */
108 len = sizeof(sunaddr);
111 sock = accept(listen_sock, (struct sockaddr *)(void *)&sunaddr,
113 } while (sock == -1 && errno == EINTR);
118 DEBUG(6,("accepted socket %d\n", sock));
120 /* Create new connection structure */
122 if ((state = TALLOC_ZERO_P(NULL, struct winbindd_cli_state)) == NULL) {
129 state->out_queue = tevent_queue_create(state, "winbind client reply");
130 if (state->out_queue == NULL) {
136 state->last_access = time(NULL);
138 state->privileged = privileged;
140 req = wb_req_read_send(state, winbind_event_context(), state->sock,
141 WINBINDD_MAX_EXTRA_DATA);
147 tevent_req_set_callback(req, winbind_client_request_read, state);
149 /* Add to connection list */
151 winbindd_add_client(state);
154 static void winbind_client_request_read(struct tevent_req *req)
156 struct winbindd_cli_state *state = tevent_req_callback_data(
157 req, struct winbindd_cli_state);
161 ret = wb_req_read_recv(req, state, &state->request, &err);
165 DEBUG(6, ("closing socket %d, client exited\n",
168 DEBUG(2, ("Could not read client request from fd %d: "
169 "%s\n", state->sock, strerror(err)));
173 winbindd_remove_client(state);
176 wb_process_request(state);
179 /* Remove a client connection from client connection list */
181 void winbindd_remove_client(struct winbindd_cli_state *state)
186 /* It's a dead client - hold a funeral */
192 if (state->sock != -1) {
193 /* tell client, we are closing ... */
194 nwritten = write(state->sock, &c, sizeof(c));
195 if (nwritten == -1) {
196 DEBUG(2, ("final write to client failed: %s\n",
206 TALLOC_FREE(state->mem_ctx);
208 /* Remove from list and free */
210 winbindd_remove_client_from_list(state);
214 /* Is a client idle? */
216 bool client_is_idle(struct winbindd_cli_state *state) {
217 return (state->response == NULL &&
218 !state->pwent_state && !state->grent_state);
221 /* Shutdown client connection which has been idle for the longest time */
223 static bool remove_idle_client(void)
225 struct winbindd_cli_state *state, *remove_state = NULL;
226 time_t last_access = 0;
229 for (state = winbindd_client_list(); state; state = state->next) {
230 if (client_is_idle(state)) {
232 if (!last_access || state->last_access < last_access) {
233 last_access = state->last_access;
234 remove_state = state;
240 DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
241 nidle, remove_state->sock, (unsigned int)remove_state->pid));
242 winbindd_remove_client(remove_state);
249 struct winbindd_listen_state {
254 static void winbindd_listen_fde_handler(struct tevent_context *ev,
255 struct tevent_fd *fde,
259 struct winbindd_listen_state *s = talloc_get_type_abort(private_data,
260 struct winbindd_listen_state);
262 while (winbindd_num_clients() > lp_winbind_max_clients() - 1) {
263 DEBUG(5,("winbindd: Exceeding %d client "
264 "connections, removing idle "
265 "connection.\n", lp_winbind_max_clients()));
266 if (!remove_idle_client()) {
267 DEBUG(0,("winbindd: Exceeding %d "
268 "client connections, no idle "
269 "connection found\n",
270 lp_winbind_max_clients()));
274 new_connection(s->fd, s->privileged);
278 * Winbindd socket accessor functions
281 const char *get_winbind_pipe_dir(void)
283 return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
286 char *get_winbind_priv_pipe_dir(void)
288 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
291 bool winbindd_setup_listeners(void)
293 struct winbindd_listen_state *pub_state = NULL;
294 struct winbindd_listen_state *priv_state = NULL;
295 struct tevent_fd *fde;
297 pub_state = talloc(winbind_event_context(),
298 struct winbindd_listen_state);
303 pub_state->privileged = false;
304 pub_state->fd = create_pipe_sock(
305 get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
306 if (pub_state->fd == -1) {
310 fde = tevent_add_fd(winbind_event_context(), pub_state, pub_state->fd,
311 TEVENT_FD_READ, winbindd_listen_fde_handler,
314 close(pub_state->fd);
317 tevent_fd_set_auto_close(fde);
319 priv_state = talloc(winbind_event_context(),
320 struct winbindd_listen_state);
325 priv_state->privileged = true;
326 priv_state->fd = create_pipe_sock(
327 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
328 if (priv_state->fd == -1) {
332 fde = tevent_add_fd(winbind_event_context(), priv_state,
333 priv_state->fd, TEVENT_FD_READ,
334 winbindd_listen_fde_handler, priv_state);
336 close(priv_state->fd);
339 tevent_fd_set_auto_close(fde);
343 TALLOC_FREE(pub_state);
344 TALLOC_FREE(priv_state);