5d1f3431ccf1e830041d2366ceb4fb89e4d1b1f3
[tprouty/samba.git] / source / 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 static int established_socket = -1;           /* fd for winbindd socket */
32
33 /*
34  * Utility and helper functions
35  */
36
37 void init_request(struct winbindd_request *req,int rq_type)
38 {
39         static char *domain_env;
40         static BOOL initialised;
41
42         req->cmd = rq_type;
43         req->pid = getpid();
44         req->domain[0] = '\0';
45
46         if (!initialised) {
47                 initialised = True;
48                 domain_env = getenv(WINBINDD_DOMAIN_ENV);
49         }
50
51         if (domain_env) {
52                 strncpy(req->domain, domain_env,
53                         sizeof(req->domain) - 1);
54                 req->domain[sizeof(req->domain) - 1] = '\0';
55         }
56 }
57
58 /* Close established socket */
59
60 void close_sock(void)
61 {
62         if (established_socket != -1) {
63                 close(established_socket);
64                 established_socket = -1;
65         }
66 }
67
68 /* Connect to winbindd socket */
69
70 static int open_pipe_sock(void)
71 {
72         struct sockaddr_un sunaddr;
73         static pid_t our_pid;
74         struct stat st;
75         pstring path;
76         
77         if (our_pid != getpid()) {
78                 if (established_socket != -1) {
79                         close(established_socket);
80                 }
81                 established_socket = -1;
82                 our_pid = getpid();
83         }
84         
85         if (established_socket != -1) {
86                 return established_socket;
87         }
88         
89         /* Check permissions on unix socket directory */
90         
91         if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
92                 return -1;
93         }
94         
95         if (!S_ISDIR(st.st_mode) || (st.st_uid != 0)) {
96                 return -1;
97         }
98         
99         /* Connect to socket */
100         
101         strncpy(path, WINBINDD_SOCKET_DIR, sizeof(path) - 1);
102         path[sizeof(path) - 1] = '\0';
103         
104         strncat(path, "/", sizeof(path) - 1);
105         path[sizeof(path) - 1] = '\0';
106         
107         strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1);
108         path[sizeof(path) - 1] = '\0';
109         
110         ZERO_STRUCT(sunaddr);
111         sunaddr.sun_family = AF_UNIX;
112         strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
113         
114         /* If socket file doesn't exist, don't bother trying to connect
115            with retry.  This is an attempt to make the system usable when
116            the winbindd daemon is not running. */
117
118         if (lstat(path, &st) == -1) {
119                 return -1;
120         }
121         
122         /* Check permissions on unix socket file */
123         
124         if (!S_ISSOCK(st.st_mode) || (st.st_uid != 0)) {
125                 return -1;
126         }
127         
128         /* Connect to socket */
129         
130         if ((established_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
131                 return -1;
132         }
133         
134         if (connect(established_socket, (struct sockaddr *)&sunaddr, 
135                     sizeof(sunaddr)) == -1) {
136                 close_sock();
137                 return -1;
138         }
139         
140         return established_socket;
141 }
142
143 /* Write data to winbindd socket with timeout */
144
145 int write_sock(void *buffer, int count)
146 {
147         int result, nwritten;
148         
149         /* Open connection to winbind daemon */
150         
151  restart:
152         
153         if (open_pipe_sock() == -1) {
154                 return -1;
155         }
156         
157         /* Write data to socket */
158         
159         nwritten = 0;
160         
161         while(nwritten < count) {
162                 struct timeval tv;
163                 fd_set r_fds;
164                 int selret;
165                 
166                 /* Catch pipe close on other end by checking if a read()
167                    call would not block by calling select(). */
168
169                 FD_ZERO(&r_fds);
170                 FD_SET(established_socket, &r_fds);
171                 ZERO_STRUCT(tv);
172                 
173                 if ((selret = select(established_socket + 1, &r_fds, 
174                                      NULL, NULL, &tv)) == -1) {
175                         close_sock();
176                         return -1;                   /* Select error */
177                 }
178                 
179                 /* Write should be OK if fd not available for reading */
180                 
181                 if (!FD_ISSET(established_socket, &r_fds)) {
182                         
183                         /* Do the write */
184                         
185                         result = write(established_socket,
186                                        (char *)buffer + nwritten, 
187                                        count - nwritten);
188                         
189                         if ((result == -1) || (result == 0)) {
190                                 
191                                 /* Write failed */
192                                 
193                                 close_sock();
194                                 return -1;
195                         }
196                         
197                         nwritten += result;
198                         
199                 } else {
200                         
201                         /* Pipe has closed on remote end */
202                         
203                         close_sock();
204                         goto restart;
205                 }
206         }
207         
208         return nwritten;
209 }
210
211 /* Read data from winbindd socket with timeout */
212
213 static int read_sock(void *buffer, int count)
214 {
215         int result = 0, nread = 0;
216
217         /* Read data from socket */
218         
219         while(nread < count) {
220                 
221                 result = read(established_socket, (char *)buffer + nread, 
222                               count - nread);
223                 
224                 if ((result == -1) || (result == 0)) {
225                         
226                         /* Read failed.  I think the only useful thing we
227                            can do here is just return -1 and fail since the
228                            transaction has failed half way through. */
229                         
230                         close_sock();
231                         return -1;
232                 }
233                 
234                 nread += result;
235         }
236         
237         return result;
238 }
239
240 /* Read reply */
241
242 int read_reply(struct winbindd_response *response)
243 {
244         int result1, result2 = 0;
245
246         if (!response) {
247                 return -1;
248         }
249         
250         /* Read fixed length response */
251         
252         if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
253             == -1) {
254                 
255                 return -1;
256         }
257         
258         /* We actually send the pointer value of the extra_data field from
259            the server.  This has no meaning in the client's address space
260            so we clear it out. */
261
262         response->extra_data = NULL;
263
264         /* Read variable length response */
265         
266         if (response->length > sizeof(struct winbindd_response)) {
267                 int extra_data_len = response->length - 
268                         sizeof(struct winbindd_response);
269                 
270                 /* Mallocate memory for extra data */
271                 
272                 if (!(response->extra_data = malloc(extra_data_len))) {
273                         return -1;
274                 }
275                 
276                 if ((result2 = read_sock(response->extra_data, extra_data_len))
277                     == -1) {
278                         return -1;
279                 }
280         }
281         
282         /* Return total amount of data read */
283         
284         return result1 + result2;
285 }
286
287 /* Free a response structure */
288
289 void free_response(struct winbindd_response *response)
290 {
291         /* Free any allocated extra_data */
292
293         if (response && response->extra_data) {
294                 free(response->extra_data);
295                 response->extra_data = NULL;
296         }
297 }
298
299 /* Handle simple types of requests */
300
301 enum nss_status winbindd_request(int req_type, 
302                                  struct winbindd_request *request,
303                                  struct winbindd_response *response)
304 {
305         struct winbindd_request lrequest;
306         struct winbindd_response lresponse;
307
308         /* Check for our tricky environment variable */
309
310         if (getenv(WINBINDD_DONT_ENV)) {
311                 return NSS_STATUS_NOTFOUND;
312         }
313
314         if (!response) {
315                 ZERO_STRUCT(lresponse);
316                 response = &lresponse;
317         }
318
319         if (!request) {
320                 ZERO_STRUCT(lrequest);
321                 request = &lrequest;
322         }
323         
324         /* Fill in request and send down pipe */
325         init_request(request, req_type);
326         
327         if (write_sock(request, sizeof(*request)) == -1) {
328                 return NSS_STATUS_UNAVAIL;
329         }
330         
331         /* Wait for reply */
332         if (read_reply(response) == -1) {
333                 return NSS_STATUS_UNAVAIL;
334         }
335
336         /* Throw away extra data if client didn't request it */
337         if (response == &lresponse) {
338                 free_response(response);
339         }
340
341         /* Copy reply data from socket */
342         if (response->result != WINBINDD_OK) {
343                 return NSS_STATUS_NOTFOUND;
344         }
345         
346         return NSS_STATUS_SUCCESS;
347 }