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