~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to nsswitch/wb_common.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
10
 
 
11
   This library is free software; you can redistribute it and/or
 
12
   modify it under the terms of the GNU Lesser General Public
 
13
   License as published by the Free Software Foundation; either
 
14
   version 3 of the License, or (at your option) any later version.
 
15
 
 
16
   This library is distributed in the hope that it will be useful,
 
17
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
19
   Library General Public License for more details.
 
20
 
 
21
   You should have received a copy of the GNU Lesser General Public License
 
22
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
23
*/
 
24
 
 
25
#include "winbind_client.h"
 
26
 
 
27
/* Global variables.  These are effectively the client state information */
 
28
 
 
29
int winbindd_fd = -1;           /* fd for winbindd socket */
 
30
static int is_privileged = 0;
 
31
 
 
32
/* Free a response structure */
 
33
 
 
34
void winbindd_free_response(struct winbindd_response *response)
 
35
{
 
36
        /* Free any allocated extra_data */
 
37
 
 
38
        if (response)
 
39
                SAFE_FREE(response->extra_data.data);
 
40
}
 
41
 
 
42
/* Initialise a request structure */
 
43
 
 
44
void winbindd_init_request(struct winbindd_request *request, int request_type)
 
45
{
 
46
        request->length = sizeof(struct winbindd_request);
 
47
 
 
48
        request->cmd = (enum winbindd_cmd)request_type;
 
49
        request->pid = getpid();
 
50
 
 
51
}
 
52
 
 
53
/* Initialise a response structure */
 
54
 
 
55
static void init_response(struct winbindd_response *response)
 
56
{
 
57
        /* Initialise return value */
 
58
 
 
59
        response->result = WINBINDD_ERROR;
 
60
}
 
61
 
 
62
/* Close established socket */
 
63
 
 
64
void winbind_close_sock(void)
 
65
{
 
66
        if (winbindd_fd != -1) {
 
67
                close(winbindd_fd);
 
68
                winbindd_fd = -1;
 
69
        }
 
70
}
 
71
 
 
72
#define CONNECT_TIMEOUT 30
 
73
 
 
74
/* Make sure socket handle isn't stdin, stdout or stderr */
 
75
#define RECURSION_LIMIT 3
 
76
 
 
77
static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
 
78
{
 
79
        int new_fd;
 
80
        if (fd >= 0 && fd <= 2) {
 
81
#ifdef F_DUPFD
 
82
                if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
 
83
                        return -1;
 
84
                }
 
85
                /* Paranoia */
 
86
                if (new_fd < 3) {
 
87
                        close(new_fd);
 
88
                        return -1;
 
89
                }
 
90
                close(fd);
 
91
                return new_fd;
 
92
#else
 
93
                if (limit <= 0)
 
94
                        return -1;
 
95
 
 
96
                new_fd = dup(fd);
 
97
                if (new_fd == -1)
 
98
                        return -1;
 
99
 
 
100
                /* use the program stack to hold our list of FDs to close */
 
101
                new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
 
102
                close(fd);
 
103
                return new_fd;
 
104
#endif
 
105
        }
 
106
        return fd;
 
107
}
 
108
 
 
109
/****************************************************************************
 
110
 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
 
111
 else
 
112
 if SYSV use O_NDELAY
 
113
 if BSD use FNDELAY
 
114
 Set close on exec also.
 
115
****************************************************************************/
 
116
 
 
117
static int make_safe_fd(int fd)
 
118
{
 
119
        int result, flags;
 
120
        int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
 
121
        if (new_fd == -1) {
 
122
                close(fd);
 
123
                return -1;
 
124
        }
 
125
 
 
126
        /* Socket should be nonblocking. */
 
127
#ifdef O_NONBLOCK
 
128
#define FLAG_TO_SET O_NONBLOCK
 
129
#else
 
130
#ifdef SYSV
 
131
#define FLAG_TO_SET O_NDELAY
 
132
#else /* BSD */
 
133
#define FLAG_TO_SET FNDELAY
 
134
#endif
 
135
#endif
 
136
 
 
137
        if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
 
138
                close(new_fd);
 
139
                return -1;
 
140
        }
 
141
 
 
142
        flags |= FLAG_TO_SET;
 
143
        if (fcntl(new_fd, F_SETFL, flags) == -1) {
 
144
                close(new_fd);
 
145
                return -1;
 
146
        }
 
147
 
 
148
#undef FLAG_TO_SET
 
149
 
 
150
        /* Socket should be closed on exec() */
 
151
#ifdef FD_CLOEXEC
 
152
        result = flags = fcntl(new_fd, F_GETFD, 0);
 
