netlogon: Implement SendToSam along with its winbind forwarding
[samba.git] / source3 / winbindd / winbindd_irpc.c
1 /*
2    Unix SMB/CIFS implementation.
3    async implementation of commands submitted over IRPC
4    Copyright (C) Volker Lendecke 2009
5    Copyright (C) Guenther Deschner 2009
6    Copyright (C) Andrew Bartlett 2014
7    Copyright (C) Andrew Tridgell 2009
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "winbindd.h"
25 #include "librpc/gen_ndr/ndr_winbind_c.h"
26 #include "source4/lib/messaging/irpc.h"
27 #include "librpc/gen_ndr/ndr_winbind.h"
28
29 struct wb_irpc_forward_state {
30         struct irpc_message *msg;
31         struct winbind_DsrUpdateReadOnlyServerDnsRecords *req;
32
33         const char *opname;
34         struct dcesrv_call_state *dce_call;
35 };
36
37 /*
38   called when the forwarded rpc request is finished
39  */
40 static void wb_irpc_forward_callback(struct tevent_req *subreq)
41 {
42         struct wb_irpc_forward_state *st =
43                 tevent_req_callback_data(subreq,
44                 struct wb_irpc_forward_state);
45         const char *opname = st->opname;
46         NTSTATUS status;
47
48         status = dcerpc_binding_handle_call_recv(subreq);
49         TALLOC_FREE(subreq);
50         if (!NT_STATUS_IS_OK(status)) {
51                 DEBUG(0,("RPC callback failed for %s - %s\n",
52                          opname, nt_errstr(status)));
53                 irpc_send_reply(st->msg, status);
54                 return;
55         }
56
57         irpc_send_reply(st->msg, status);
58 }
59
60
61
62 /**
63  * Forward a RPC call using IRPC to another task
64  */
65
66 static NTSTATUS wb_irpc_forward_rpc_call(struct irpc_message *msg, TALLOC_CTX *mem_ctx,
67                                          struct tevent_context *ev,
68                                          void *r, uint32_t callid,
69                                          const char *opname,
70                                          struct winbindd_domain *domain,
71                                          uint32_t timeout)
72 {
73         struct wb_irpc_forward_state *st;
74         struct dcerpc_binding_handle *binding_handle;
75         struct tevent_req *subreq;
76
77         st = talloc(mem_ctx, struct wb_irpc_forward_state);
78         if (st == NULL) {
79                 return NT_STATUS_NO_MEMORY;
80         }
81
82         st->msg = msg;
83         st->opname   = opname;
84
85         binding_handle =  dom_child_handle(domain);
86         if (binding_handle == NULL) {
87                 DEBUG(0,("%s: Failed to forward request to winbind handler for %s\n",
88                          opname, domain->name));
89                 return NT_STATUS_UNSUCCESSFUL;
90         }
91
92         /* reset timeout for the handle */
93         dcerpc_binding_handle_set_timeout(binding_handle, timeout);
94
95         /* forward the call */
96         subreq = dcerpc_binding_handle_call_send(st, ev,
97                                                  binding_handle,
98                                                  NULL, &ndr_table_winbind,
99                                                  callid,
100                                                  msg, r);
101         if (subreq == NULL) {
102                 DEBUG(0,("%s: Failed to forward request to winbind handler for %s\n",
103                          opname, domain->name));
104                 return NT_STATUS_UNSUCCESSFUL;
105         }
106
107         /* mark the request as replied async */
108         msg->defer_reply = true;
109
110         /* setup the callback */
111         tevent_req_set_callback(subreq, wb_irpc_forward_callback, st);
112         return NT_STATUS_OK;
113 }
114
115 static NTSTATUS wb_irpc_DsrUpdateReadOnlyServerDnsRecords(struct irpc_message *msg,
116                                                    struct winbind_DsrUpdateReadOnlyServerDnsRecords *req)
117 {
118         struct winbindd_domain *domain = find_our_domain();
119         if (domain == NULL) {
120                 return NT_STATUS_NO_SUCH_DOMAIN;
121         }
122
123         DEBUG(5, ("wb_irpc_DsrUpdateReadOnlyServerDnsRecords called\n"));
124
125         return wb_irpc_forward_rpc_call(msg, msg,
126                                         winbind_event_context(),
127                                         req, NDR_WINBIND_DSRUPDATEREADONLYSERVERDNSRECORDS,
128                                         "winbind_DsrUpdateReadOnlyServerDnsRecords",
129                                         domain, IRPC_CALL_TIMEOUT);
130 }
131
132 static NTSTATUS wb_irpc_SamLogon(struct irpc_message *msg,
133                                  struct winbind_SamLogon *req)
134 {
135         struct winbindd_domain *domain;
136         const char *target_domain_name;
137         if (req->in.logon.network == NULL) {
138                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
139         }
140         target_domain_name = req->in.logon.network->identity_info.domain_name.string;
141
142         domain = find_auth_domain(0, target_domain_name);
143         if (domain == NULL) {
144                 req->out.result = NT_STATUS_NO_SUCH_USER;
145                 req->out.authoritative = 0;
146                 return NT_STATUS_OK;
147         }
148
149         DEBUG(5, ("wb_irpc_SamLogon called\n"));
150
151         return wb_irpc_forward_rpc_call(msg, msg,
152                                         winbind_event_context(),
153                                         req, NDR_WINBIND_SAMLOGON,
154                                         "winbind_SamLogon",
155                                         domain, IRPC_CALL_TIMEOUT);
156 }
157
158 static NTSTATUS wb_irpc_LogonControl(struct irpc_message *msg,
159                                      struct winbind_LogonControl *req)
160 {
161         TALLOC_CTX *frame = talloc_stackframe();
162         char *domain_name = NULL;
163         struct winbindd_domain *domain = NULL;
164
165         DEBUG(5, ("wb_irpc_LogonControl called\n"));
166
167         switch (req->in.function_code) {
168         case NETLOGON_CONTROL_REDISCOVER:
169         case NETLOGON_CONTROL_TC_QUERY:
170         case NETLOGON_CONTROL_CHANGE_PASSWORD:
171         case NETLOGON_CONTROL_TC_VERIFY:
172                 if (req->in.data->domain == NULL) {
173                         TALLOC_FREE(frame);
174                         return NT_STATUS_INVALID_PARAMETER;
175                 }
176
177                 domain_name = talloc_strdup(frame, req->in.data->domain);
178                 if (domain_name == NULL) {
179                         req->out.result = WERR_NOT_ENOUGH_MEMORY;
180                         TALLOC_FREE(frame);
181                         return NT_STATUS_OK;
182                 }
183
184                 break;
185         default:
186                 TALLOC_FREE(frame);
187                 return NT_STATUS_NOT_IMPLEMENTED;
188         }
189
190         if (req->in.function_code == NETLOGON_CONTROL_REDISCOVER) {
191                 char *p = NULL;
192
193                 /*
194                  * NETLOGON_CONTROL_REDISCOVER
195                  * get's an optional \dcname appended to the domain name
196                  */
197                 p = strchr_m(domain_name, '\\');
198                 if (p != NULL) {
199                         *p = '\0';
200                 }
201         }
202
203         domain = find_domain_from_name_noinit(domain_name);
204         if (domain == NULL) {
205                 req->out.result = WERR_NO_SUCH_DOMAIN;
206                 TALLOC_FREE(frame);
207                 return NT_STATUS_OK;
208         }
209
210         TALLOC_FREE(frame);
211         return wb_irpc_forward_rpc_call(msg, msg,
212                                         winbind_event_context(),
213                                         req, NDR_WINBIND_LOGONCONTROL,
214                                         "winbind_LogonControl",
215                                         domain, 45 /* timeout */);
216 }
217
218 static NTSTATUS wb_irpc_GetForestTrustInformation(struct irpc_message *msg,
219                                      struct winbind_GetForestTrustInformation *req)
220 {
221         struct winbindd_domain *domain = NULL;
222
223         if (req->in.trusted_domain_name == NULL) {
224                 req->out.result = WERR_NO_SUCH_DOMAIN;
225                 return NT_STATUS_OK;
226         }
227
228         domain = find_domain_from_name_noinit(req->in.trusted_domain_name);
229         if (domain == NULL) {
230                 req->out.result = WERR_NO_SUCH_DOMAIN;
231                 return NT_STATUS_OK;
232         }
233
234         /*
235          * checking for domain->internal and domain->primary
236          * makes sure we only do some work when running as DC.
237          */
238
239         if (domain->internal) {
240                 req->out.result = WERR_NO_SUCH_DOMAIN;
241                 return NT_STATUS_OK;
242         }
243
244         if (domain->primary) {
245                 req->out.result = WERR_NO_SUCH_DOMAIN;
246                 return NT_STATUS_OK;
247         }
248
249         DEBUG(5, ("wb_irpc_GetForestTrustInformation called\n"));
250
251         return wb_irpc_forward_rpc_call(msg, msg,
252                                         winbind_event_context(),
253                                         req, NDR_WINBIND_GETFORESTTRUSTINFORMATION,
254                                         "winbind_GetForestTrustInformation",
255                                         domain, 45 /* timeout */);
256 }
257
258 static NTSTATUS wb_irpc_SendToSam(struct irpc_message *msg,
259                                   struct winbind_SendToSam *req)
260 {
261         /* TODO make sure that it is RWDC */
262         struct winbindd_domain *domain = find_our_domain();
263         if (domain == NULL) {
264                 return NT_STATUS_NO_SUCH_DOMAIN;
265         }
266
267         DEBUG(5, ("wb_irpc_SendToSam called\n"));
268
269         return wb_irpc_forward_rpc_call(msg, msg,
270                                         winbind_event_context(),
271                                         req, NDR_WINBIND_SENDTOSAM,
272                                         "winbind_SendToSam",
273                                         domain, IRPC_CALL_TIMEOUT);
274 }
275
276 NTSTATUS wb_irpc_register(void)
277 {
278         NTSTATUS status;
279
280         status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_DSRUPDATEREADONLYSERVERDNSRECORDS,
281                                wb_irpc_DsrUpdateReadOnlyServerDnsRecords, NULL);
282         if (!NT_STATUS_IS_OK(status)) {
283                 return status;
284         }
285         status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_SAMLOGON,
286                                wb_irpc_SamLogon, NULL);
287         if (!NT_STATUS_IS_OK(status)) {
288                 return status;
289         }
290         status = IRPC_REGISTER(winbind_imessaging_context(), winbind,
291                                WINBIND_LOGONCONTROL,
292                                wb_irpc_LogonControl, NULL);
293         if (!NT_STATUS_IS_OK(status)) {
294                 return status;
295         }
296         status = IRPC_REGISTER(winbind_imessaging_context(), winbind,
297                                WINBIND_GETFORESTTRUSTINFORMATION,
298                                wb_irpc_GetForestTrustInformation, NULL);
299         if (!NT_STATUS_IS_OK(status)) {
300                 return status;
301         }
302         status = IRPC_REGISTER(winbind_imessaging_context(), winbind, WINBIND_SENDTOSAM,
303                                wb_irpc_SendToSam, NULL);
304         if (!NT_STATUS_IS_OK(status)) {
305                 return status;
306         }
307
308         return NT_STATUS_OK;
309 }