pyldb: avoid segfault when adding an element with no name
[kai/samba-autobuild/.git] / source3 / winbindd / wb_queryuser.c
1 /*
2    Unix SMB/CIFS implementation.
3    async queryuser
4    Copyright (C) Volker Lendecke 2009
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "winbindd.h"
22 #include "librpc/gen_ndr/ndr_winbind_c.h"
23 #include "../libcli/security/security.h"
24 #include "libsmb/samlogon_cache.h"
25
26 struct wb_queryuser_state {
27         struct tevent_context *ev;
28         struct wbint_userinfo *info;
29         bool tried_dclookup;
30 };
31
32 static void wb_queryuser_got_uid(struct tevent_req *subreq);
33 static void wb_queryuser_got_domain(struct tevent_req *subreq);
34 static void wb_queryuser_got_dc(struct tevent_req *subreq);
35 static void wb_queryuser_got_gid(struct tevent_req *subreq);
36 static void wb_queryuser_got_group_name(struct tevent_req *subreq);
37 static void wb_queryuser_done(struct tevent_req *subreq);
38
39 struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
40                                      struct tevent_context *ev,
41                                      const struct dom_sid *user_sid)
42 {
43         struct tevent_req *req, *subreq;
44         struct wb_queryuser_state *state;
45         struct wbint_userinfo *info;
46
47         req = tevent_req_create(mem_ctx, &state, struct wb_queryuser_state);
48         if (req == NULL) {
49                 return NULL;
50         }
51         state->ev = ev;
52
53         state->info = talloc_zero(state, struct wbint_userinfo);
54         if (tevent_req_nomem(state->info, req)) {
55                 return tevent_req_post(req, ev);
56         }
57         info = state->info;
58
59         info->primary_gid = (gid_t)-1;
60
61         sid_copy(&info->user_sid, user_sid);
62
63         subreq = wb_sids2xids_send(
64                 state, state->ev, &state->info->user_sid, 1);
65         if (tevent_req_nomem(subreq, req)) {
66                 return tevent_req_post(req, ev);
67         }
68         tevent_req_set_callback(subreq, wb_queryuser_got_uid, req);
69         return req;
70 }
71
72 static void wb_queryuser_got_uid(struct tevent_req *subreq)
73 {
74         struct tevent_req *req = tevent_req_callback_data(
75                 subreq, struct tevent_req);
76         struct wb_queryuser_state *state = tevent_req_data(
77                 req, struct wb_queryuser_state);
78         struct wbint_userinfo *info = state->info;
79         struct netr_SamInfo3 *info3;
80         struct winbindd_child *child;
81         struct unixid xid;
82         NTSTATUS status;
83
84         status = wb_sids2xids_recv(subreq, &xid, 1);
85         TALLOC_FREE(subreq);
86         if (tevent_req_nterror(req, status)) {
87                 return;
88         }
89
90         if ((xid.type != ID_TYPE_UID) && (xid.type != ID_TYPE_BOTH)) {
91                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
92                 return;
93         }
94
95         info->uid = xid.id;
96
97         /*
98          * Default the group sid to "Domain Users" in the user's
99          * domain. The samlogon cache or the query_user call later on
100          * can override this.
101          */
102         sid_copy(&info->group_sid, &info->user_sid);
103         sid_split_rid(&info->group_sid, NULL);
104         sid_append_rid(&info->group_sid, DOMAIN_RID_USERS);
105
106         info->homedir = talloc_strdup(info, lp_template_homedir());
107         if (tevent_req_nomem(info->homedir, req)) {
108                 return;
109         }
110
111         info->shell = talloc_strdup(info, lp_template_shell());
112         if (tevent_req_nomem(info->shell, req)) {
113                 return;
114         }
115
116         info3 = netsamlogon_cache_get(state, &info->user_sid);
117         if (info3 != NULL) {
118
119                 sid_compose(&info->group_sid, info3->base.domain_sid,
120                             info3->base.primary_gid);
121                 info->acct_name = talloc_move(
122                         info, &info3->base.account_name.string);
123                 info->full_name = talloc_move(
124                         info, &info3->base.full_name.string);
125
126                 info->domain_name = talloc_move(
127                         state, &info3->base.logon_domain.string);
128
129                 TALLOC_FREE(info3);
130         }
131
132         if (info->domain_name == NULL) {
133                 subreq = wb_lookupsid_send(state, state->ev, &info->user_sid);
134                 if (tevent_req_nomem(subreq, req)) {
135                         return;
136                 }
137                 tevent_req_set_callback(subreq, wb_queryuser_got_domain, req);
138                 return;
139         }
140
141         child = idmap_child();
142
143         subreq = dcerpc_wbint_GetNssInfo_send(
144                 state, state->ev, child->binding_handle, info);
145         if (tevent_req_nomem(subreq, req)) {
146                 return;
147         }
148         tevent_req_set_callback(subreq, wb_queryuser_done, req);
149 }
150
151 static void wb_queryuser_got_domain(struct tevent_req *subreq)
152 {
153         struct tevent_req *req = tevent_req_callback_data(
154                 subreq, struct tevent_req);
155         struct wb_queryuser_state *state = tevent_req_data(
156                 req, struct wb_queryuser_state);
157         struct wbint_userinfo *info = state->info;
158         enum lsa_SidType type;
159         struct winbindd_child *child;
160         NTSTATUS status;
161
162         status = wb_lookupsid_recv(subreq, state, &type,
163                                    &info->domain_name, &info->acct_name);
164         TALLOC_FREE(subreq);
165         if (tevent_req_nterror(req, status)) {
166                 return;
167         }
168
169         if (type != SID_NAME_USER) {
170                 /* allow SID_NAME_COMPUTER? */
171                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
172                 return;
173         }
174
175         child = idmap_child();
176
177         subreq = dcerpc_wbint_GetNssInfo_send(
178                 state, state->ev, child->binding_handle, info);
179         if (tevent_req_nomem(subreq, req)) {
180                 return;
181         }
182         tevent_req_set_callback(subreq, wb_queryuser_done, req);
183 }
184
185 static void wb_queryuser_done(struct tevent_req *subreq)
186 {
187         struct tevent_req *req = tevent_req_callback_data(
188                 subreq, struct tevent_req);
189         struct wb_queryuser_state *state = tevent_req_data(
190                 req, struct wb_queryuser_state);
191         struct wbint_userinfo *info = state->info;
192         NTSTATUS status, result;
193         bool need_group_name = false;
194         const char *tmpl = NULL;
195
196         status = dcerpc_wbint_GetNssInfo_recv(subreq, info, &result);
197         TALLOC_FREE(subreq);
198         if (tevent_req_nterror(req, status)) {
199                 return;
200         }
201
202         if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
203             !state->tried_dclookup) {
204                 subreq = wb_dsgetdcname_send(
205                         state, state->ev, state->info->domain_name, NULL, NULL,
206                         DS_RETURN_DNS_NAME);
207                 if (tevent_req_nomem(subreq, req)) {
208                         return;
209                 }
210                 tevent_req_set_callback(subreq, wb_queryuser_got_dc, req);
211                 return;
212         }
213
214         /*
215          * Ignore failure in "result" here. We'll try to fill in stuff
216          * that misses further down.
217          */
218
219         if (state->info->primary_gid == (gid_t)-1) {
220                 subreq = wb_sids2xids_send(
221                         state, state->ev, &info->group_sid, 1);
222                 if (tevent_req_nomem(subreq, req)) {
223                         return;
224                 }
225                 tevent_req_set_callback(subreq, wb_queryuser_got_gid, req);
226                 return;
227         }
228
229         tmpl = lp_template_homedir();
230         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
231                 need_group_name = true;
232         }
233         tmpl = lp_template_shell();
234         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
235                 need_group_name = true;
236         }
237
238         if (need_group_name && state->info->primary_group_name == NULL) {
239                 subreq = wb_lookupsid_send(state, state->ev, &info->group_sid);
240                 if (tevent_req_nomem(subreq, req)) {
241                         return;
242                 }
243                 tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
244                                         req);
245                 return;
246         }
247
248         tevent_req_done(req);
249 }
250
251 static void wb_queryuser_got_dc(struct tevent_req *subreq)
252 {
253         struct tevent_req *req = tevent_req_callback_data(
254                 subreq, struct tevent_req);
255         struct wb_queryuser_state *state = tevent_req_data(
256                 req, struct wb_queryuser_state);
257         struct wbint_userinfo *info = state->info;
258         struct netr_DsRGetDCNameInfo *dcinfo;
259         struct winbindd_child *child;
260         NTSTATUS status;
261
262         status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
263         TALLOC_FREE(subreq);
264         if (tevent_req_nterror(req, status)) {
265                 return;
266         }
267
268         state->tried_dclookup = true;
269
270         status = wb_dsgetdcname_gencache_set(info->domain_name, dcinfo);
271         if (tevent_req_nterror(req, status)) {
272                 return;
273         }
274
275         child = idmap_child();
276
277         subreq = dcerpc_wbint_GetNssInfo_send(
278                 state, state->ev, child->binding_handle, info);
279         if (tevent_req_nomem(subreq, req)) {
280                 return;
281         }
282         tevent_req_set_callback(subreq, wb_queryuser_done, req);
283 }
284
285 static void wb_queryuser_got_gid(struct tevent_req *subreq)
286 {
287         struct tevent_req *req = tevent_req_callback_data(
288                 subreq, struct tevent_req);
289         struct wb_queryuser_state *state = tevent_req_data(
290                 req, struct wb_queryuser_state);
291         struct unixid xid;
292         NTSTATUS status;
293         bool need_group_name = false;
294         const char *tmpl = NULL;
295
296         status = wb_sids2xids_recv(subreq, &xid, 1);
297         TALLOC_FREE(subreq);
298         if (tevent_req_nterror(req, status)) {
299                 return;
300         }
301
302         if ((xid.type != ID_TYPE_GID) && (xid.type != ID_TYPE_BOTH)) {
303                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_USER);
304                 return;
305         }
306
307         state->info->primary_gid = xid.id;
308
309         tmpl = lp_template_homedir();
310         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
311                 need_group_name = true;
312         }
313         tmpl = lp_template_shell();
314         if(strstr_m(tmpl, "%g") || strstr_m(tmpl, "%G")) {
315                 need_group_name = true;
316         }
317
318         if (need_group_name && state->info->primary_group_name == NULL) {
319                 subreq = wb_lookupsid_send(state, state->ev,
320                                            &state->info->group_sid);
321                 if (tevent_req_nomem(subreq, req)) {
322                         return;
323                 }
324                 tevent_req_set_callback(subreq, wb_queryuser_got_group_name,
325                                         req);
326                 return;
327         }
328
329         tevent_req_done(req);
330 }
331
332 static void wb_queryuser_got_group_name(struct tevent_req *subreq)
333 {
334         struct tevent_req *req = tevent_req_callback_data(
335                 subreq, struct tevent_req);
336         struct wb_queryuser_state *state = tevent_req_data(
337                 req, struct wb_queryuser_state);
338         enum lsa_SidType type;
339         NTSTATUS status;
340         const char *domain_name;
341
342         status = wb_lookupsid_recv(subreq, state->info, &type, &domain_name,
343                                    &state->info->primary_group_name);
344         TALLOC_FREE(subreq);
345         if (tevent_req_nterror(req, status)) {
346                 return;
347         }
348         tevent_req_done(req);
349 }
350
351 NTSTATUS wb_queryuser_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
352                            struct wbint_userinfo **pinfo)
353 {
354         struct wb_queryuser_state *state = tevent_req_data(
355                 req, struct wb_queryuser_state);
356         NTSTATUS status;
357
358         if (tevent_req_is_nterror(req, &status)) {
359                 return status;
360         }
361         *pinfo = talloc_move(mem_ctx, &state->info);
362         return NT_STATUS_OK;
363 }