/src/samba/nsswitch/wb_common.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | winbind client common code |
5 | | |
6 | | Copyright (C) Tim Potter 2000 |
7 | | Copyright (C) Andrew Tridgell 2000 |
8 | | Copyright (C) Andrew Bartlett 2002 |
9 | | Copyright (C) Matthew Newton 2015 |
10 | | |
11 | | |
12 | | This library is free software; you can redistribute it and/or |
13 | | modify it under the terms of the GNU Lesser General Public |
14 | | License as published by the Free Software Foundation; either |
15 | | version 3 of the License, or (at your option) any later version. |
16 | | |
17 | | This library 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 GNU |
20 | | Library General Public License for more details. |
21 | | |
22 | | You should have received a copy of the GNU Lesser General Public License |
23 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
24 | | */ |
25 | | |
26 | | #include "replace.h" |
27 | | #include "system/select.h" |
28 | | #include "winbind_client.h" |
29 | | #include "lib/util/dlinklist.h" |
30 | | #include <assert.h> |
31 | | |
32 | | #ifdef HAVE_PTHREAD_H |
33 | | #include <pthread.h> |
34 | | #endif |
35 | | |
36 | | static __thread char client_name[32]; |
37 | | |
38 | | /* Global context */ |
39 | | |
40 | | struct winbindd_context { |
41 | | struct winbindd_context *prev, *next; |
42 | | int winbindd_fd; /* winbind file descriptor */ |
43 | | bool is_privileged; /* using the privileged socket? */ |
44 | | pid_t our_pid; /* calling process pid */ |
45 | | bool autofree; /* this is a thread global context */ |
46 | | }; |
47 | | |
48 | | static struct wb_global_ctx { |
49 | | #ifdef HAVE_PTHREAD |
50 | | pthread_once_t control; |
51 | | pthread_key_t key; |
52 | | bool key_initialized; |
53 | | #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
54 | 0 | #define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP |
55 | | #else |
56 | | #define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER |
57 | | #endif |
58 | 0 | #define WB_GLOBAL_LIST_LOCK do { \ |
59 | 0 | int __pret = pthread_mutex_lock(&wb_global_ctx.list_mutex); \ |
60 | 0 | assert(__pret == 0); \ |
61 | 0 | } while(0) |
62 | 0 | #define WB_GLOBAL_LIST_UNLOCK do { \ |
63 | 0 | int __pret = pthread_mutex_unlock(&wb_global_ctx.list_mutex); \ |
64 | 0 | assert(__pret == 0); \ |
65 | 0 | } while(0) |
66 | | pthread_mutex_t list_mutex; |
67 | | #else /* => not HAVE_PTHREAD */ |
68 | | #define WB_GLOBAL_LIST_LOCK do { } while(0) |
69 | | #define WB_GLOBAL_LIST_UNLOCK do { } while(0) |
70 | | #endif /* not HAVE_PTHREAD */ |
71 | | struct winbindd_context *list; |
72 | | } wb_global_ctx = { |
73 | | #ifdef HAVE_PTHREAD |
74 | | .control = PTHREAD_ONCE_INIT, |
75 | | .list_mutex = WB_GLOBAL_MUTEX_INITIALIZER, |
76 | | #endif |
77 | | .list = NULL, |
78 | | }; |
79 | | |
80 | | static void winbind_close_sock(struct winbindd_context *ctx); |
81 | | static void winbind_ctx_free_locked(struct winbindd_context *ctx); |
82 | | static void winbind_cleanup_list(void); |
83 | | |
84 | | #ifdef HAVE_PTHREAD |
85 | | static void wb_thread_ctx_initialize(void); |
86 | | |
87 | | static void wb_atfork_prepare(void) |
88 | 0 | { |
89 | 0 | WB_GLOBAL_LIST_LOCK; |
90 | 0 | } |
91 | | |
92 | | static void wb_atfork_parent(void) |
93 | 0 | { |
94 | 0 | WB_GLOBAL_LIST_UNLOCK; |
95 | 0 | } |
96 | | |
97 | | static void wb_atfork_child(void) |
98 | 0 | { |
99 | 0 | wb_global_ctx.list_mutex = (pthread_mutex_t)WB_GLOBAL_MUTEX_INITIALIZER; |
100 | |
|
101 | 0 | if (wb_global_ctx.key_initialized) { |
102 | 0 | int ret; |
103 | | |
104 | | /* |
105 | | * After a fork the child still believes |
106 | | * it is the same thread as in the parent. |
107 | | * So pthread_getspecific() would return the |
108 | | * value of the thread that called fork(). |
109 | | * |
110 | | * But we don't want that behavior, so |
111 | | * we just clear the reference and let |
112 | | * winbind_cleanup_list() below 'autofree' |
113 | | * the parent threads global context. |
114 | | */ |
115 | 0 | ret = pthread_setspecific(wb_global_ctx.key, NULL); |
116 | 0 | assert(ret == 0); |
117 | 0 | } |
118 | | |
119 | | /* |
120 | | * But we need to close/cleanup the global state |
121 | | * of the parents threads. |
122 | | */ |
123 | 0 | winbind_cleanup_list(); |
124 | 0 | } |
125 | | |
126 | | static void wb_thread_ctx_destructor(void *p) |
127 | 0 | { |
128 | 0 | struct winbindd_context *ctx = (struct winbindd_context *)p; |
129 | |
|
130 | 0 | winbindd_ctx_free(ctx); |
131 | 0 | } |
132 | | |
133 | | static void wb_thread_ctx_initialize(void) |
134 | 0 | { |
135 | 0 | int ret; |
136 | |
|
137 | 0 | ret = pthread_atfork(wb_atfork_prepare, |
138 | 0 | wb_atfork_parent, |
139 | 0 | wb_atfork_child); |
140 | 0 | assert(ret == 0); |
141 | |
|
142 | 0 | ret = pthread_key_create(&wb_global_ctx.key, |
143 | 0 | wb_thread_ctx_destructor); |
144 | 0 | assert(ret == 0); |
145 | |
|
146 | 0 | wb_global_ctx.key_initialized = true; |
147 | 0 | } |
148 | | |
149 | | static struct winbindd_context *get_wb_thread_ctx(void) |
150 | 0 | { |
151 | 0 | struct winbindd_context *ctx = NULL; |
152 | 0 | int ret; |
153 | |
|
154 | 0 | ret = pthread_once(&wb_global_ctx.control, |
155 | 0 | wb_thread_ctx_initialize); |
156 | 0 | assert(ret == 0); |
157 | |
|
158 | 0 | ctx = (struct winbindd_context *)pthread_getspecific( |
159 | 0 | wb_global_ctx.key); |
160 | 0 | if (ctx != NULL) { |
161 | 0 | return ctx; |
162 | 0 | } |
163 | | |
164 | 0 | ctx = malloc(sizeof(struct winbindd_context)); |
165 | 0 | if (ctx == NULL) { |
166 | 0 | return NULL; |
167 | 0 | } |
168 | | |
169 | 0 | *ctx = (struct winbindd_context) { |
170 | 0 | .winbindd_fd = -1, |
171 | 0 | .is_privileged = false, |
172 | 0 | .our_pid = 0, |
173 | 0 | .autofree = true, |
174 | 0 | }; |
175 | |
|
176 | 0 | WB_GLOBAL_LIST_LOCK; |
177 | 0 | DLIST_ADD_END(wb_global_ctx.list, ctx); |
178 | 0 | WB_GLOBAL_LIST_UNLOCK; |
179 | |
|
180 | 0 | ret = pthread_setspecific(wb_global_ctx.key, ctx); |
181 | 0 | if (ret != 0) { |
182 | 0 | free(ctx); |
183 | 0 | return NULL; |
184 | 0 | } |
185 | 0 | return ctx; |
186 | 0 | } |
187 | | #endif /* HAVE_PTHREAD */ |
188 | | |
189 | | static struct winbindd_context *get_wb_global_ctx(void) |
190 | 0 | { |
191 | 0 | struct winbindd_context *ctx = NULL; |
192 | | #ifndef HAVE_PTHREAD |
193 | | static struct winbindd_context _ctx = { |
194 | | .winbindd_fd = -1, |
195 | | .is_privileged = false, |
196 | | .our_pid = 0, |
197 | | .autofree = false, |
198 | | }; |
199 | | #endif |
200 | |
|
201 | 0 | #ifdef HAVE_PTHREAD |
202 | 0 | ctx = get_wb_thread_ctx(); |
203 | | #else |
204 | | ctx = &_ctx; |
205 | | if (ctx->prev == NULL && ctx->next == NULL) { |
206 | | DLIST_ADD_END(wb_global_ctx.list, ctx); |
207 | | } |
208 | | #endif |
209 | |
|
210 | 0 | return ctx; |
211 | 0 | } |
212 | | |
213 | | void winbind_set_client_name(const char *name) |
214 | 0 | { |
215 | 0 | if (name == NULL || strlen(name) == 0) { |
216 | 0 | return; |
217 | 0 | } |
218 | | |
219 | 0 | (void)snprintf(client_name, sizeof(client_name), "%s", name); |
220 | 0 | } |
221 | | |
222 | | static const char *winbind_get_client_name(void) |
223 | 0 | { |
224 | 0 | if (client_name[0] == '\0') { |
225 | 0 | const char *progname = getprogname(); |
226 | 0 | int len; |
227 | |
|
228 | 0 | if (progname == NULL) { |
229 | 0 | progname = "<unknown>"; |
230 | 0 | } |
231 | |
|
232 | 0 | len = snprintf(client_name, |
233 | 0 | sizeof(client_name), |
234 | 0 | "%s", |
235 | 0 | progname); |
236 | 0 | if (len <= 0) { |
237 | 0 | return progname; |
238 | 0 | } |
239 | 0 | } |
240 | | |
241 | 0 | return client_name; |
242 | 0 | } |
243 | | |
244 | | /* Initialise a request structure */ |
245 | | |
246 | | static void winbindd_init_request(struct winbindd_request *request, |
247 | | int request_type) |
248 | 0 | { |
249 | 0 | request->length = sizeof(struct winbindd_request); |
250 | |
|
251 | 0 | request->cmd = (enum winbindd_cmd)request_type; |
252 | 0 | request->pid = getpid(); |
253 | |
|
254 | 0 | (void)snprintf(request->client_name, |
255 | 0 | sizeof(request->client_name), |
256 | 0 | "%s", |
257 | 0 | winbind_get_client_name()); |
258 | 0 | } |
259 | | |
260 | | /* Initialise a response structure */ |
261 | | |
262 | | static void init_response(struct winbindd_response *response) |
263 | 0 | { |
264 | | /* Initialise return value */ |
265 | |
|
266 | 0 | response->result = WINBINDD_ERROR; |
267 | 0 | } |
268 | | |
269 | | /* Close established socket */ |
270 | | |
271 | | static void winbind_close_sock(struct winbindd_context *ctx) |
272 | 0 | { |
273 | 0 | if (!ctx) { |
274 | 0 | return; |
275 | 0 | } |
276 | | |
277 | 0 | if (ctx->winbindd_fd != -1) { |
278 | 0 | close(ctx->winbindd_fd); |
279 | 0 | ctx->winbindd_fd = -1; |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | static void winbind_ctx_free_locked(struct winbindd_context *ctx) |
284 | 0 | { |
285 | 0 | winbind_close_sock(ctx); |
286 | 0 | DLIST_REMOVE(wb_global_ctx.list, ctx); |
287 | 0 | free(ctx); |
288 | 0 | } |
289 | | |
290 | | static void winbind_cleanup_list(void) |
291 | 0 | { |
292 | 0 | struct winbindd_context *ctx = NULL, *next = NULL; |
293 | |
|
294 | 0 | WB_GLOBAL_LIST_LOCK; |
295 | 0 | for (ctx = wb_global_ctx.list; ctx != NULL; ctx = next) { |
296 | 0 | next = ctx->next; |
297 | |
|
298 | 0 | if (ctx->autofree) { |
299 | 0 | winbind_ctx_free_locked(ctx); |
300 | 0 | } else { |
301 | 0 | winbind_close_sock(ctx); |
302 | 0 | } |
303 | 0 | } |
304 | 0 | WB_GLOBAL_LIST_UNLOCK; |
305 | 0 | } |
306 | | |
307 | | /* Destructor for global context to ensure fd is closed */ |
308 | | |
309 | | #ifdef HAVE_DESTRUCTOR_ATTRIBUTE |
310 | | __attribute__((destructor)) |
311 | | #elif defined (HAVE_PRAGMA_FINI) |
312 | | #pragma fini (winbind_destructor) |
313 | | #endif |
314 | | static void winbind_destructor(void) |
315 | 0 | { |
316 | 0 | #ifdef HAVE_PTHREAD |
317 | 0 | if (wb_global_ctx.key_initialized) { |
318 | 0 | int ret; |
319 | 0 | ret = pthread_key_delete(wb_global_ctx.key); |
320 | 0 | assert(ret == 0); |
321 | 0 | wb_global_ctx.key_initialized = false; |
322 | 0 | } |
323 | |
|
324 | 0 | wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT; |
325 | 0 | #endif /* HAVE_PTHREAD */ |
326 | |
|
327 | 0 | winbind_cleanup_list(); |
328 | 0 | } |
329 | | |
330 | 0 | #define CONNECT_TIMEOUT 30 |
331 | | |
332 | | /* Make sure socket handle isn't stdin, stdout or stderr */ |
333 | 0 | #define RECURSION_LIMIT 3 |
334 | | |
335 | | static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */) |
336 | 0 | { |
337 | 0 | int new_fd; |
338 | 0 | if (fd >= 0 && fd <= 2) { |
339 | 0 | #ifdef F_DUPFD |
340 | 0 | if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) { |
341 | 0 | return -1; |
342 | 0 | } |
343 | | /* Paranoia */ |
344 | 0 | if (new_fd < 3) { |
345 | 0 | close(new_fd); |
346 | 0 | return -1; |
347 | 0 | } |
348 | 0 | close(fd); |
349 | 0 | return new_fd; |
350 | | #else |
351 | | if (limit <= 0) |
352 | | return -1; |
353 | | |
354 | | new_fd = dup(fd); |
355 | | if (new_fd == -1) |
356 | | return -1; |
357 | | |
358 | | /* use the program stack to hold our list of FDs to close */ |
359 | | new_fd = make_nonstd_fd_internals(new_fd, limit - 1); |
360 | | close(fd); |
361 | | return new_fd; |
362 | | #endif |
363 | 0 | } |
364 | 0 | return fd; |
365 | 0 | } |
366 | | |
367 | | /**************************************************************************** |
368 | | Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, |
369 | | else |
370 | | if SYSV use O_NDELAY |
371 | | if BSD use FNDELAY |
372 | | Set close on exec also. |
373 | | ****************************************************************************/ |
374 | | |
375 | | static int make_safe_fd(int fd) |
376 | 0 | { |
377 | 0 | int result, flags; |
378 | 0 | int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT); |
379 | 0 | if (new_fd == -1) { |
380 | 0 | close(fd); |
381 | 0 | return -1; |
382 | 0 | } |
383 | | |
384 | | /* Socket should be nonblocking. */ |
385 | 0 | #ifdef O_NONBLOCK |
386 | 0 | #define FLAG_TO_SET O_NONBLOCK |
387 | | #else |
388 | | #ifdef SYSV |
389 | | #define FLAG_TO_SET O_NDELAY |
390 | | #else /* BSD */ |
391 | | #define FLAG_TO_SET FNDELAY |
392 | | #endif |
393 | | #endif |
394 | | |
395 | 0 | if ((flags = fcntl(new_fd, F_GETFL)) == -1) { |
396 | 0 | close(new_fd); |
397 | 0 | return -1; |
398 | 0 | } |
399 | | |
400 | 0 | flags |= FLAG_TO_SET; |
401 | 0 | if (fcntl(new_fd, F_SETFL, flags) == -1) { |
402 | 0 | close(new_fd); |
403 | 0 | return -1; |
404 | 0 | } |
405 | | |
406 | 0 | #undef FLAG_TO_SET |
407 | | |
408 | | /* Socket should be closed on exec() */ |
409 | 0 | #ifdef FD_CLOEXEC |
410 | 0 | result = flags = fcntl(new_fd, F_GETFD, 0); |
411 | 0 | if (flags >= 0) { |
412 | 0 | flags |= FD_CLOEXEC; |
413 | 0 | result = fcntl( new_fd, F_SETFD, flags ); |
414 | 0 | } |
415 | 0 | if (result < 0) { |
416 | 0 | close(new_fd); |
417 | 0 | return -1; |
418 | 0 | } |
419 | 0 | #endif |
420 | 0 | return new_fd; |
421 | 0 | } |
422 | | |
423 | | /** |
424 | | * @internal |
425 | | * |
426 | | * @brief Check if we talk to the privileged pipe which should be owned by root. |
427 | | * |
428 | | * This checks if we have uid_wrapper running and if this is the case it will |
429 | | * allow one to connect to the winbind privileged pipe even it is not owned by root. |
430 | | * |
431 | | * @param[in] uid The uid to check if we can safely talk to the pipe. |
432 | | * |
433 | | * @return If we have access it returns true, else false. |
434 | | */ |
435 | | static bool winbind_privileged_pipe_is_root(uid_t uid) |
436 | 0 | { |
437 | 0 | if (uid == 0) { |
438 | 0 | return true; |
439 | 0 | } |
440 | | |
441 | 0 | if (uid_wrapper_enabled()) { |
442 | 0 | return true; |
443 | 0 | } |
444 | | |
445 | 0 | return false; |
446 | 0 | } |
447 | | |
448 | | /* Connect to winbindd socket */ |
449 | | |
450 | | static int winbind_named_pipe_sock(const char *dir) |
451 | 0 | { |
452 | 0 | struct sockaddr_un sunaddr; |
453 | 0 | struct stat st; |
454 | 0 | int fd; |
455 | 0 | int wait_time; |
456 | 0 | int slept; |
457 | 0 | int ret; |
458 | | |
459 | | /* Check permissions on unix socket directory */ |
460 | |
|
461 | 0 | if (lstat(dir, &st) == -1) { |
462 | 0 | errno = ENOENT; |
463 | 0 | return -1; |
464 | 0 | } |
465 | | |
466 | | /* |
467 | | * This tells us that the pipe is owned by a privileged |
468 | | * process, as we will be sending passwords to it. |
469 | | */ |
470 | 0 | if (!S_ISDIR(st.st_mode) || |
471 | 0 | !winbind_privileged_pipe_is_root(st.st_uid)) { |
472 | 0 | errno = ENOENT; |
473 | 0 | return -1; |
474 | 0 | } |
475 | | |
476 | | /* Connect to socket */ |
477 | | |
478 | 0 | sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX }; |
479 | |
|
480 | 0 | ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), |
481 | 0 | "%s/%s", dir, WINBINDD_SOCKET_NAME); |
482 | 0 | if ((ret == -1) || (ret >= sizeof(sunaddr.sun_path))) { |
483 | 0 | errno = ENAMETOOLONG; |
484 | 0 | return -1; |
485 | 0 | } |
486 | | |
487 | | /* If socket file doesn't exist, don't bother trying to connect |
488 | | with retry. This is an attempt to make the system usable when |
489 | | the winbindd daemon is not running. */ |
490 | | |
491 | 0 | if (lstat(sunaddr.sun_path, &st) == -1) { |
492 | 0 | errno = ENOENT; |
493 | 0 | return -1; |
494 | 0 | } |
495 | | |
496 | | /* Check permissions on unix socket file */ |
497 | | |
498 | | /* |
499 | | * This tells us that the pipe is owned by a privileged |
500 | | * process, as we will be sending passwords to it. |
501 | | */ |
502 | 0 | if (!S_ISSOCK(st.st_mode) || |
503 | 0 | !winbind_privileged_pipe_is_root(st.st_uid)) { |
504 | 0 | errno = ENOENT; |
505 | 0 | return -1; |
506 | 0 | } |
507 | | |
508 | | /* Connect to socket */ |
509 | | |
510 | 0 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { |
511 | 0 | return -1; |
512 | 0 | } |
513 | | |
514 | | /* Set socket non-blocking and close on exec. */ |
515 | | |
516 | 0 | if ((fd = make_safe_fd( fd)) == -1) { |
517 | 0 | return fd; |
518 | 0 | } |
519 | | |
520 | 0 | for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1; |
521 | 0 | wait_time += slept) { |
522 | 0 | struct pollfd pfd; |
523 | 0 | int connect_errno = 0; |
524 | 0 | socklen_t errnosize; |
525 | |
|
526 | 0 | if (wait_time >= CONNECT_TIMEOUT) |
527 | 0 | goto error_out; |
528 | | |
529 | 0 | switch (errno) { |
530 | 0 | case EINPROGRESS: |
531 | 0 | pfd.fd = fd; |
532 | 0 | pfd.events = POLLOUT; |
533 | |
|
534 | 0 | ret = poll(&pfd, 1, (CONNECT_TIMEOUT - wait_time) * 1000); |
535 | |
|
536 | 0 | if (ret > 0) { |
537 | 0 | errnosize = sizeof(connect_errno); |
538 | |
|
539 | 0 | ret = getsockopt(fd, SOL_SOCKET, |
540 | 0 | SO_ERROR, &connect_errno, &errnosize); |
541 | |
|
542 | 0 | if (ret >= 0 && connect_errno == 0) { |
543 | | /* Connect succeed */ |
544 | 0 | goto out; |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | 0 | slept = CONNECT_TIMEOUT; |
549 | 0 | break; |
550 | 0 | case EAGAIN: |
551 | 0 | slept = rand() % 3 + 1; |
552 | 0 | sleep(slept); |
553 | 0 | break; |
554 | 0 | default: |
555 | 0 | goto error_out; |
556 | 0 | } |
557 | |
|
558 | 0 | } |
559 | | |
560 | 0 | out: |
561 | |
|
562 | 0 | return fd; |
563 | | |
564 | 0 | error_out: |
565 | |
|
566 | 0 | close(fd); |
567 | 0 | return -1; |
568 | 0 | } |
569 | | |
570 | | static const char *winbindd_socket_dir(void) |
571 | 0 | { |
572 | 0 | if (nss_wrapper_enabled()) { |
573 | 0 | const char *env_dir; |
574 | |
|
575 | 0 | env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR"); |
576 | 0 | if (env_dir != NULL) { |
577 | 0 | return env_dir; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | 0 | return WINBINDD_SOCKET_DIR; |
582 | 0 | } |
583 | | |
584 | | /* Connect to winbindd socket */ |
585 | | |
586 | | static int winbind_open_pipe_sock(struct winbindd_context *ctx, |
587 | | int recursing, int need_priv) |
588 | 0 | { |
589 | 0 | #ifdef HAVE_UNIXSOCKET |
590 | 0 | struct winbindd_request request; |
591 | 0 | struct winbindd_response response; |
592 | |
|
593 | 0 | ZERO_STRUCT(request); |
594 | 0 | ZERO_STRUCT(response); |
595 | |
|
596 | 0 | if (!ctx) { |
597 | 0 | return -1; |
598 | 0 | } |
599 | | |
600 | 0 | if (ctx->our_pid != getpid()) { |
601 | 0 | winbind_close_sock(ctx); |
602 | 0 | ctx->our_pid = getpid(); |
603 | 0 | } |
604 | |
|
605 | 0 | if ((need_priv != 0) && !ctx->is_privileged) { |
606 | 0 | winbind_close_sock(ctx); |
607 | 0 | } |
608 | |
|
609 | 0 | if (ctx->winbindd_fd != -1) { |
610 | 0 | return ctx->winbindd_fd; |
611 | 0 | } |
612 | | |
613 | 0 | if (recursing) { |
614 | 0 | return -1; |
615 | 0 | } |
616 | | |
617 | 0 | ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir()); |
618 | |
|
619 | 0 | if (ctx->winbindd_fd == -1) { |
620 | 0 | return -1; |
621 | 0 | } |
622 | | |
623 | 0 | ctx->is_privileged = false; |
624 | | |
625 | | /* version-check the socket */ |
626 | |
|
627 | 0 | request.wb_flags = WBFLAG_RECURSE; |
628 | 0 | if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request, |
629 | 0 | &response) != NSS_STATUS_SUCCESS) || |
630 | 0 | (response.data.interface_version != WINBIND_INTERFACE_VERSION)) { |
631 | 0 | winbind_close_sock(ctx); |
632 | 0 | return -1; |
633 | 0 | } |
634 | | |
635 | 0 | if (need_priv == 0) { |
636 | 0 | return ctx->winbindd_fd; |
637 | 0 | } |
638 | | |
639 | | /* try and get priv pipe */ |
640 | | |
641 | 0 | request.wb_flags = WBFLAG_RECURSE; |
642 | | |
643 | | /* Note that response needs to be initialized to avoid |
644 | | * crashing on clean up after WINBINDD_PRIV_PIPE_DIR call failed |
645 | | * as interface version (from the first request) returned as a fstring, |
646 | | * thus response.extra_data.data will not be NULL even though |
647 | | * winbindd response did not write over it due to a failure */ |
648 | 0 | ZERO_STRUCT(response); |
649 | 0 | if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request, |
650 | 0 | &response) == NSS_STATUS_SUCCESS) { |
651 | 0 | int fd; |
652 | 0 | fd = winbind_named_pipe_sock((char *)response.extra_data.data); |
653 | 0 | if (fd != -1) { |
654 | 0 | close(ctx->winbindd_fd); |
655 | 0 | ctx->winbindd_fd = fd; |
656 | 0 | ctx->is_privileged = true; |
657 | 0 | } |
658 | |
|
659 | 0 | SAFE_FREE(response.extra_data.data); |
660 | 0 | } |
661 | |
|
662 | 0 | if (!ctx->is_privileged) { |
663 | 0 | return -1; |
664 | 0 | } |
665 | | |
666 | 0 | return ctx->winbindd_fd; |
667 | | #else |
668 | | return -1; |
669 | | #endif /* HAVE_UNIXSOCKET */ |
670 | 0 | } |
671 | | |
672 | | /* Write data to winbindd socket */ |
673 | | |
674 | | static ssize_t winbind_write_sock(struct winbindd_context *ctx, |
675 | | void *buffer, |
676 | | size_t count, |
677 | | int recursing, |
678 | | int need_priv) |
679 | 0 | { |
680 | 0 | int fd; |
681 | 0 | ssize_t nwritten; |
682 | | |
683 | | /* Open connection to winbind daemon */ |
684 | |
|
685 | 0 | restart: |
686 | |
|
687 | 0 | fd = winbind_open_pipe_sock(ctx, recursing, need_priv); |
688 | 0 | if (fd == -1) { |
689 | 0 | errno = ENOENT; |
690 | 0 | return -1; |
691 | 0 | } |
692 | | |
693 | | /* Write data to socket */ |
694 | | |
695 | 0 | nwritten = 0; |
696 | |
|
697 | 0 | while(nwritten < count) { |
698 | 0 | struct pollfd pfd; |
699 | 0 | ssize_t result; |
700 | 0 | int ret; |
701 | | |
702 | | /* Catch pipe close on other end by checking if a read() |
703 | | call would not block by calling poll(). */ |
704 | |
|
705 | 0 | pfd.fd = fd; |
706 | 0 | pfd.events = POLLIN|POLLOUT|POLLHUP; |
707 | |
|
708 | 0 | ret = poll(&pfd, 1, -1); |
709 | 0 | if (ret == -1) { |
710 | 0 | winbind_close_sock(ctx); |
711 | 0 | return -1; /* poll error */ |
712 | 0 | } |
713 | | |
714 | | /* Write should be OK if fd not available for reading */ |
715 | | |
716 | 0 | if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) { |
717 | | |
718 | | /* Pipe has closed on remote end */ |
719 | |
|
720 | 0 | winbind_close_sock(ctx); |
721 | 0 | goto restart; |
722 | 0 | } |
723 | | |
724 | | /* Do the write */ |
725 | | |
726 | 0 | result = write(fd, (char *)buffer + nwritten, |
727 | 0 | count - nwritten); |
728 | |
|
729 | 0 | if ((result == -1) || (result == 0)) { |
730 | | |
731 | | /* Write failed */ |
732 | |
|
733 | 0 | winbind_close_sock(ctx); |
734 | 0 | return -1; |
735 | 0 | } |
736 | | |
737 | 0 | nwritten += result; |
738 | 0 | } |
739 | | |
740 | 0 | return nwritten; |
741 | 0 | } |
742 | | |
743 | | /* Read data from winbindd socket */ |
744 | | |
745 | | static int winbind_read_sock(struct winbindd_context *ctx, |
746 | | void *buffer, int count) |
747 | 0 | { |
748 | 0 | int fd; |
749 | 0 | int nread = 0; |
750 | 0 | int total_time = 0; |
751 | |
|
752 | 0 | fd = winbind_open_pipe_sock(ctx, false, false); |
753 | 0 | if (fd == -1) { |
754 | 0 | return -1; |
755 | 0 | } |
756 | | |
757 | | /* Read data from socket */ |
758 | 0 | while(nread < count) { |
759 | 0 | struct pollfd pfd; |
760 | 0 | int ret; |
761 | | |
762 | | /* Catch pipe close on other end by checking if a read() |
763 | | call would not block by calling poll(). */ |
764 | |
|
765 | 0 | pfd.fd = fd; |
766 | 0 | pfd.events = POLLIN|POLLHUP; |
767 | | |
768 | | /* Wait for 5 seconds for a reply. May need to parameterise this... */ |
769 | |
|
770 | 0 | ret = poll(&pfd, 1, 5000); |
771 | 0 | if (ret == -1) { |
772 | 0 | winbind_close_sock(ctx); |
773 | 0 | return -1; /* poll error */ |
774 | 0 | } |
775 | | |
776 | 0 | if (ret == 0) { |
777 | | /* Not ready for read yet... */ |
778 | 0 | if (total_time >= 300) { |
779 | | /* Timeout */ |
780 | 0 | winbind_close_sock(ctx); |
781 | 0 | return -1; |
782 | 0 | } |
783 | 0 | total_time += 5; |
784 | 0 | continue; |
785 | 0 | } |
786 | | |
787 | 0 | if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) { |
788 | | |
789 | | /* Do the Read */ |
790 | |
|
791 | 0 | int result = read(fd, (char *)buffer + nread, |
792 | 0 | count - nread); |
793 | |
|
794 | 0 | if ((result == -1) || (result == 0)) { |
795 | | |
796 | | /* Read failed. I think the only useful thing we |
797 | | can do here is just return -1 and fail since the |
798 | | transaction has failed half way through. */ |
799 | |
|
800 | 0 | winbind_close_sock(ctx); |
801 | 0 | return -1; |
802 | 0 | } |
803 | | |
804 | 0 | nread += result; |
805 | |
|
806 | 0 | } |
807 | 0 | } |
808 | | |
809 | 0 | return nread; |
810 | 0 | } |
811 | | |
812 | | /* Read reply */ |
813 | | |
814 | | static int winbindd_read_reply(struct winbindd_context *ctx, |
815 | | struct winbindd_response *response) |
816 | 0 | { |
817 | 0 | int result1, result2 = 0; |
818 | |
|
819 | 0 | if (!response) { |
820 | 0 | return -1; |
821 | 0 | } |
822 | | |
823 | | /* Read fixed length response */ |
824 | | |
825 | 0 | result1 = winbind_read_sock(ctx, response, |
826 | 0 | sizeof(struct winbindd_response)); |
827 | | |
828 | | /* We actually send the pointer value of the extra_data field from |
829 | | the server. This has no meaning in the client's address space |
830 | | so we clear it out. */ |
831 | |
|
832 | 0 | response->extra_data.data = NULL; |
833 | |
|
834 | 0 | if (result1 == -1) { |
835 | 0 | return -1; |
836 | 0 | } |
837 | | |
838 | 0 | if (response->length < sizeof(struct winbindd_response)) { |
839 | 0 | return -1; |
840 | 0 | } |
841 | | |
842 | | /* Read variable length response */ |
843 | | |
844 | 0 | if (response->length > sizeof(struct winbindd_response)) { |
845 | 0 | int extra_data_len = response->length - |
846 | 0 | sizeof(struct winbindd_response); |
847 | | |
848 | | /* Mallocate memory for extra data */ |
849 | |
|
850 | 0 | if (!(response->extra_data.data = malloc(extra_data_len))) { |
851 | 0 | return -1; |
852 | 0 | } |
853 | | |
854 | 0 | result2 = winbind_read_sock(ctx, response->extra_data.data, |
855 | 0 | extra_data_len); |
856 | 0 | if (result2 == -1) { |
857 | 0 | winbindd_free_response(response); |
858 | 0 | return -1; |
859 | 0 | } |
860 | 0 | } |
861 | | |
862 | | /* Return total amount of data read */ |
863 | | |
864 | 0 | return result1 + result2; |
865 | 0 | } |
866 | | |
867 | | /* |
868 | | * send simple types of requests |
869 | | */ |
870 | | |
871 | | static NSS_STATUS winbindd_send_request( |
872 | | struct winbindd_context *ctx, |
873 | | int req_type, |
874 | | int need_priv, |
875 | | struct winbindd_request *request) |
876 | 0 | { |
877 | 0 | struct winbindd_request lrequest; |
878 | | |
879 | | /* Check for our tricky environment variable */ |
880 | |
|
881 | 0 | if (winbind_env_set()) { |
882 | 0 | return NSS_STATUS_NOTFOUND; |
883 | 0 | } |
884 | | |
885 | 0 | if (!request) { |
886 | 0 | ZERO_STRUCT(lrequest); |
887 | 0 | request = &lrequest; |
888 | 0 | } |
889 | | |
890 | | /* Fill in request and send down pipe */ |
891 | |
|
892 | 0 | winbindd_init_request(request, req_type); |
893 | |
|
894 | 0 | if (winbind_write_sock(ctx, request, sizeof(*request), |
895 | 0 | request->wb_flags & WBFLAG_RECURSE, |
896 | 0 | need_priv) == -1) |
897 | 0 | { |
898 | | /* Set ENOENT for consistency. Required by some apps */ |
899 | 0 | errno = ENOENT; |
900 | |
|
901 | 0 | return NSS_STATUS_UNAVAIL; |
902 | 0 | } |
903 | | |
904 | 0 | if ((request->extra_len != 0) && |
905 | 0 | (winbind_write_sock(ctx, request->extra_data.data, |
906 | 0 | request->extra_len, |
907 | 0 | request->wb_flags & WBFLAG_RECURSE, |
908 | 0 | need_priv) == -1)) |
909 | 0 | { |
910 | | /* Set ENOENT for consistency. Required by some apps */ |
911 | 0 | errno = ENOENT; |
912 | |
|
913 | 0 | return NSS_STATUS_UNAVAIL; |
914 | 0 | } |
915 | | |
916 | 0 | return NSS_STATUS_SUCCESS; |
917 | 0 | } |
918 | | |
919 | | /* |
920 | | * Get results from winbindd request |
921 | | */ |
922 | | |
923 | | static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx, |
924 | | struct winbindd_response *response) |
925 | 0 | { |
926 | 0 | struct winbindd_response lresponse; |
927 | |
|
928 | 0 | if (!response) { |
929 | 0 | ZERO_STRUCT(lresponse); |
930 | 0 | response = &lresponse; |
931 | 0 | } |
932 | |
|
933 | 0 | init_response(response); |
934 | | |
935 | | /* Wait for reply */ |
936 | 0 | if (winbindd_read_reply(ctx, response) == -1) { |
937 | | /* Set ENOENT for consistency. Required by some apps */ |
938 | 0 | errno = ENOENT; |
939 | |
|
940 | 0 | return NSS_STATUS_UNAVAIL; |
941 | 0 | } |
942 | | |
943 | | /* Throw away extra data if client didn't request it */ |
944 | 0 | if (response == &lresponse) { |
945 | 0 | winbindd_free_response(response); |
946 | 0 | } |
947 | | |
948 | | /* Copy reply data from socket */ |
949 | 0 | if (response->result != WINBINDD_OK) { |
950 | 0 | return NSS_STATUS_NOTFOUND; |
951 | 0 | } |
952 | | |
953 | 0 | return NSS_STATUS_SUCCESS; |
954 | 0 | } |
955 | | |
956 | | /* Handle simple types of requests */ |
957 | | |
958 | | NSS_STATUS winbindd_request_response(struct winbindd_context *ctx, |
959 | | int req_type, |
960 | | struct winbindd_request *request, |
961 | | struct winbindd_response *response) |
962 | 0 | { |
963 | 0 | NSS_STATUS status = NSS_STATUS_UNAVAIL; |
964 | |
|
965 | 0 | if (ctx == NULL) { |
966 | 0 | ctx = get_wb_global_ctx(); |
967 | 0 | } |
968 | |
|
969 | 0 | status = winbindd_send_request(ctx, req_type, 0, request); |
970 | 0 | if (status != NSS_STATUS_SUCCESS) { |
971 | 0 | goto out; |
972 | 0 | } |
973 | 0 | status = winbindd_get_response(ctx, response); |
974 | |
|
975 | 0 | out: |
976 | 0 | return status; |
977 | 0 | } |
978 | | |
979 | | NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx, |
980 | | int req_type, |
981 | | struct winbindd_request *request, |
982 | | struct winbindd_response *response) |
983 | 0 | { |
984 | 0 | NSS_STATUS status = NSS_STATUS_UNAVAIL; |
985 | |
|
986 | 0 | if (ctx == NULL) { |
987 | 0 | ctx = get_wb_global_ctx(); |
988 | 0 | } |
989 | |
|
990 | 0 | status = winbindd_send_request(ctx, req_type, 1, request); |
991 | 0 | if (status != NSS_STATUS_SUCCESS) { |
992 | 0 | goto out; |
993 | 0 | } |
994 | 0 | status = winbindd_get_response(ctx, response); |
995 | |
|
996 | 0 | out: |
997 | 0 | return status; |
998 | 0 | } |
999 | | |
1000 | | /* Create and free winbindd context */ |
1001 | | |
1002 | | struct winbindd_context *winbindd_ctx_create(void) |
1003 | 0 | { |
1004 | 0 | struct winbindd_context *ctx; |
1005 | |
|
1006 | 0 | ctx = calloc(1, sizeof(struct winbindd_context)); |
1007 | |
|
1008 | 0 | if (!ctx) { |
1009 | 0 | return NULL; |
1010 | 0 | } |
1011 | | |
1012 | 0 | ctx->winbindd_fd = -1; |
1013 | |
|
1014 | 0 | WB_GLOBAL_LIST_LOCK; |
1015 | 0 | DLIST_ADD_END(wb_global_ctx.list, ctx); |
1016 | 0 | WB_GLOBAL_LIST_UNLOCK; |
1017 | |
|
1018 | 0 | return ctx; |
1019 | 0 | } |
1020 | | |
1021 | | void winbindd_ctx_free(struct winbindd_context *ctx) |
1022 | 0 | { |
1023 | 0 | WB_GLOBAL_LIST_LOCK; |
1024 | 0 | winbind_ctx_free_locked(ctx); |
1025 | | WB_GLOBAL_LIST_UNLOCK; |
1026 | 0 | } |