Line data Source code
1 : /*
2 : * Unix SMB/CIFS implementation.
3 : *
4 : * RPC Socket Helper
5 : *
6 : * Copyright (c) 2011 Andreas Schneider <asn@samba.org>
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 3 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, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "ntdomain.h"
24 :
25 : #include "../lib/tsocket/tsocket.h"
26 : #include "librpc/rpc/dcesrv_core.h"
27 : #include "rpc_server/rpc_sock_helper.h"
28 : #include "librpc/ndr/ndr_table.h"
29 :
30 : #undef DBGC_CLASS
31 : #define DBGC_CLASS DBGC_RPC_SRV
32 :
33 1523 : static NTSTATUS dcesrv_create_ncacn_np_socket(
34 : struct dcerpc_binding *b, int *out_fd)
35 : {
36 1523 : char *np_dir = NULL;
37 1523 : int fd = -1;
38 0 : NTSTATUS status;
39 0 : const char *endpoint;
40 1523 : char *endpoint_normalized = NULL;
41 1523 : char *p = NULL;
42 :
43 1523 : endpoint = dcerpc_binding_get_string_option(b, "endpoint");
44 1523 : if (endpoint == NULL) {
45 0 : DBG_ERR("Endpoint mandatory for named pipes\n");
46 0 : return NT_STATUS_INVALID_PARAMETER;
47 : }
48 :
49 : /* The endpoint string from IDL can be mixed uppercase and case is
50 : * normalized by smbd on connection */
51 1523 : endpoint_normalized = strlower_talloc(talloc_tos(), endpoint);
52 1523 : if (endpoint_normalized == NULL) {
53 0 : return NT_STATUS_NO_MEMORY;
54 : }
55 :
56 : /* The endpoint string from IDL can be prefixed by \pipe\ */
57 1523 : p = endpoint_normalized;
58 1523 : if (strncmp(p, "\\pipe\\", 6) == 0) {
59 1523 : p += 6;
60 : }
61 :
62 : /*
63 : * As lp_ncalrpc_dir() should have 0755, but
64 : * lp_ncalrpc_dir()/np should have 0700, we need to
65 : * create lp_ncalrpc_dir() first.
66 : */
67 1523 : if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
68 0 : status = map_nt_error_from_unix_common(errno);
69 0 : DBG_ERR("Failed to create pipe directory %s - %s\n",
70 : lp_ncalrpc_dir(), strerror(errno));
71 0 : goto out;
72 : }
73 :
74 1523 : np_dir = talloc_asprintf(talloc_tos(), "%s/np", lp_ncalrpc_dir());
75 1523 : if (!np_dir) {
76 0 : status = NT_STATUS_NO_MEMORY;
77 0 : DBG_ERR("Out of memory\n");
78 0 : goto out;
79 : }
80 :
81 1523 : if (!directory_create_or_exist_strict(np_dir, geteuid(), 0700)) {
82 0 : status = map_nt_error_from_unix_common(errno);
83 0 : DBG_ERR("Failed to create pipe directory %s - %s\n",
84 : np_dir, strerror(errno));
85 0 : goto out;
86 : }
87 :
88 1523 : fd = create_pipe_sock(np_dir, p, 0700);
89 1523 : if (fd == -1) {
90 0 : status = map_nt_error_from_unix_common(errno);
91 0 : DBG_ERR("Failed to create ncacn_np socket! '%s/%s': %s\n",
92 : np_dir, p, strerror(errno));
93 0 : goto out;
94 : }
95 :
96 1523 : DBG_DEBUG("Opened pipe socket fd %d for %s\n", fd, p);
97 :
98 1523 : *out_fd = fd;
99 :
100 1523 : status = NT_STATUS_OK;
101 :
102 1523 : out:
103 1523 : TALLOC_FREE(endpoint_normalized);
104 1523 : TALLOC_FREE(np_dir);
105 1523 : return status;
106 : }
107 :
108 : /********************************************************************
109 : * Start listening on the tcp/ip socket
110 : ********************************************************************/
111 :
112 160 : static NTSTATUS dcesrv_create_ncacn_ip_tcp_socket(
113 : const struct sockaddr_storage *ifss, uint16_t *port, int *out_fd)
114 : {
115 160 : int fd = -1;
116 :
117 160 : if (*port == 0) {
118 0 : static uint16_t low = 0;
119 0 : uint16_t i;
120 :
121 64 : if (low == 0) {
122 16 : low = lp_rpc_low_port();
123 : }
124 :
125 64 : for (i = low; i <= lp_rpc_high_port(); i++) {
126 64 : fd = open_socket_in(SOCK_STREAM, ifss, i, false);
127 64 : if (fd >= 0) {
128 64 : *port = i;
129 64 : low = i+1;
130 64 : break;
131 : }
132 : }
133 : } else {
134 96 : fd = open_socket_in(SOCK_STREAM, ifss, *port, true);
135 : }
136 :
137 160 : if (fd < 0) {
138 0 : DBG_ERR("Failed to create socket on port %u!\n", *port);
139 0 : return map_nt_error_from_unix(-fd);
140 : }
141 :
142 : /* ready to listen */
143 160 : set_socket_options(fd, "SO_KEEPALIVE");
144 160 : set_socket_options(fd, lp_socket_options());
145 :
146 160 : DBG_DEBUG("Opened ncacn_ip_tcp socket fd %d for port %u\n", fd, *port);
147 :
148 160 : *out_fd = fd;
149 :
150 160 : return NT_STATUS_OK;
151 : }
152 :
153 80 : static NTSTATUS dcesrv_create_ncacn_ip_tcp_sockets(
154 : struct dcerpc_binding *b,
155 : TALLOC_CTX *mem_ctx,
156 : size_t *pnum_fds,
157 : int **pfds)
158 : {
159 80 : uint16_t port = 0;
160 0 : char port_str[11];
161 80 : const char *endpoint = NULL;
162 80 : size_t i = 0, num_fds;
163 80 : int *fds = NULL;
164 80 : struct samba_sockaddr *addrs = NULL;
165 80 : NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
166 0 : bool ok;
167 :
168 80 : endpoint = dcerpc_binding_get_string_option(b, "endpoint");
169 80 : if (endpoint != NULL) {
170 16 : port = atoi(endpoint);
171 : }
172 :
173 80 : if (lp_interfaces() && lp_bind_interfaces_only()) {
174 80 : num_fds = iface_count();
175 : } else {
176 0 : num_fds = 1;
177 : #ifdef HAVE_IPV6
178 0 : num_fds += 1;
179 : #endif
180 : }
181 :
182 80 : addrs = talloc_array(mem_ctx, struct samba_sockaddr, num_fds);
183 80 : if (addrs == NULL) {
184 0 : status = NT_STATUS_NO_MEMORY;
185 0 : goto fail;
186 : }
187 80 : fds = talloc_array(mem_ctx, int, num_fds);
188 80 : if (fds == NULL) {
189 0 : status = NT_STATUS_NO_MEMORY;
190 0 : goto fail;
191 : }
192 :
193 : /*
194 : * Fill "addrs"
195 : */
196 :
197 105 : if (lp_interfaces() && lp_bind_interfaces_only()) {
198 240 : for (i=0; i<num_fds; i++) {
199 0 : const struct sockaddr_storage *ifss =
200 160 : iface_n_sockaddr_storage(i);
201 :
202 160 : ok = sockaddr_storage_to_samba_sockaddr(
203 160 : &addrs[i], ifss);
204 160 : if (!ok) {
205 0 : i = 0; /* nothing to close */
206 0 : goto fail;
207 : }
208 : }
209 : } else {
210 0 : struct sockaddr_storage ss = { .ss_family = 0 };
211 :
212 : #ifdef HAVE_IPV6
213 0 : ok = interpret_string_addr(
214 : &ss, "::", AI_NUMERICHOST|AI_PASSIVE);
215 0 : if (!ok) {
216 0 : goto fail;
217 : }
218 0 : ok = sockaddr_storage_to_samba_sockaddr(&addrs[0], &ss);
219 0 : if (!ok) {
220 0 : goto fail;
221 : }
222 : #endif
223 0 : ok = interpret_string_addr(
224 : &ss, "0.0.0.0", AI_NUMERICHOST|AI_PASSIVE);
225 0 : if (!ok) {
226 0 : goto fail;
227 : }
228 :
229 : /* num_fds set above depending on HAVE_IPV6 */
230 0 : ok = sockaddr_storage_to_samba_sockaddr(
231 0 : &addrs[num_fds-1], &ss);
232 0 : if (!ok) {
233 0 : goto fail;
234 : }
235 : }
236 :
237 240 : for (i=0; i<num_fds; i++) {
238 160 : status = dcesrv_create_ncacn_ip_tcp_socket(
239 160 : &addrs[i].u.ss, &port, &fds[i]);
240 160 : if (!NT_STATUS_IS_OK(status)) {
241 0 : goto fail;
242 : }
243 160 : samba_sockaddr_set_port(&addrs[i], port);
244 : }
245 :
246 : /* Set the port in the endpoint */
247 80 : snprintf(port_str, sizeof(port_str), "%"PRIu16, port);
248 :
249 80 : status = dcerpc_binding_set_string_option(b, "endpoint", port_str);
250 80 : if (!NT_STATUS_IS_OK(status)) {
251 0 : DBG_ERR("Failed to set binding endpoint '%s': %s\n",
252 : port_str, nt_errstr(status));
253 0 : goto fail;
254 : }
255 :
256 80 : TALLOC_FREE(addrs);
257 :
258 80 : *pfds = fds;
259 80 : *pnum_fds = num_fds;
260 :
261 80 : return NT_STATUS_OK;
262 :
263 0 : fail:
264 0 : while (i > 0) {
265 0 : close(fds[i-1]);
266 0 : i -= 1;
267 : }
268 0 : TALLOC_FREE(fds);
269 0 : TALLOC_FREE(addrs);
270 0 : return status;
271 : }
272 :
273 : /********************************************************************
274 : * Start listening on the ncalrpc socket
275 : ********************************************************************/
276 :
277 112 : static NTSTATUS dcesrv_create_ncalrpc_socket(
278 : struct dcerpc_binding *b, int *out_fd)
279 : {
280 112 : int fd = -1;
281 112 : const char *endpoint = NULL;
282 0 : NTSTATUS status;
283 :
284 112 : endpoint = dcerpc_binding_get_string_option(b, "endpoint");
285 112 : if (endpoint == NULL) {
286 : /*
287 : * No identifier specified: use DEFAULT or SMBD.
288 : *
289 : * When role is AD DC we run two rpc server instances, the one
290 : * started by 'samba' and the one embedded in 'smbd'.
291 : * Avoid listening in DEFAULT socket for NCALRPC as both
292 : * servers will race to accept connections. In this case smbd
293 : * will listen in SMBD socket and rpcint binding handle
294 : * implementation will pick the right socket to use.
295 : *
296 : * TODO: DO NOT hardcode this value anywhere else. Rather,
297 : * specify no endpoint and let the epmapper worry about it.
298 : */
299 0 : if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
300 0 : endpoint = "SMBD";
301 : } else {
302 0 : endpoint = "DEFAULT";
303 : }
304 0 : status = dcerpc_binding_set_string_option(
305 : b, "endpoint", endpoint);
306 0 : if (!NT_STATUS_IS_OK(status)) {
307 0 : DBG_ERR("Failed to set ncalrpc 'endpoint' binding "
308 : "string option to '%s': %s\n",
309 : endpoint, nt_errstr(status));
310 0 : return status;
311 : }
312 : }
313 :
314 112 : if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
315 0 : status = map_nt_error_from_unix_common(errno);
316 0 : DBG_ERR("Failed to create ncalrpc directory '%s': %s\n",
317 : lp_ncalrpc_dir(), strerror(errno));
318 0 : goto out;
319 : }
320 :
321 112 : fd = create_pipe_sock(lp_ncalrpc_dir(), endpoint, 0755);
322 112 : if (fd == -1) {
323 0 : status = map_nt_error_from_unix_common(errno);
324 0 : DBG_ERR("Failed to create ncalrpc socket '%s/%s': %s\n",
325 : lp_ncalrpc_dir(), endpoint, strerror(errno));
326 0 : goto out;
327 : }
328 :
329 112 : DBG_DEBUG("Opened ncalrpc socket fd '%d' for '%s/%s'\n",
330 : fd, lp_ncalrpc_dir(), endpoint);
331 :
332 112 : *out_fd = fd;
333 :
334 112 : return NT_STATUS_OK;
335 :
336 0 : out:
337 0 : return status;
338 : }
339 :
340 1731 : NTSTATUS dcesrv_create_binding_sockets(
341 : struct dcerpc_binding *b,
342 : TALLOC_CTX *mem_ctx,
343 : size_t *pnum_fds,
344 : int **pfds)
345 : {
346 1731 : enum dcerpc_transport_t transport = dcerpc_binding_get_transport(b);
347 1731 : size_t i, num_fds = 1;
348 1731 : int *fds = NULL;
349 0 : NTSTATUS status;
350 :
351 1731 : if ((transport == NCALRPC) || (transport == NCACN_NP)) {
352 1635 : fds = talloc(mem_ctx, int);
353 1635 : if (fds == NULL) {
354 0 : return NT_STATUS_NO_MEMORY;
355 : }
356 : }
357 :
358 1731 : switch(transport) {
359 112 : case NCALRPC:
360 112 : status = dcesrv_create_ncalrpc_socket(b, fds);
361 112 : break;
362 1523 : case NCACN_NP:
363 1523 : status = dcesrv_create_ncacn_np_socket(b, fds);
364 1523 : break;
365 80 : case NCACN_IP_TCP:
366 80 : status = dcesrv_create_ncacn_ip_tcp_sockets(
367 : b, talloc_tos(), &num_fds, &fds);
368 80 : break;
369 16 : default:
370 16 : status = NT_STATUS_NOT_SUPPORTED;
371 16 : break;
372 : }
373 :
374 1731 : if (!NT_STATUS_IS_OK(status)) {
375 16 : TALLOC_FREE(fds);
376 16 : return status;
377 : }
378 :
379 3510 : for (i=0; i<num_fds; i++) {
380 1795 : bool ok = smb_set_close_on_exec(fds[i]);
381 1795 : if (!ok) {
382 0 : status = map_nt_error_from_unix(errno);
383 0 : break;
384 : }
385 : }
386 1715 : if (i < num_fds) {
387 0 : for (i=0; i<num_fds; i++) {
388 0 : close(fds[i]);
389 : }
390 0 : TALLOC_FREE(fds);
391 0 : return status;
392 : }
393 :
394 1715 : *pfds = fds;
395 1715 : *pnum_fds = num_fds;
396 1715 : return NT_STATUS_OK;
397 : }
398 :
399 : /* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
|