153
        if (flags >= 0) {
 
154
                flags |= FD_CLOEXEC;
 
155
                result = fcntl( new_fd, F_SETFD, flags );
 
156
        }
 
157
        if (result < 0) {
 
158
                close(new_fd);
 
159
                return -1;
 
160
        }
 
161
#endif
 
162
        return new_fd;
 
163
}
 
164
 
 
165
/* Connect to winbindd socket */
 
166
 
 
167
static int winbind_named_pipe_sock(const char *dir)
 
168
{
 
169
        struct sockaddr_un sunaddr;
 
170
        struct stat st;
 
171
        char *path = NULL;
 
172
        int fd;
 
173
        int wait_time;
 
174
        int slept;
 
175
 
 
176
        /* Check permissions on unix socket directory */
 
177
 
 
178
        if (lstat(dir, &st) == -1) {
 
179
                errno = ENOENT;
 
180
                return -1;
 
181
        }
 
182
 
 
183
        if (!S_ISDIR(st.st_mode) ||
 
184
            (st.st_uid != 0 && st.st_uid != geteuid())) {
 
185
                errno = ENOENT;
 
186
                return -1;
 
187
        }
 
188
 
 
189
        /* Connect to socket */
 
190
 
 
191
        if (asprintf(&path, "%s/%s", dir, WINBINDD_SOCKET_NAME) < 0) {
 
192
                return -1;
 
193
        }
 
194
 
 
195
        ZERO_STRUCT(sunaddr);
 
196
        sunaddr.sun_family = AF_UNIX;
 
197
        strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
 
198
 
 
199
        /* If socket file doesn't exist, don't bother trying to connect
 
200
           with retry.  This is an attempt to make the system usable when
 
201
           the winbindd daemon is not running. */
 
202
 
 
203
        if (lstat(path, &st) == -1) {
 
204
                errno = ENOENT;
 
205
                SAFE_FREE(path);
 
206
                return -1;
 
207
        }
 
208
 
 
209
        SAFE_FREE(path);
 
210
        /* Check permissions on unix socket file */
 
211
 
 
212
        if (!S_ISSOCK(st.st_mode) ||
 
213
            (st.st_uid != 0 && st.st_uid != geteuid())) {
 
214
                errno = ENOENT;
 
215
                return -1;
 
216
        }
 
217
 
 
218
        /* Connect to socket */
 
219
 
 
220
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 
221
                return -1;
 
222
        }
 
223
 
 
224
        /* Set socket non-blocking and close on exec. */
 
225
 
 
226
        if ((fd = make_safe_fd( fd)) == -1) {
 
227
                return fd;
 
228
        }
 
229
 
 
230
        for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
 
231
                        wait_time += slept) {
 
232
                struct timeval tv;
 
233
                fd_set w_fds;
 
234
                int ret;
 
235
                int connect_errno = 0;
 
236
                socklen_t errnosize;
 
237
 
 
238
                if (wait_time >= CONNECT_TIMEOUT)
 
239
                        goto error_out;
 
240
 
 
241
                switch (errno) {
 
242
                        case EINPROGRESS:
 
243
                                FD_ZERO(&w_fds);
 
244
                                FD_SET(fd, &w_fds);
 
245
                                tv.tv_sec = CONNECT_TIMEOUT - wait_time;
 
246
                                tv.tv_usec = 0;
 
247
 
 
248
                                ret = select(fd + 1, NULL, &w_fds, NULL, &tv);
 
249
 
 
250
                                if (ret > 0) {
 
251
                                        errnosize = sizeof(connect_errno);
 
252
 
 
253
                                        ret = getsockopt(fd, SOL_SOCKET,
 
254
                                                        SO_ERROR, &connect_errno, &errnosize);
 
255
 
 
256
                                        if (ret >= 0 && connect_errno == 0) {
 
257
                                                /* Connect succeed */
 
258
                                                goto out;
 
259
                                        }
 
260
                                }
 
261
 
 
262
                                slept = CONNECT_TIMEOUT;
 
263
                                break;
 
264
                        case EAGAIN:
 
265
                                slept = rand() % 3 + 1;
 
266
                                sleep(slept);
 
267
                                break;
 
268
                        default:
 
269
                                goto error_out;
 
270
                }
 
271
 
 
272
        }
 
273
 
 
274
  out:
 
275
 
 
276
        return fd;
 
277
 
 
278
  error_out:
 
279
 
 
280
        close(fd);
 
281
        return -1;
 
282
}
 
283
 
 
284
static const char *winbindd_socket_dir(void)
 
