r12608: Remove some unused #include lines.
[ira/wip.git] / source4 / winbind / wb_server.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Main winbindd server routines
4
5    Copyright (C) Stefan Metzmacher      2005
6    Copyright (C) Andrew Tridgell        2005
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "lib/socket/socket.h"
25 #include "dlinklist.h"
26 #include "lib/events/events.h"
27 #include "smbd/service_task.h"
28 #include "smbd/service_stream.h"
29 #include "nsswitch/winbind_nss_config.h"
30 #include "winbind/wb_server.h"
31
32 void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
33 {
34         stream_terminate_connection(wbconn->conn, reason);
35 }
36
37 /*
38   called when we get a new connection
39 */
40 static void wbsrv_accept(struct stream_connection *conn)
41 {
42         struct wbsrv_listen_socket *listen_socket =
43                 talloc_get_type(conn->private, struct wbsrv_listen_socket);
44         struct wbsrv_connection *wbconn;
45
46         wbconn = talloc_zero(conn, struct wbsrv_connection);
47         if (!wbconn) {
48                 stream_terminate_connection(conn,
49                                             "wbsrv_accept: out of memory");
50                 return;
51         }
52         wbconn->conn            = conn;
53         wbconn->listen_socket   = listen_socket;
54         conn->private = wbconn;
55 }
56
57 /*
58   receive some data on a winbind connection
59 */
60 static void wbsrv_recv(struct stream_connection *conn, uint16_t flags)
61 {
62         struct wbsrv_connection *wbconn =
63                 talloc_get_type(conn->private, struct wbsrv_connection);
64         const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
65         struct wbsrv_call *call;
66         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
67         size_t nread;
68
69         /* avoid recursion, because of half async code */
70         if (wbconn->processing) {
71                 EVENT_FD_NOT_READABLE(conn->event.fde);
72                 return;
73         }
74
75         /* if the used protocol doesn't support pending requests disallow
76          * them */
77         if (wbconn->pending_calls && !ops->allow_pending_calls) {
78                 EVENT_FD_NOT_READABLE(conn->event.fde);
79                 return;
80         }
81
82         if (wbconn->partial.length == 0) {
83                 wbconn->partial = data_blob_talloc(wbconn, NULL, 4);
84                 if (!wbconn->partial.data) goto nomem;
85
86                 wbconn->partial_read = 0;
87         }
88
89         /* read in the packet length */
90         if (wbconn->partial_read < 4) {
91                 uint32_t packet_length;
92
93                 status = socket_recv(conn->socket, 
94                                      wbconn->partial.data+wbconn->partial_read,
95                                      4 - wbconn->partial_read,
96                                      &nread, 0);
97                 if (NT_STATUS_IS_ERR(status)) goto failed;
98                 if (!NT_STATUS_IS_OK(status)) return;
99
100                 wbconn->partial_read += nread;
101                 if (wbconn->partial_read != 4) return;
102
103                 packet_length = ops->packet_length(wbconn->partial);
104
105                 wbconn->partial.data =
106                         talloc_realloc(wbconn, wbconn->partial.data, uint8_t,
107                                        packet_length);
108                 if (!wbconn->partial.data) goto nomem;
109
110                 wbconn->partial.length = packet_length;
111         }
112
113         /* read in the body */
114         status = socket_recv(conn->socket, 
115                              wbconn->partial.data + wbconn->partial_read,
116                              wbconn->partial.length - wbconn->partial_read,
117                              &nread, 0);
118         if (NT_STATUS_IS_ERR(status)) goto failed;
119         if (!NT_STATUS_IS_OK(status)) return;
120
121         wbconn->partial_read += nread;
122         if (wbconn->partial_read != wbconn->partial.length) return;
123
124         /* we have a full request - parse it */
125         status = ops->pull_request(wbconn->partial, wbconn, &call);
126         if (!NT_STATUS_IS_OK(status)) goto failed;
127         call->wbconn    = wbconn;
128         call->event_ctx = conn->event.ctx;
129
130         /*
131          * we have parsed the request, so we can reset the
132          * wbconn->partial_read, maybe we could also free wbconn->partial, but
133          * for now we keep it, and overwrite it the next time
134          */
135         wbconn->partial_read = 0;
136
137         /* actually process the request */
138         wbconn->pending_calls++;
139         wbconn->processing = True;
140         status = ops->handle_call(call);
141         wbconn->processing = False;
142         if (!NT_STATUS_IS_OK(status)) goto failed;
143
144         /* if the backend want to reply later just return here */
145         if (call->flags & WBSRV_CALL_FLAGS_REPLY_ASYNC) {
146                 return;
147         }
148
149         /*
150          * and queue the reply, this implies talloc_free(call),
151          * and set the socket to readable again
152          */
153         status = wbsrv_send_reply(call);
154         if (!NT_STATUS_IS_OK(status)) goto failed;
155
156         return;
157 nomem:
158         status = NT_STATUS_NO_MEMORY;
159 failed:
160         wbsrv_terminate_connection(wbconn, nt_errstr(status));
161 }
162
163 /*
164  * queue a wbsrv_call reply on a wbsrv_connection
165  * NOTE: that this implies talloc_free(call),
166  *       use talloc_reference(call) if you need it after
167  *       calling wbsrv_queue_reply
168  * NOTE: if this function desn't return NT_STATUS_OK,
169  *       the caller needs to call
170  *           wbsrv_terminate_connection(call->wbconn, "reason...");
171  *           return;
172  *       to drop the connection
173  */
174 NTSTATUS wbsrv_send_reply(struct wbsrv_call *call)
175 {
176         struct wbsrv_connection *wbconn = call->wbconn;
177         const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
178         struct data_blob_list_item *rep;
179         NTSTATUS status;
180
181         /* and now encode the reply */
182         rep = talloc(wbconn, struct data_blob_list_item);
183         NT_STATUS_HAVE_NO_MEMORY(rep);
184
185         status = ops->push_reply(call, rep, &rep->blob);
186         NT_STATUS_NOT_OK_RETURN(status);
187
188         if (!wbconn->send_queue) {
189                 EVENT_FD_WRITEABLE(wbconn->conn->event.fde);
190         }
191         DLIST_ADD_END(wbconn->send_queue, rep, struct data_blob_list_item *);
192
193         EVENT_FD_READABLE(wbconn->conn->event.fde);
194
195         /* the call isn't needed any more */
196         wbconn->pending_calls--;
197         talloc_free(call);
198         return NT_STATUS_OK;
199 }
200
201 /*
202   called when we can write to a connection
203 */
204 static void wbsrv_send(struct stream_connection *conn, uint16_t flags)
205 {
206         struct wbsrv_connection *wbconn = talloc_get_type(conn->private, struct wbsrv_connection);
207         NTSTATUS status;
208
209         while (wbconn->send_queue) {
210                 struct data_blob_list_item *q = wbconn->send_queue;
211                 size_t sendlen;
212
213                 status = socket_send(conn->socket, &q->blob, &sendlen, 0);
214                 if (NT_STATUS_IS_ERR(status)) goto failed;
215                 if (!NT_STATUS_IS_OK(status)) return;
216
217                 q->blob.length -= sendlen;
218                 q->blob.data   += sendlen;
219
220                 if (q->blob.length == 0) {
221                         DLIST_REMOVE(wbconn->send_queue, q);
222                         talloc_free(q);
223                 }
224         }
225
226         EVENT_FD_NOT_WRITEABLE(conn->event.fde);
227         return;
228 failed:
229         wbsrv_terminate_connection(wbconn, nt_errstr(status));
230 }
231
232 static const struct stream_server_ops wbsrv_ops = {
233         .name                   = "winbind",
234         .accept_connection      = wbsrv_accept,
235         .recv_handler           = wbsrv_recv,
236         .send_handler           = wbsrv_send
237 };
238
239 static const struct wbsrv_protocol_ops wbsrv_samba3_protocol_ops = {
240         .name                   = "winbind samba3 protocol",
241         .allow_pending_calls    = False,
242         .packet_length          = wbsrv_samba3_packet_length,
243         .pull_request           = wbsrv_samba3_pull_request,
244         .handle_call            = wbsrv_samba3_handle_call,
245         .push_reply             = wbsrv_samba3_push_reply
246 };
247
248 /*
249   startup the winbind task
250 */
251 static void winbind_task_init(struct task_server *task)
252 {
253         uint16_t port = 1;
254         const struct model_ops *model_ops;
255         NTSTATUS status;
256         struct wbsrv_service *service;
257         struct wbsrv_listen_socket *listen_socket;
258
259         /* within the winbind task we want to be a single process, so
260            ask for the single process model ops and pass these to the
261            stream_setup_socket() call. */
262         model_ops = process_model_byname("single");
263         if (!model_ops) {
264                 task_server_terminate(task,
265                                       "Can't find 'single' process model_ops");
266                 return;
267         }
268
269         /* Make sure the directory for NCALRPC exists */
270         if (!directory_exist(WINBINDD_DIR)) {
271                 mkdir(WINBINDD_DIR, 0755);
272         }
273
274         service = talloc_zero(task, struct wbsrv_service);
275         if (!service) goto nomem;
276         service->task   = task;
277
278         service->primary_sid = secrets_get_domain_sid(service,
279                                                       lp_workgroup());
280         if (service->primary_sid == NULL) {
281                 task_server_terminate(
282                         task, nt_errstr(NT_STATUS_CANT_ACCESS_DOMAIN_INFO));
283                 return;
284         }
285
286         /* setup the unprivileged samba3 socket */
287         listen_socket = talloc(service, struct wbsrv_listen_socket);
288         if (!listen_socket) goto nomem;
289         listen_socket->socket_path      = WINBINDD_SAMBA3_SOCKET;
290         if (!listen_socket->socket_path) goto nomem;
291         listen_socket->service          = service;
292         listen_socket->privileged       = False;
293         listen_socket->ops              = &wbsrv_samba3_protocol_ops;
294         status = stream_setup_socket(task->event_ctx, model_ops,
295                                      &wbsrv_ops, "unix",
296                                      listen_socket->socket_path, &port,
297                                      listen_socket);
298         if (!NT_STATUS_IS_OK(status)) goto listen_failed;
299
300         /* setup the privileged samba3 socket */
301         listen_socket = talloc(service, struct wbsrv_listen_socket);
302         if (!listen_socket) goto nomem;
303         listen_socket->socket_path      =
304                 smbd_tmp_path(listen_socket,
305                               WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
306         if (!listen_socket->socket_path) goto nomem;
307         listen_socket->service          = service;
308         listen_socket->privileged       = True;
309         listen_socket->ops              = &wbsrv_samba3_protocol_ops;
310         status = stream_setup_socket(task->event_ctx, model_ops,
311                                      &wbsrv_ops, "unix",
312                                      listen_socket->socket_path, &port,
313                                      listen_socket);
314         if (!NT_STATUS_IS_OK(status)) goto listen_failed;
315
316         return;
317
318 listen_failed:
319         DEBUG(0,("stream_setup_socket(path=%s) failed - %s\n",
320                  listen_socket->socket_path, nt_errstr(status)));
321         task_server_terminate(task, nt_errstr(status));
322         return;
323 nomem:
324         task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY));
325         return;
326 }
327
328 /*
329   initialise the winbind server
330  */
331 static NTSTATUS winbind_init(struct event_context *event_ctx,
332                              const struct model_ops *model_ops)
333 {
334         return task_server_startup(event_ctx, model_ops, winbind_task_init);
335 }
336
337 /*
338   register ourselves as a available server
339 */
340 NTSTATUS server_service_winbind_init(void)
341 {
342         return register_server_service("winbind", winbind_init);
343 }