merge IRIX winbind support from Samba 2.2 branch
[samba.git] / source3 / nsswitch / wb_common.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    winbind client common code
6
7    Copyright (C) Tim Potter 2000
8    Copyright (C) Andrew Tridgell 2000
9    
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Library General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14    
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19    
20    You should have received a copy of the GNU Library General Public
21    License along with this library; if not, write to the
22    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23    Boston, MA  02111-1307, USA.   
24 */
25
26 #include "winbind_nss_config.h"
27 #include "winbindd_nss.h"
28
29 /* Global variables.  These are effectively the client state information */
30
31 int winbindd_fd = -1;           /* fd for winbindd socket */
32 static char *excluded_domain;
33
34 /* Free a response structure */
35
36 void free_response(struct winbindd_response *response)
37 {
38         /* Free any allocated extra_data */
39
40         if (response)
41                 SAFE_FREE(response->extra_data);
42 }
43
44 /*
45   smbd needs to be able to exclude lookups for its own domain
46 */
47 void winbind_exclude_domain(const char *domain)
48 {
49         SAFE_FREE(excluded_domain);
50         excluded_domain = strdup(domain);
51 }
52
53
54 /* Initialise a request structure */
55
56 void init_request(struct winbindd_request *request, int request_type)
57 {
58         static char *domain_env;
59         static BOOL initialised;
60
61         request->cmd = (enum winbindd_cmd)request_type;
62         request->pid = getpid();
63         request->domain[0] = '\0';
64
65         if (!initialised) {
66                 initialised = True;
67                 domain_env = getenv(WINBINDD_DOMAIN_ENV);
68         }
69
70         if (domain_env) {
71                 strncpy(request->domain, domain_env,
72                         sizeof(request->domain) - 1);
73                 request->domain[sizeof(request->domain) - 1] = '\0';
74         }
75 }
76
77 /* Initialise a response structure */
78
79 void init_response(struct winbindd_response *response)
80 {
81         /* Initialise return value */
82
83         response->result = WINBINDD_ERROR;
84 }
85
86 /* Close established socket */
87
88 void close_sock(void)
89 {
90         if (winbindd_fd != -1) {
91                 close(winbindd_fd);
92                 winbindd_fd = -1;
93         }
94 }
95
96 /* Connect to winbindd socket */
97
98 int winbind_open_pipe_sock(void)
99 {
100         struct sockaddr_un sunaddr;
101         static pid_t our_pid;
102         struct stat st;
103         pstring path;
104         
105         if (our_pid != getpid()) {
106                 close_sock();
107                 our_pid = getpid();
108         }
109         
110         if (winbindd_fd != -1) {
111                 return winbindd_fd;
112         }
113         
114         /* Check permissions on unix socket directory */
115         
116         if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
117                 return -1;
118         }
119         
120         if (!S_ISDIR(st.st_mode) || 
121             (st.st_uid != 0 && st.st_uid != geteuid())) {
122                 return -1;
123         }
124         
125         /* Connect to socket */
126         
127         strncpy(path, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
128         path[sizeof(path) - 1] = '\0';
129         
130         strncat(path, "/", sizeof(path) - 1);
131         path[sizeof(path) - 1] = '\0';
132         
133         strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
134         path[sizeof(path) - 1] = '\0';
135         
136         ZERO_STRUCT(sunaddr);
137         sunaddr.sun_family = AF_UNIX;
138         strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
139         
140         /* If socket file doesn't exist, don't bother trying to connect
141            with retry.  This is an attempt to make the system usable when
142            the winbindd daemon is not running. */
143
144         if (lstat(path, &st) == -1) {
145                 return -1;
146         }
147         
148         /* Check permissions on unix socket file */
149         
150         if (!S_ISSOCK(st.st_mode) || 
151             (st.st_uid != 0 && st.st_uid != geteuid())) {
152                 return -1;
153         }
154         
155         /* Connect to socket */
156         
157         if ((winbindd_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
158                 return -1;
159         }
160         
161         if (connect(winbindd_fd, (struct sockaddr *)&sunaddr, 
162                     sizeof(sunaddr)) == -1) {
163                 close_sock();
164                 return -1;
165         }
166         
167         return winbindd_fd;
168 }
169
170 /* Write data to winbindd socket with timeout */
171
172 int write_sock(void *buffer, int count)
173 {
174         int result, nwritten;
175         
176         /* Open connection to winbind daemon */
177         
178  restart:
179         
180         if (winbind_open_pipe_sock() == -1) {
181                 return -1;
182         }
183         
184         /* Write data to socket */
185         
186         nwritten = 0;
187         
188         while(nwritten < count) {
189                 struct timeval tv;
190                 fd_set r_fds;
191                 
192                 /* Catch pipe close on other end by checking if a read()
193                    call would not block by calling select(). */
194
195                 FD_ZERO(&r_fds);
196                 FD_SET(winbindd_fd, &r_fds);
197                 ZERO_STRUCT(tv);
198                 
199                 if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
200                         close_sock();
201                         return -1;                   /* Select error */
202                 }
203                 
204                 /* Write should be OK if fd not available for reading */
205                 
206                 if (!FD_ISSET(winbindd_fd, &r_fds)) {
207                         
208                         /* Do the write */
209                         
210                         result = write(winbindd_fd,
211                                        (char *)buffer + nwritten, 
212                                        count - nwritten);
213                         
214                         if ((result == -1) || (result == 0)) {
215                                 
216                                 /* Write failed */
217                                 
218                                 close_sock();
219                                 return -1;
220                         }
221                         
222                         nwritten += result;
223                         
224                 } else {
225                         
226                         /* Pipe has closed on remote end */
227                         
228                         close_sock();
229                         goto restart;
230                 }
231         }
232         
233         return nwritten;
234 }
235
236 /* Read data from winbindd socket with timeout */
237
238 static int read_sock(void *buffer, int count)
239 {
240         int result = 0, nread = 0;
241
242         /* Read data from socket */
243         
244         while(nread < count) {
245                 
246                 result = read(winbindd_fd, (char *)buffer + nread, 
247                               count - nread);
248                 
249                 if ((result == -1) || (result == 0)) {
250                         
251                         /* Read failed.  I think the only useful thing we
252                            can do here is just return -1 and fail since the
253                            transaction has failed half way through. */
254                         
255                         close_sock();
256                         return -1;
257                 }
258                 
259                 nread += result;
260         }
261         
262         return result;
263 }
264
265 /* Read reply */
266
267 int read_reply(struct winbindd_response *response)
268 {
269         int result1, result2 = 0;
270
271         if (!response) {
272                 return -1;
273         }
274         
275         /* Read fixed length response */
276         
277         if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
278             == -1) {
279                 
280                 return -1;
281         }
282         
283         /* We actually send the pointer value of the extra_data field from
284            the server.  This has no meaning in the client's address space
285            so we clear it out. */
286
287         response->extra_data = NULL;
288
289         /* Read variable length response */
290         
291         if (response->length > sizeof(struct winbindd_response)) {
292                 int extra_data_len = response->length - 
293                         sizeof(struct winbindd_response);
294                 
295                 /* Mallocate memory for extra data */
296                 
297                 if (!(response->extra_data = malloc(extra_data_len))) {
298                         return -1;
299                 }
300                 
301                 if ((result2 = read_sock(response->extra_data, extra_data_len))
302                     == -1) {
303                         free_response(response);
304                         return -1;
305                 }
306         }
307         
308         /* Return total amount of data read */
309         
310         return result1 + result2;
311 }
312
313 /* 
314  * send simple types of requests 
315  */
316
317 NSS_STATUS winbindd_send_request(int req_type, struct winbindd_request *request)
318 {
319         struct winbindd_request lrequest;
320
321         /* Check for our tricky environment variable */
322
323         if (getenv(WINBINDD_DONT_ENV)) {
324                 return NSS_STATUS_NOTFOUND;
325         }
326
327         /* smbd may have excluded this domain */
328         if (excluded_domain && 
329             strcasecmp(excluded_domain, request->domain) == 0) {
330                 return NSS_STATUS_NOTFOUND;
331         }
332
333         if (!request) {
334                 ZERO_STRUCT(lrequest);
335                 request = &lrequest;
336         }
337         
338         /* Fill in request and send down pipe */
339
340         init_request(request, req_type);
341         
342         if (write_sock(request, sizeof(*request)) == -1) {
343                 return NSS_STATUS_UNAVAIL;
344         }
345         
346         return NSS_STATUS_SUCCESS;
347 }
348
349 /*
350  * Get results from winbindd request
351  */
352
353 NSS_STATUS winbindd_get_response(struct winbindd_response *response)
354 {
355         struct winbindd_response lresponse;
356
357         if (!response) {
358                 ZERO_STRUCT(lresponse);
359                 response = &lresponse;
360         }
361
362         init_response(response);
363
364         /* Wait for reply */
365         if (read_reply(response) == -1) {
366                 return NSS_STATUS_UNAVAIL;
367         }
368
369         /* Throw away extra data if client didn't request it */
370         if (response == &lresponse) {
371                 free_response(response);
372         }
373
374         /* Copy reply data from socket */
375         if (response->result != WINBINDD_OK) {
376                 return NSS_STATUS_NOTFOUND;
377         }
378         
379         return NSS_STATUS_SUCCESS;
380 }
381
382 /* Handle simple types of requests */
383
384 NSS_STATUS winbindd_request(int req_type, 
385                                  struct winbindd_request *request,
386                                  struct winbindd_response *response)
387 {
388         NSS_STATUS status;
389
390         status = winbindd_send_request(req_type, request);
391         if (status != NSS_STATUS_SUCCESS) 
392                 return(status);
393         return winbindd_get_response(response);
394 }