b5183be121b409d228b25e9aff535e3d6d4c578e
[samba.git] / source4 / libcli / wbclient / wbclient.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind client library.
5
6    Copyright (C) 2008 Kai Blin  <kai@samba.org>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include <tevent.h>
24 #include "lib/util/tevent_unix.h"
25 #include "libcli/wbclient/wbclient.h"
26 #include "nsswitch/wb_reqtrans.h"
27 #include "system/network.h"
28 #include "libcli/util/error.h"
29 #include "libcli/security/dom_sid.h"
30
31 static int wb_simple_trans(struct tevent_context *ev, int fd,
32                            struct winbindd_request *wb_req,
33                            TALLOC_CTX *mem_ctx,
34                            struct winbindd_response **resp, int *err)
35 {
36         struct tevent_req *req;
37         bool polled;
38         int ret;
39
40         req = wb_simple_trans_send(ev, ev, NULL, fd, wb_req);
41         if (req == NULL) {
42                 *err = ENOMEM;
43                 return -1;
44         }
45
46         polled = tevent_req_poll(req, ev);
47         if (!polled) {
48                 *err = errno;
49                 DEBUG(10, ("tevent_req_poll returned %s\n",
50                            strerror(*err)));
51                 return -1;
52         }
53
54         ret = wb_simple_trans_recv(req, mem_ctx, resp, err);
55         TALLOC_FREE(req);
56         return ret;
57 }
58
59 static const char *winbindd_socket_dir(void)
60 {
61 #ifdef SOCKET_WRAPPER
62         const char *env_dir;
63
64         env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
65         if (env_dir) {
66                 return env_dir;
67         }
68 #endif
69
70         return WINBINDD_SOCKET_DIR;
71 }
72
73 static int winbindd_pipe_sock(void)
74 {
75         struct sockaddr_un sunaddr = {};
76         int ret, fd;
77         char *path;
78
79         ret = asprintf(&path, "%s/%s", winbindd_socket_dir(),
80                        WINBINDD_SOCKET_NAME);
81         if (ret == -1) {
82                 errno = ENOMEM;
83                 return -1;
84         }
85         sunaddr.sun_family = AF_UNIX;
86         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
87         free(path);
88
89         fd = socket(AF_UNIX, SOCK_STREAM, 0);
90         if (fd == -1) {
91                 return -1;
92         }
93
94         ret = connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
95         if (ret == -1) {
96                 int err = errno;
97                 close(fd);
98                 errno = err;
99                 return -1;
100         }
101
102         return fd;
103 }
104
105 NTSTATUS wbc_sids_to_xids(struct tevent_context *ev, struct id_map *ids,
106                           uint32_t count)
107 {
108         TALLOC_CTX *mem_ctx;
109         struct winbindd_request req = {};
110         struct winbindd_response *resp;
111         uint32_t i;
112         int fd, ret, err;
113         char *sids, *p;
114         size_t sidslen;
115
116         fd = winbindd_pipe_sock();
117         if (fd == -1) {
118                 return map_nt_error_from_unix_common(errno);
119         }
120
121         mem_ctx = talloc_new(NULL);
122         if (mem_ctx == NULL) {
123                 close(fd);
124                 return NT_STATUS_NO_MEMORY;
125         }
126
127         sidslen = count * (DOM_SID_STR_BUFLEN + 1);
128
129         sids = talloc_array(mem_ctx, char, sidslen);
130         if (sids == NULL) {
131                 close(fd);
132                 TALLOC_FREE(mem_ctx);
133                 return NT_STATUS_NO_MEMORY;
134         }
135
136         p = sids;
137         for (i=0; i<count; i++) {
138                 p += dom_sid_string_buf(ids[i].sid, p, sidslen - (p - sids));
139                 *p++ = '\n';
140         }
141         *p++ = '\0';
142
143         DEBUG(10, ("sids=\n%s", sids));
144
145         req.length = sizeof(struct winbindd_request);
146         req.cmd = WINBINDD_SIDS_TO_XIDS;
147         req.pid = getpid();
148         req.extra_data.data = sids;
149         req.extra_len = sidslen;
150
151         ret = wb_simple_trans(ev, fd, &req, mem_ctx, &resp, &err);
152         if (ret == -1) {
153                 return map_nt_error_from_unix_common(err);
154         }
155
156         close(fd);
157
158         if (resp->result != WINBINDD_OK || p == NULL) {
159                 return NT_STATUS_INTERNAL_ERROR;
160         }
161
162         p = resp->extra_data.data;
163
164         for (i=0; i<count; i++) {
165                 struct unixid *id = &ids[i].xid;
166                 char *q;
167
168                 switch (p[0]) {
169                 case 'U':
170                         id->type = ID_TYPE_UID;
171                         id->id = strtoul(p+1, &q, 10);
172                         break;
173                 case 'G':
174                         id->type = ID_TYPE_GID;
175                         id->id = strtoul(p+1, &q, 10);
176                         break;
177                 case 'B':
178                         id->type = ID_TYPE_BOTH;
179                         id->id = strtoul(p+1, &q, 10);
180                         break;
181                 default:
182                         id->type = ID_TYPE_NOT_SPECIFIED;
183                         id->id = UINT32_MAX;
184                         q = strchr(p, '\n');
185                         break;
186                 };
187                 ids[i].status = ID_MAPPED;
188
189                 if (q == NULL || q[0] != '\n') {
190                         TALLOC_FREE(mem_ctx);
191                         return NT_STATUS_INTERNAL_ERROR;
192                 }
193                 p = q+1;
194         }
195
196         return NT_STATUS_OK;
197 }
198
199 struct wbc_id_to_sid_state {
200         struct winbindd_request wbreq;
201         struct dom_sid sid;
202 };
203
204 static void wbc_id_to_sid_done(struct tevent_req *subreq);
205
206 static struct tevent_req *wbc_id_to_sid_send(TALLOC_CTX *mem_ctx,
207                                              struct tevent_context *ev,
208                                              int fd, const struct unixid *id)
209 {
210         struct tevent_req *req, *subreq;
211         struct wbc_id_to_sid_state *state;
212
213         req = tevent_req_create(mem_ctx, &state, struct wbc_id_to_sid_state);
214         if (req == NULL) {
215                 return NULL;
216         }
217
218         switch(id->type) {
219         case ID_TYPE_UID:
220                 state->wbreq.cmd = WINBINDD_UID_TO_SID;
221                 state->wbreq.data.uid = id->id;
222                 break;
223         case ID_TYPE_GID:
224                 state->wbreq.cmd = WINBINDD_GID_TO_SID;
225                 state->wbreq.data.gid = id->id;
226                 break;
227         default:
228                 tevent_req_error(req, ENOENT);
229                 return tevent_req_post(req, ev);
230         }
231
232         subreq = wb_simple_trans_send(state, ev, NULL, fd, &state->wbreq);
233         if (tevent_req_nomem(subreq, req)) {
234                 return tevent_req_post(req, ev);
235         }
236         tevent_req_set_callback(subreq, wbc_id_to_sid_done, req);
237         return req;
238 }
239
240 static void wbc_id_to_sid_done(struct tevent_req *subreq)
241 {
242         struct tevent_req *req = tevent_req_callback_data(
243                 subreq, struct tevent_req);
244         struct wbc_id_to_sid_state *state = tevent_req_data(
245                 req, struct wbc_id_to_sid_state);
246         struct winbindd_response *wbresp;
247         int ret, err;
248
249         ret = wb_simple_trans_recv(subreq, state, &wbresp, &err);
250         TALLOC_FREE(subreq);
251         if (ret == -1) {
252                 tevent_req_error(req, err);
253                 return;
254         }
255         if ((wbresp->result != WINBINDD_OK) ||
256             !dom_sid_parse(wbresp->data.sid.sid, &state->sid)) {
257                 tevent_req_error(req, ENOENT);
258                 return;
259         }
260         tevent_req_done(req);
261 }
262
263 static int wbc_id_to_sid_recv(struct tevent_req *req, struct dom_sid *sid)
264 {
265         struct wbc_id_to_sid_state *state = tevent_req_data(
266                 req, struct wbc_id_to_sid_state);
267         int err;
268
269         if (tevent_req_is_unix_error(req, &err)) {
270                 return err;
271         }
272         sid_copy(sid, &state->sid);
273         return 0;
274 }
275
276 struct wbc_ids_to_sids_state {
277         struct tevent_context *ev;
278         int fd;
279         struct id_map *ids;
280         uint32_t count;
281         uint32_t idx;
282 };
283
284 static void wbc_ids_to_sids_done(struct tevent_req *subreq);
285
286 static struct tevent_req *wbc_ids_to_sids_send(
287         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
288         int fd, struct id_map *ids, uint32_t count)
289 {
290         struct tevent_req *req, *subreq;
291         struct wbc_ids_to_sids_state *state;
292
293         req = tevent_req_create(mem_ctx, &state,
294                                 struct wbc_ids_to_sids_state);
295         if (req == NULL) {
296                 return NULL;
297         }
298         state->ev = ev;
299         state->fd = fd;
300         state->ids = ids;
301         state->count = count;
302
303         if (count == 0) {
304                 tevent_req_done(req);
305                 return tevent_req_post(req, ev);
306         }
307
308         subreq = wbc_id_to_sid_send(state, state->ev, state->fd,
309                                     &state->ids[state->idx].xid);
310         if (tevent_req_nomem(subreq, req)) {
311                 return tevent_req_post(req, ev);
312         }
313         tevent_req_set_callback(subreq, wbc_ids_to_sids_done, req);
314         return req;
315 }
316
317 static void wbc_ids_to_sids_done(struct tevent_req *subreq)
318 {
319         struct tevent_req *req = tevent_req_callback_data(
320                 subreq, struct tevent_req);
321         struct wbc_ids_to_sids_state *state = tevent_req_data(
322                 req, struct wbc_ids_to_sids_state);
323         struct id_map *id;
324         struct dom_sid sid;
325         int ret;
326
327         ret = wbc_id_to_sid_recv(subreq, &sid);
328         TALLOC_FREE(subreq);
329
330         id = &state->ids[state->idx];
331         if (ret == 0) {
332                 id->status = ID_MAPPED;
333                 id->sid = dom_sid_dup(state->ids, &sid);
334                 if (id->sid == NULL) {
335                         tevent_req_error(req, ENOMEM);
336                         return;
337                 }
338         } else {
339                 id->status = ID_UNMAPPED;
340                 id->sid = NULL;
341         }
342
343         state->idx += 1;
344         if (state->idx == state->count) {
345                 tevent_req_done(req);
346                 return;
347         }
348
349         subreq = wbc_id_to_sid_send(state, state->ev, state->fd,
350                                     &state->ids[state->idx].xid);
351         if (tevent_req_nomem(subreq, req)) {
352                 return;
353         }
354         tevent_req_set_callback(subreq, wbc_ids_to_sids_done, req);
355 }
356
357 static int wbc_ids_to_sids_recv(struct tevent_req *req)
358 {
359         int err;
360         if (tevent_req_is_unix_error(req, &err)) {
361                 return err;
362         }
363         return 0;
364 }
365
366 NTSTATUS wbc_xids_to_sids(struct tevent_context *ev, struct id_map *ids,
367                           uint32_t count)
368 {
369         struct tevent_req *req;
370         NTSTATUS status;
371         bool polled;
372         int ret, fd;
373
374         DEBUG(5, ("wbc_xids_to_sids called: %u ids\n", (unsigned)count));
375
376         fd = winbindd_pipe_sock();
377         if (fd == -1) {
378                 status = map_nt_error_from_unix_common(errno);
379                 DEBUG(10, ("winbindd_pipe_sock returned %s\n",
380                            strerror(errno)));
381                 return status;
382         }
383
384         req = wbc_ids_to_sids_send(ev, ev, fd, ids, count);
385         if (req == NULL) {
386                 status = NT_STATUS_NO_MEMORY;
387                 goto done;
388         }
389
390         polled = tevent_req_poll(req, ev);
391         if (!polled) {
392                 status = map_nt_error_from_unix_common(errno);
393                 DEBUG(10, ("tevent_req_poll returned %s\n",
394                            strerror(errno)));
395                 goto done;
396         }
397
398         ret = wbc_ids_to_sids_recv(req);
399         TALLOC_FREE(req);
400         if (ret != 0) {
401                 status = map_nt_error_from_unix_common(ret);
402                 DEBUG(10, ("tevent_req_poll returned %s\n",
403                            strerror(ret)));
404         } else {
405                 status = NT_STATUS_OK;
406         }
407
408 done:
409         close(fd);
410         return status;
411 }