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