Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : idmap NSS backend
5 :
6 : Copyright (C) Simo Sorce 2006
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 "system/passwd.h"
24 : #include "winbindd.h"
25 : #include "nsswitch/winbind_client.h"
26 : #include "idmap.h"
27 : #include "lib/winbind_util.h"
28 : #include "libcli/security/dom_sid.h"
29 : #include "lib/global_contexts.h"
30 : #include "messages.h"
31 :
32 : #undef DBGC_CLASS
33 : #define DBGC_CLASS DBGC_IDMAP
34 :
35 : struct idmap_nss_context {
36 : struct idmap_domain *dom;
37 : bool use_upn;
38 : };
39 :
40 0 : static int idmap_nss_context_destructor(struct idmap_nss_context *ctx)
41 : {
42 0 : if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) {
43 0 : ctx->dom->private_data = NULL;
44 : }
45 0 : return 0;
46 : }
47 :
48 0 : static NTSTATUS idmap_nss_context_create(TALLOC_CTX *mem_ctx,
49 : struct idmap_domain *dom,
50 : struct idmap_nss_context **pctx)
51 : {
52 0 : struct idmap_nss_context *ctx = NULL;
53 :
54 0 : ctx = talloc_zero(mem_ctx, struct idmap_nss_context);
55 0 : if (ctx == NULL) {
56 0 : return NT_STATUS_NO_MEMORY;
57 : }
58 0 : ctx->dom = dom;
59 :
60 0 : talloc_set_destructor(ctx, idmap_nss_context_destructor);
61 :
62 0 : ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
63 :
64 0 : *pctx = ctx;
65 0 : return NT_STATUS_OK;
66 : }
67 :
68 0 : static NTSTATUS idmap_nss_get_context(struct idmap_domain *dom,
69 : struct idmap_nss_context **pctx)
70 : {
71 0 : struct idmap_nss_context *ctx = NULL;
72 0 : NTSTATUS status;
73 :
74 0 : if (dom->private_data != NULL) {
75 0 : *pctx = talloc_get_type_abort(dom->private_data,
76 : struct idmap_nss_context);
77 0 : return NT_STATUS_OK;
78 : }
79 :
80 0 : status = idmap_nss_context_create(dom, dom, &ctx);
81 0 : if (!NT_STATUS_IS_OK(status)) {
82 0 : DBG_WARNING("idmap_nss_context_create failed: %s\n",
83 : nt_errstr(status));
84 0 : return status;
85 : }
86 :
87 0 : dom->private_data = ctx;
88 0 : *pctx = ctx;
89 0 : return NT_STATUS_OK;
90 : }
91 :
92 0 : static bool idmap_nss_msg_filter(struct messaging_rec *rec, void *private_data)
93 : {
94 0 : struct idmap_domain *dom = talloc_get_type_abort(private_data,
95 : struct idmap_domain);
96 0 : struct idmap_nss_context *ctx = NULL;
97 0 : NTSTATUS status;
98 0 : bool ret;
99 :
100 0 : if (rec->msg_type == MSG_SMB_CONF_UPDATED) {
101 0 : ret = lp_load_global(get_dyn_CONFIGFILE());
102 0 : if (!ret) {
103 0 : DBG_WARNING("Failed to reload configuration\n");
104 0 : return false;
105 : }
106 :
107 0 : status = idmap_nss_get_context(dom, &ctx);
108 0 : if (NT_STATUS_IS_ERR(status)) {
109 0 : DBG_WARNING("Failed to get idmap nss context: %s\n",
110 : nt_errstr(status));
111 0 : return false;
112 : }
113 :
114 0 : ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
115 : }
116 :
117 0 : return false;
118 : }
119 :
120 : /*****************************
121 : Initialise idmap database.
122 : *****************************/
123 :
124 0 : static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom)
125 : {
126 0 : struct idmap_nss_context *ctx = NULL;
127 0 : NTSTATUS status;
128 0 : struct messaging_context *msg_ctx = global_messaging_context();
129 0 : struct tevent_req *req = NULL;
130 :
131 0 : status = idmap_nss_context_create(dom, dom, &ctx);
132 0 : if (NT_STATUS_IS_ERR(status)) {
133 0 : return status;
134 : }
135 :
136 0 : dom->private_data = ctx;
137 :
138 0 : req = messaging_filtered_read_send(
139 : dom,
140 : messaging_tevent_context(msg_ctx),
141 : msg_ctx,
142 : idmap_nss_msg_filter,
143 : dom);
144 0 : if (req == NULL) {
145 0 : DBG_WARNING("messaging_filtered_read_send failed\n");
146 0 : return NT_STATUS_UNSUCCESSFUL;
147 : }
148 :
149 0 : return status;
150 : }
151 :
152 0 : static NTSTATUS idmap_nss_lookup_name(const char *namespace,
153 : const char *username,
154 : struct dom_sid *sid,
155 : enum lsa_SidType *type)
156 : {
157 0 : bool ret;
158 :
159 : /*
160 : * By default calls to winbindd are disabled
161 : * the following call will not recurse so this is safe
162 : */
163 0 : (void)winbind_on();
164 0 : ret = winbind_lookup_name(namespace, username, sid, type);
165 0 : (void)winbind_off();
166 :
167 0 : if (!ret) {
168 0 : DBG_NOTICE("Failed to lookup name [%s] in namespace [%s]\n",
169 : username, namespace);
170 0 : return NT_STATUS_NOT_FOUND;
171 : }
172 :
173 0 : return NT_STATUS_OK;
174 : }
175 :
176 : /**********************************
177 : lookup a set of unix ids.
178 : **********************************/
179 :
180 0 : static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
181 : {
182 0 : struct idmap_nss_context *ctx = NULL;
183 0 : NTSTATUS status;
184 0 : int i;
185 :
186 0 : status = idmap_nss_get_context(dom, &ctx);
187 0 : if (NT_STATUS_IS_ERR(status)) {
188 0 : DBG_WARNING("Failed to get idmap nss context: %s\n",
189 : nt_errstr(status));
190 0 : return status;
191 : }
192 :
193 : /* initialize the status to avoid surprise */
194 0 : for (i = 0; ids[i]; i++) {
195 0 : ids[i]->status = ID_UNKNOWN;
196 : }
197 :
198 0 : for (i = 0; ids[i]; i++) {
199 0 : struct passwd *pw;
200 0 : struct group *gr;
201 0 : const char *name;
202 0 : struct dom_sid sid;
203 0 : enum lsa_SidType type;
204 :
205 0 : switch (ids[i]->xid.type) {
206 0 : case ID_TYPE_UID:
207 0 : errno = 0;
208 0 : pw = getpwuid((uid_t)ids[i]->xid.id);
209 0 : if (!pw) {
210 0 : DBG_DEBUG("getpwuid(%lu) failed: %s\n",
211 : (unsigned long)ids[i]->xid.id,
212 : errno != 0
213 : ? strerror(errno)
214 : : "not found");
215 0 : ids[i]->status = ID_UNMAPPED;
216 0 : continue;
217 : }
218 0 : name = pw->pw_name;
219 0 : break;
220 0 : case ID_TYPE_GID:
221 0 : errno = 0;
222 0 : gr = getgrgid((gid_t)ids[i]->xid.id);
223 0 : if (!gr) {
224 0 : DBG_DEBUG("getgrgid(%lu) failed: %s\n",
225 : (unsigned long)ids[i]->xid.id,
226 : errno != 0
227 : ? strerror(errno)
228 : : "not found");
229 0 : ids[i]->status = ID_UNMAPPED;
230 0 : continue;
231 : }
232 0 : name = gr->gr_name;
233 0 : break;
234 0 : default: /* ?? */
235 0 : DBG_WARNING("Unexpected xid type %d\n",
236 : ids[i]->xid.type);
237 0 : ids[i]->status = ID_UNKNOWN;
238 0 : continue;
239 : }
240 :
241 : /* Lookup name from PDC using lsa_lookup_names() */
242 0 : if (ctx->use_upn) {
243 0 : char *p = NULL;
244 0 : const char *namespace = NULL;
245 0 : const char *domname = NULL;
246 0 : const char *domuser = NULL;
247 :
248 0 : p = strstr(name, lp_winbind_separator());
249 0 : if (p != NULL) {
250 0 : *p = '\0';
251 0 : domname = name;
252 0 : namespace = domname;
253 0 : domuser = p + 1;
254 : } else {
255 0 : p = strchr(name, '@');
256 0 : if (p != NULL) {
257 0 : *p = '\0';
258 0 : namespace = p + 1;
259 0 : domname = "";
260 0 : domuser = name;
261 : } else {
262 0 : namespace = dom->name;
263 0 : domuser = name;
264 : }
265 : }
266 :
267 0 : DBG_DEBUG("Using namespace [%s] from UPN instead "
268 : "of [%s] to lookup the name [%s]\n",
269 : namespace, dom->name, domuser);
270 :
271 0 : status = idmap_nss_lookup_name(namespace,
272 : domuser,
273 : &sid,
274 : &type);
275 : } else {
276 0 : status = idmap_nss_lookup_name(dom->name,
277 : name,
278 : &sid,
279 : &type);
280 : }
281 :
282 0 : if (NT_STATUS_IS_ERR(status)) {
283 : /*
284 : * TODO: how do we know if the name is really
285 : * not mapped, or something just failed ?
286 : */
287 0 : ids[i]->status = ID_UNMAPPED;
288 0 : continue;
289 : }
290 :
291 0 : switch (type) {
292 0 : case SID_NAME_USER:
293 0 : if (ids[i]->xid.type == ID_TYPE_UID) {
294 0 : sid_copy(ids[i]->sid, &sid);
295 0 : ids[i]->status = ID_MAPPED;
296 : }
297 0 : break;
298 :
299 0 : case SID_NAME_DOM_GRP:
300 : case SID_NAME_ALIAS:
301 : case SID_NAME_WKN_GRP:
302 0 : if (ids[i]->xid.type == ID_TYPE_GID) {
303 0 : sid_copy(ids[i]->sid, &sid);
304 0 : ids[i]->status = ID_MAPPED;
305 : }
306 0 : break;
307 :
308 0 : default:
309 0 : ids[i]->status = ID_UNKNOWN;
310 0 : break;
311 : }
312 : }
313 0 : return NT_STATUS_OK;
314 : }
315 :
316 : /**********************************
317 : lookup a set of sids.
318 : **********************************/
319 :
320 0 : static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
321 : {
322 0 : struct idmap_nss_context *ctx = NULL;
323 0 : NTSTATUS status;
324 0 : int i;
325 :
326 0 : status = idmap_nss_get_context(dom, &ctx);
327 0 : if (NT_STATUS_IS_ERR(status)) {
328 0 : DBG_WARNING("Failed to get idmap nss context: %s\n",
329 : nt_errstr(status));
330 0 : return status;
331 : }
332 :
333 : /* initialize the status to avoid surprise */
334 0 : for (i = 0; ids[i]; i++) {
335 0 : ids[i]->status = ID_UNKNOWN;
336 : }
337 :
338 0 : for (i = 0; ids[i]; i++) {
339 0 : struct group *gr;
340 0 : enum lsa_SidType type;
341 0 : const char *_domain = NULL;
342 0 : const char *_name = NULL;
343 0 : char *domain = NULL;
344 0 : char *name = NULL;
345 0 : char *fqdn = NULL;
346 0 : char *sname = NULL;
347 0 : bool ret;
348 :
349 : /* by default calls to winbindd are disabled
350 : the following call will not recurse so this is safe */
351 0 : (void)winbind_on();
352 0 : ret = winbind_lookup_sid(talloc_tos(),
353 0 : ids[i]->sid,
354 : &_domain,
355 : &_name,
356 : &type);
357 0 : (void)winbind_off();
358 0 : if (!ret) {
359 : /* TODO: how do we know if the name is really not mapped,
360 : * or something just failed ? */
361 0 : ids[i]->status = ID_UNMAPPED;
362 0 : continue;
363 : }
364 :
365 0 : domain = discard_const_p(char, _domain);
366 0 : name = discard_const_p(char, _name);
367 :
368 0 : if (!strequal(domain, dom->name)) {
369 0 : struct dom_sid_buf buf;
370 0 : DBG_ERR("DOMAIN[%s] ignoring SID[%s] belongs to %s [%s\\%s]\n",
371 : dom->name, dom_sid_str_buf(ids[i]->sid, &buf),
372 : sid_type_lookup(type), domain, name);
373 0 : ids[i]->status = ID_UNMAPPED;
374 0 : continue;
375 : }
376 :
377 0 : if (ctx->use_upn) {
378 0 : fqdn = talloc_asprintf(talloc_tos(),
379 : "%s%s%s",
380 : domain,
381 : lp_winbind_separator(),
382 : name);
383 0 : if (fqdn == NULL) {
384 0 : DBG_ERR("No memory\n");
385 0 : ids[i]->status = ID_UNMAPPED;
386 0 : continue;
387 : }
388 0 : DBG_DEBUG("Using UPN [%s] instead of plain name [%s]\n",
389 : fqdn, name);
390 0 : sname = fqdn;
391 : } else {
392 0 : sname = name;
393 : }
394 :
395 0 : switch (type) {
396 0 : case SID_NAME_USER: {
397 0 : struct passwd *pw;
398 :
399 : /* this will find also all lower case name and use username level */
400 0 : pw = Get_Pwnam_alloc(talloc_tos(), sname);
401 0 : if (pw) {
402 0 : ids[i]->xid.id = pw->pw_uid;
403 0 : ids[i]->xid.type = ID_TYPE_UID;
404 0 : ids[i]->status = ID_MAPPED;
405 : }
406 0 : TALLOC_FREE(pw);
407 0 : break;
408 : }
409 :
410 0 : case SID_NAME_DOM_GRP:
411 : case SID_NAME_ALIAS:
412 : case SID_NAME_WKN_GRP:
413 :
414 0 : gr = getgrnam(sname);
415 0 : if (gr) {
416 0 : ids[i]->xid.id = gr->gr_gid;
417 0 : ids[i]->xid.type = ID_TYPE_GID;
418 0 : ids[i]->status = ID_MAPPED;
419 : }
420 0 : break;
421 :
422 0 : default:
423 0 : ids[i]->status = ID_UNKNOWN;
424 0 : break;
425 : }
426 0 : TALLOC_FREE(domain);
427 0 : TALLOC_FREE(name);
428 0 : TALLOC_FREE(fqdn);
429 : }
430 0 : return NT_STATUS_OK;
431 : }
432 :
433 : /**********************************
434 : Close the idmap tdb instance
435 : **********************************/
436 :
437 : static const struct idmap_methods nss_methods = {
438 : .init = idmap_nss_int_init,
439 : .unixids_to_sids = idmap_nss_unixids_to_sids,
440 : .sids_to_unixids = idmap_nss_sids_to_unixids,
441 : };
442 :
443 0 : NTSTATUS idmap_nss_init(TALLOC_CTX *mem_ctx)
444 : {
445 0 : return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "nss", &nss_methods);
446 : }
|