285
{
 
286
#ifdef SOCKET_WRAPPER
 
287
        const char *env_dir;
 
288
 
 
289
        env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
 
290
        if (env_dir) {
 
291
                return env_dir;
 
292
        }
 
293
#endif
 
294
 
 
295
        return WINBINDD_SOCKET_DIR;
 
296
}
 
297
 
 
298
/* Connect to winbindd socket */
 
299
 
 
300
static int winbind_open_pipe_sock(int recursing, int need_priv)
 
301
{
 
302
#ifdef HAVE_UNIXSOCKET
 
303
        static pid_t our_pid;
 
304
        struct winbindd_request request;
 
305
        struct winbindd_response response;
 
306
        ZERO_STRUCT(request);
 
307
        ZERO_STRUCT(response);
 
308
 
 
309
        if (our_pid != getpid()) {
 
310
                winbind_close_sock();
 
311
                our_pid = getpid();
 
312
        }
 
313
 
 
314
        if ((need_priv != 0) && (is_privileged == 0)) {
 
315
                winbind_close_sock();
 
316
        }
 
317
 
 
318
        if (winbindd_fd != -1) {
 
319
                return winbindd_fd;
 
320
        }
 
321
 
 
322
        if (recursing) {
 
323
                return -1;
 
324
        }
 
325
 
 
326
        if ((winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir())) == -1) {
 
327
                return -1;
 
328
        }
 
329
 
 
330
        is_privileged = 0;
 
331
 
 
332
        /* version-check the socket */
 
333
 
 
334
        request.wb_flags = WBFLAG_RECURSE;
 
335
        if ((winbindd_request_response(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
 
336
                winbind_close_sock();
 
337
                return -1;
 
338
        }
 
339
 
 
340
        /* try and get priv pipe */
 
341
 
 
342
        request.wb_flags = WBFLAG_RECURSE;
 
343
        if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
 
344
                int fd;
 
345
                if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) {
 
346
                        close(winbindd_fd);
 
347
                        winbindd_fd = fd;
 
348
                        is_privileged = 1;
 
349
                }
 
350
        }
 
351
 
 
352
        if ((need_priv != 0) && (is_privileged == 0)) {
 
353
                return -1;
 
354
        }
 
355
 
 
356
        SAFE_FREE(response.extra_data.data);
 
357
 
 
358
        return winbindd_fd;
 
359
#else
 
360
        return -1;
 
361
#endif /* HAVE_UNIXSOCKET */
 
362
}
 
363
 
 
364
/* Write data to winbindd socket */
 
365
 
 
366
int winbind_write_sock(void *buffer, int count, int recursing, int need_priv)
 
367
{
 
368
        int result, nwritten;
 
369
 
 
370
        /* Open connection to winbind daemon */
 
371
 
 
372
 restart:
 
373
 
 
374
        if (winbind_open_pipe_sock(recursing, need_priv) == -1) {
 
375
                errno = ENOENT;
 
376
                return -1;
 
377
        }
 
378
 
 
379
        /* Write data to socket */
 
380
 
 
381
        nwritten = 0;
 
382
 
 
383
        while(nwritten < count) {
 
384
                struct timeval tv;
 
385
                fd_set r_fds;
 
386
 
 
387
                /* Catch pipe close on other end by checking if a read()
 
388
                   call would not block by calling select(). */
 
389
 
 
390
                FD_ZERO(&r_fds);
 
391
                FD_SET(winbindd_fd, &r_fds);
 
392
                ZERO_STRUCT(tv);
 
393
 
 
394
                if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
 
395
                        winbind_close_sock();
 
396
                        return -1;                   /* Select error */
 
397
                }
 
398
 
 
399
                /* Write should be OK if fd not available for reading */
 
400
 
 
401
                if (!FD_ISSET(winbindd_fd, &r_fds)) {
 
402
 
 
403
                        /* Do the write */
 
404
 
 
405
                        result = write(winbindd_fd,
 
406
                                       (char *)buffer + nwritten,
 
407
                                       count - nwritten);
 
408
 
 
409
                        if ((result == -1) || (result == 0)) {
 
410
 
 
411
                                /* Write failed */
 
412
 
 
413
                                winbind_close_sock();
 
414
                                return -1;
 
415
                        }
 
416
 
 
417
                        nwritten += result;
 
418
 
 
419
                } else {
 
420
 
 
421
                        /* Pipe has closed on remote end */
 
422
 
 
423
                        winbind_close_sock();
 
424
                        goto restart;
 
425
                }
 
426
        }
 
427
 
 
428
        return nwritten;
 
429
}
 
