be0b24cc5d898a42960d17004f909d9107b68a57
[kai/samba.git] / source / nsswitch / 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 "ntdom_config.h"
27 #include "winbindd_ntdom.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 with
115        retry.  This is an attempt to make the system usable when the
116        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() call would 
167            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, NULL, NULL, 
174                              &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, (char *)buffer + nwritten, 
186                            count - nwritten);
187
188             if ((result == -1) || (result == 0)) {
189
190                 /* Write failed */
191             
192                 close_sock();
193                 return -1;
194             }
195
196             nwritten += result;
197
198         } else {
199
200             /* Pipe has closed on remote end */
201
202             close_sock();
203             goto restart;
204         }
205     }
206     
207     return nwritten;
208 }
209
210 /* Read data from winbindd socket with timeout */
211
212 static int read_sock(void *buffer, int count)
213 {
214     int result, nread;
215
216     /* Read data from socket */
217
218     nread = 0;
219
220     while(nread < count) {
221
222         result = read(established_socket, (char *)buffer + nread, 
223                       count - nread);
224         
225         if ((result == -1) || (result == 0)) {
226
227             /* Read failed.  I think the only useful thing we can do here 
228                is just return -1 and fail since the transaction has failed
229                half way through. */
230             
231             close_sock();
232             return -1;
233         }
234         
235         nread += result;
236     }
237
238     return result;
239 }
240
241 /* Read reply */
242
243 int read_reply(struct winbindd_response *response)
244 {
245     int result1, result2;
246
247     if (!response) {
248         return -1;
249     }
250
251     /* Read fixed length response */
252
253     if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
254          == -1) {
255
256         return -1;
257     }
258
259     /* Read variable length response */
260
261     if (response->length > sizeof(struct winbindd_response)) {
262         int extra_data_len = response->length - 
263             sizeof(struct winbindd_response);
264
265         /* Mallocate memory for extra data */
266
267         if (!(response->extra_data = malloc(extra_data_len))) {
268             return -1;
269         }
270
271         if ((result2 = read_sock(response->extra_data, extra_data_len))
272             == -1) {
273
274             return -1;
275         }
276     }
277
278     /* Return total amount of data read */
279
280     return result1 + result2;
281 }
282