430
 
 
431
/* Read data from winbindd socket */
 
432
 
 
433
int winbind_read_sock(void *buffer, int count)
 
434
{
 
435
        int nread = 0;
 
436
        int total_time = 0, selret;
 
437
 
 
438
        if (winbindd_fd == -1) {
 
439
                return -1;
 
440
        }
 
441
 
 
442
        /* Read data from socket */
 
443
        while(nread < count) {
 
444
                struct timeval tv;
 
445
                fd_set r_fds;
 
446
 
 
447
                /* Catch pipe close on other end by checking if a read()
 
448
                   call would not block by calling select(). */
 
449
 
 
450
                FD_ZERO(&r_fds);
 
451
                FD_SET(winbindd_fd, &r_fds);
 
452
                ZERO_STRUCT(tv);
 
453
                /* Wait for 5 seconds for a reply. May need to parameterise this... */
 
454
                tv.tv_sec = 5;
 
455
 
 
456
                if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) {
 
457
                        winbind_close_sock();
 
458
                        return -1;                   /* Select error */
 
459
                }
 
460
 
 
461
                if (selret == 0) {
 
462
                        /* Not ready for read yet... */
 
463
                        if (total_time >= 30) {
 
464
                                /* Timeout */
 
465
                                winbind_close_sock();
 
466
                                return -1;
 
467
                        }
 
468
                        total_time += 5;
 
469
                        continue;
 
470
                }
 
471
 
 
472
                if (FD_ISSET(winbindd_fd, &r_fds)) {
 
473
 
 
474
                        /* Do the Read */
 
475
 
 
476
                        int result = read(winbindd_fd, (char *)buffer + nread,
 
477
                              count - nread);
 
478
 
 
479
                        if ((result == -1) || (result == 0)) {
 
480
 
 
481
                                /* Read failed.  I think the only useful thing we
 
482
                                   can do here is just return -1 and fail since the
 
483
                                   transaction has failed half way through. */
 
484
 
 
485
                                winbind_close_sock();
 
486
                                return -1;
 
487
                        }
 
488
 
 
489
                        nread += result;
 
490
 
 
491
                }
 
492
        }
 
493
 
 
494
        return nread;
 
495
}
 
496
 
 
497
/* Read reply */
 
498
 
 
499
int winbindd_read_reply(struct winbindd_response *response)
 
500
{
 
501
        int result1, result2 = 0;
 
502
 
 
503
        if (!response) {
 
504
                return -1;
 
505
        }
 
506
 
 
507
        /* Read fixed length response */
 
508
 
 
509
        result1 = winbind_read_sock(response,
 
510
                                    sizeof(struct winbindd_response));
 
511
        if (result1 == -1) {
 
512
                return -1;
 
513
        }
 
514
 
 
515
        /* We actually send the pointer value of the extra_data field from
 
516
           the server.  This has no meaning in the client's address space
 
517
           so we clear it out. */
 
518
 
 
519
        response->extra_data.data = NULL;
 
520
 
 
521
        /* Read variable length response */
 
522
 
 
523
        if (response->length > sizeof(struct winbindd_response)) {
 
524
                int extra_data_len = response->length -
 
525
                        sizeof(struct winbindd_response);
 
526
 
 
527
                /* Mallocate memory for extra data */
 
528
 
 
529
                if (!(response->extra_data.data = malloc(extra_data_len))) {
 
530
                        return -1;
 
531
                }
 
532
 
 
533
                result2 = winbind_read_sock(response->extra_data.data,
 
534
                                            extra_data_len);
 
535
                if (result2 == -1) {
 
536
                        winbindd_free_response(response);
 
537
                        return -1;
 
538
                }
 
539
        }
 
540
 
 
541
        /* Return total amount of data read */
 
542
 
 
543
        return result1 + result2;
 
544
}
 
545
 
 
546
/*
 
547
 * send simple types of requests
 
548
 */
 
549
 
 
550
NSS_STATUS winbindd_send_request(int req_type, int need_priv,
 
551
                                 struct winbindd_request *request)
 
552
{
 
553
        struct winbindd_request lrequest;
 
554
 
 
555
        /* Check for our tricky environment variable */
 
556
 
 
557
        if (winbind_env_set()) {
 
558
                return NSS_STATUS_NOTFOUND;
 
559
        }
 
560
 
 
561
        if (!request) {
 
562
                ZERO_STRUCT(lrequest);
 
563
                request = &lrequest;
 
564
        }
 
565
 
 
566
        /* Fill in request and send down pipe */
 
567
 
 
568
        winbindd_init_request(request, req_type);
 
569
 
 
570
        if (winbind_write_sock(request, sizeof(*request),
 
571
                               request->wb_flags & WBFLAG_RECURSE,
 
572
                               need_priv) == -1)
 
573
        {
 
574
                /* Set ENOENT for consistency.  Required by some apps */
 
575
                errno = ENOENT;
 
576
 
 
577
                return NSS_STATUS_UNAVAIL;
 
578
        }
 
579
 
 
580
        if ((request->extra_len != 0) &&
 
581
            (winbind_write_sock(request->extra_data.data,
 
582
                                request->extra_len,
 
583
                                request->wb_flags & WBFLAG_RECURSE,
 
584
                                need_priv) == -1))
 
585
        {
 
586
                /* Set ENOENT for consistency.  Required by some apps */
 
587
                errno = ENOENT;
 
588
 
 
589
                return NSS_STATUS_UNAVAIL;
 
590
        }
 
591
 
 
592
        return NSS_STATUS_SUCCESS;
 
593
}
 
594
 
 
595
/*
 
596
 * Get results from winbindd request
 
597
 */
 
598
 
 
599
NSS_STATUS winbindd_get_response(struct winbindd_response *response)
 
600
{
 
601
        struct winbindd_response lresponse;
 
602
 
 
603
        if (!response) {
 
604
                ZERO_STRUCT(lresponse);
 
605
                response = &lresponse;
 
606
        }
 
607
 
 
608
        init_response(response);
 
609
 
 
610
        /* Wait for reply */
 
611
        if (winbindd_read_reply(response) == -1) {
 
612
                /* Set ENOENT for consistency.  Required by some apps */
 
613
                errno = ENOENT;
 
614
 
 
615
                return NSS_STATUS_UNAVAIL;
 
616
        }
 
617
 
 
618
        /* Throw away extra data if client didn't request it */
 
619
        if (response == &lresponse) {
 
620
                winbindd_free_response(response);
 
621
        }
 
622
 
 
623
        /* Copy reply data from socket */
 
624
        if (response->result != WINBINDD_OK) {
 
625
                return NSS_STATUS_NOTFOUND;
 
626
        }
 
627
 
 
628
        return NSS_STATUS_SUCCESS;
 
629
}
 
630
 
 
631
/* Handle simple types of requests */
 
632
 
 
633
NSS_STATUS winbindd_request_response(int req_type,
 
634
                            struct winbindd_request *request,
 
635
                            struct winbindd_response *response)
 
636
{
 
637
        NSS_STATUS status = NSS_STATUS_UNAVAIL;
 
638
        int count = 0;
 
639
 
 
640
        while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
 
641
                status = winbindd_send_request(req_type, 0, request);
 
642
                if (status != NSS_STATUS_SUCCESS)
 
643
                        return(status);
 
644
                status = winbindd_get_response(response);
 
645
                count += 1;
 
646
        }
 
647
 
 
648
        return status;
 
649
}
 
650
 
 
651
NSS_STATUS winbindd_priv_request_response(int req_type,
 
652
                                          struct winbindd_request *request,
 
653
                                          struct winbindd_response *response)
 
654
{
 
655
        NSS_STATUS status = NSS_STATUS_UNAVAIL;
 
656
        int count = 0;
 
657
 
 
658
        while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
 
659
                status = winbindd_send_request(req_type, 1, request);
 
660
                if (status != NSS_STATUS_SUCCESS)
 
661
                        return(status);
 
662
                status = winbindd_get_response(response);
 
663
                count += 1;
 
664
        }
 
665
 
 
666
        return status;
 
667
}
 
668
 
 
669
/*************************************************************************
 
670
 ************************************************************************/
 
671
 
 
672
const char *nss_err_str(NSS_STATUS ret)
 
673
{
 
674
        switch (ret) {
 
675
                case NSS_STATUS_TRYAGAIN:
 
676
                        return "NSS_STATUS_TRYAGAIN";
 
677
                case NSS_STATUS_SUCCESS:
 
678
                        return "NSS_STATUS_SUCCESS";
 
679
                case NSS_STATUS_NOTFOUND:
 
680
                        return "NSS_STATUS_NOTFOUND";
 
681
                case NSS_STATUS_UNAVAIL:
 
682
                        return "NSS_STATUS_UNAVAIL";
 
683
#ifdef NSS_STATUS_RETURN
 
684
                case NSS_STATUS_RETURN:
 
685
                        return "NSS_STATUS_RETURN";
 
686
#endif
 
687
                default:
 
688
                        return "UNKNOWN RETURN CODE!!!!!!!";
 
689
        }
 
690
}