s4:rpc_srv:getncchanges: 4.5 anc emulation uses qsort(), not ldb_qsort()
[samba.git] / source3 / winbindd / idmap_nss.c
blob0af2536221906396e64f38955e7b9c2258bf8499
1 /*
2 Unix SMB/CIFS implementation.
4 idmap NSS backend
6 Copyright (C) Simo Sorce 2006
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.
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.
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/>.
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"
32 #undef DBGC_CLASS
33 #define DBGC_CLASS DBGC_IDMAP
35 struct idmap_nss_context {
36 struct idmap_domain *dom;
37 bool use_upn;
40 static int idmap_nss_context_destructor(struct idmap_nss_context *ctx)
42 if ((ctx->dom != NULL) && (ctx->dom->private_data == ctx)) {
43 ctx->dom->private_data = NULL;
45 return 0;
48 static NTSTATUS idmap_nss_context_create(TALLOC_CTX *mem_ctx,
49 struct idmap_domain *dom,
50 struct idmap_nss_context **pctx)
52 struct idmap_nss_context *ctx = NULL;
54 ctx = talloc_zero(mem_ctx, struct idmap_nss_context);
55 if (ctx == NULL) {
56 return NT_STATUS_NO_MEMORY;
58 ctx->dom = dom;
60 talloc_set_destructor(ctx, idmap_nss_context_destructor);
62 ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
64 *pctx = ctx;
65 return NT_STATUS_OK;
68 static NTSTATUS idmap_nss_get_context(struct idmap_domain *dom,
69 struct idmap_nss_context **pctx)
71 struct idmap_nss_context *ctx = NULL;
72 NTSTATUS status;
74 if (dom->private_data != NULL) {
75 *pctx = talloc_get_type_abort(dom->private_data,
76 struct idmap_nss_context);
77 return NT_STATUS_OK;
80 status = idmap_nss_context_create(dom, dom, &ctx);
81 if (!NT_STATUS_IS_OK(status)) {
82 DBG_WARNING("idmap_nss_context_create failed: %s\n",
83 nt_errstr(status));
84 return status;
87 dom->private_data = ctx;
88 *pctx = ctx;
89 return NT_STATUS_OK;
92 static bool idmap_nss_msg_filter(struct messaging_rec *rec, void *private_data)
94 struct idmap_domain *dom = talloc_get_type_abort(private_data,
95 struct idmap_domain);
96 struct idmap_nss_context *ctx = NULL;
97 NTSTATUS status;
98 bool ret;
100 if (rec->msg_type == MSG_SMB_CONF_UPDATED) {
101 ret = lp_load_global(get_dyn_CONFIGFILE());
102 if (!ret) {
103 DBG_WARNING("Failed to reload configuration\n");
104 return false;
107 status = idmap_nss_get_context(dom, &ctx);
108 if (NT_STATUS_IS_ERR(status)) {
109 DBG_WARNING("Failed to get idmap nss context: %s\n",
110 nt_errstr(status));
111 return false;
114 ctx->use_upn = idmap_config_bool(dom->name, "use_upn", false);
117 return false;
120 /*****************************
121 Initialise idmap database.
122 *****************************/
124 static NTSTATUS idmap_nss_int_init(struct idmap_domain *dom)
126 struct idmap_nss_context *ctx = NULL;
127 NTSTATUS status;
128 struct messaging_context *msg_ctx = global_messaging_context();
129 struct tevent_req *req = NULL;
131 status = idmap_nss_context_create(dom, dom, &ctx);
132 if (NT_STATUS_IS_ERR(status)) {
133 return status;
136 dom->private_data = ctx;
138 req = messaging_filtered_read_send(
139 dom,
140 messaging_tevent_context(msg_ctx),
141 msg_ctx,
142 idmap_nss_msg_filter,
143 dom);
144 if (req == NULL) {
145 DBG_WARNING("messaging_filtered_read_send failed\n");
146 return NT_STATUS_UNSUCCESSFUL;
149 return status;
152 static NTSTATUS idmap_nss_lookup_name(const char *namespace,
153 const char *username,
154 struct dom_sid *sid,
155 enum lsa_SidType *type)
157 bool ret;
160 * By default calls to winbindd are disabled
161 * the following call will not recurse so this is safe
163 (void)winbind_on();
164 ret = winbind_lookup_name(namespace, username, sid, type);
165 (void)winbind_off();
167 if (!ret) {
168 DBG_NOTICE("Failed to lookup name [%s] in namespace [%s]\n",
169 username, namespace);
170 return NT_STATUS_NOT_FOUND;
173 return NT_STATUS_OK;
176 /**********************************
177 lookup a set of unix ids.
178 **********************************/
180 static NTSTATUS idmap_nss_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
182 struct idmap_nss_context *ctx = NULL;
183 NTSTATUS status;
184 int i;
186 status = idmap_nss_get_context(dom, &ctx);
187 if (NT_STATUS_IS_ERR(status)) {
188 DBG_WARNING("Failed to get idmap nss context: %s\n",
189 nt_errstr(status));
190 return status;
193 /* initialize the status to avoid surprise */
194 for (i = 0; ids[i]; i++) {
195 ids[i]->status = ID_UNKNOWN;
198 for (i = 0; ids[i]; i++) {
199 struct passwd *pw;
200 struct group *gr;
201 const char *name;
202 struct dom_sid sid;
203 enum lsa_SidType type;
205 switch (ids[i]->xid.type) {
206 case ID_TYPE_UID:
207 errno = 0;
208 pw = getpwuid((uid_t)ids[i]->xid.id);
209 if (!pw) {
210 DBG_DEBUG("getpwuid(%lu) failed: %s\n",
211 (unsigned long)ids[i]->xid.id,
212 errno != 0
213 ? strerror(errno)
214 : "not found");
215 ids[i]->status = ID_UNMAPPED;
216 continue;
218 name = pw->pw_name;
219 break;
220 case ID_TYPE_GID:
221 errno = 0;
222 gr = getgrgid((gid_t)ids[i]->xid.id);
223 if (!gr) {
224 DBG_DEBUG("getgrgid(%lu) failed: %s\n",
225 (unsigned long)ids[i]->xid.id,
226 errno != 0
227 ? strerror(errno)
228 : "not found");
229 ids[i]->status = ID_UNMAPPED;
230 continue;
232 name = gr->gr_name;
233 break;
234 default: /* ?? */
235 DBG_WARNING("Unexpected xid type %d\n",
236 ids[i]->xid.type);
237 ids[i]->status = ID_UNKNOWN;
238 continue;
241 /* Lookup name from PDC using lsa_lookup_names() */
242 if (ctx->use_upn) {
243 char *p = NULL;
244 const char *namespace = NULL;
245 const char *domname = NULL;
246 const char *domuser = NULL;
248 p = strstr(name, lp_winbind_separator());
249 if (p != NULL) {
250 *p = '\0';
251 domname = name;
252 namespace = domname;
253 domuser = p + 1;
254 } else {
255 p = strchr(name, '@');
256 if (p != NULL) {
257 *p = '\0';
258 namespace = p + 1;
259 domname = "";
260 domuser = name;
261 } else {
262 namespace = dom->name;
263 domuser = name;
267 DBG_DEBUG("Using namespace [%s] from UPN instead "
268 "of [%s] to lookup the name [%s]\n",
269 namespace, dom->name, domuser);
271 status = idmap_nss_lookup_name(namespace,
272 domuser,
273 &sid,
274 &type);
275 } else {
276 status = idmap_nss_lookup_name(dom->name,
277 name,
278 &sid,
279 &type);
282 if (NT_STATUS_IS_ERR(status)) {
284 * TODO: how do we know if the name is really
285 * not mapped, or something just failed ?
287 ids[i]->status = ID_UNMAPPED;
288 continue;
291 switch (type) {
292 case SID_NAME_USER:
293 if (ids[i]->xid.type == ID_TYPE_UID) {
294 sid_copy(ids[i]->sid, &sid);
295 ids[i]->status = ID_MAPPED;
297 break;
299 case SID_NAME_DOM_GRP:
300 case SID_NAME_ALIAS:
301 case SID_NAME_WKN_GRP:
302 if (ids[i]->xid.type == ID_TYPE_GID) {
303 sid_copy(ids[i]->sid, &sid);
304 ids[i]->status = ID_MAPPED;
306 break;
308 default:
309 ids[i]->status = ID_UNKNOWN;
310 break;
313 return NT_STATUS_OK;
316 /**********************************
317 lookup a set of sids.
318 **********************************/
320 static NTSTATUS idmap_nss_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
322 struct idmap_nss_context *ctx = NULL;
323 NTSTATUS status;
324 int i;
326 status = idmap_nss_get_context(dom, &ctx);
327 if (NT_STATUS_IS_ERR(status)) {
328 DBG_WARNING("Failed to get idmap nss context: %s\n",
329 nt_errstr(status));
330 return status;
333 /* initialize the status to avoid surprise */
334 for (i = 0; ids[i]; i++) {
335 ids[i]->status = ID_UNKNOWN;
338 for (i = 0; ids[i]; i++) {
339 struct group *gr;
340 enum lsa_SidType type;
341 const char *_domain = NULL;
342 const char *_name = NULL;
343 char *domain = NULL;
344 char *name = NULL;
345 char *fqdn = NULL;
346 char *sname = NULL;
347 bool ret;
349 /* by default calls to winbindd are disabled
350 the following call will not recurse so this is safe */
351 (void)winbind_on();
352 ret = winbind_lookup_sid(talloc_tos(),
353 ids[i]->sid,
354 &_domain,
355 &_name,
356 &type);
357 (void)winbind_off();
358 if (!ret) {
359 /* TODO: how do we know if the name is really not mapped,
360 * or something just failed ? */
361 ids[i]->status = ID_UNMAPPED;
362 continue;
365 domain = discard_const_p(char, _domain);
366 name = discard_const_p(char, _name);
368 if (!strequal(domain, dom->name)) {
369 struct dom_sid_buf buf;
370 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 ids[i]->status = ID_UNMAPPED;
374 continue;
377 if (ctx->use_upn) {
378 fqdn = talloc_asprintf(talloc_tos(),
379 "%s%s%s",
380 domain,
381 lp_winbind_separator(),
382 name);
383 if (fqdn == NULL) {
384 DBG_ERR("No memory\n");
385 ids[i]->status = ID_UNMAPPED;
386 continue;
388 DBG_DEBUG("Using UPN [%s] instead of plain name [%s]\n",
389 fqdn, name);
390 sname = fqdn;
391 } else {
392 sname = name;
395 switch (type) {
396 case SID_NAME_USER: {
397 struct passwd *pw;
399 /* this will find also all lower case name and use username level */
400 pw = Get_Pwnam_alloc(talloc_tos(), sname);
401 if (pw) {
402 ids[i]->xid.id = pw->pw_uid;
403 ids[i]->xid.type = ID_TYPE_UID;
404 ids[i]->status = ID_MAPPED;
406 TALLOC_FREE(pw);
407 break;
410 case SID_NAME_DOM_GRP:
411 case SID_NAME_ALIAS:
412 case SID_NAME_WKN_GRP:
414 gr = getgrnam(sname);
415 if (gr) {
416 ids[i]->xid.id = gr->gr_gid;
417 ids[i]->xid.type = ID_TYPE_GID;
418 ids[i]->status = ID_MAPPED;
420 break;
422 default:
423 ids[i]->status = ID_UNKNOWN;
424 break;
426 TALLOC_FREE(domain);
427 TALLOC_FREE(name);
428 TALLOC_FREE(fqdn);
430 return NT_STATUS_OK;
433 /**********************************
434 Close the idmap tdb instance
435 **********************************/
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,
443 NTSTATUS idmap_nss_init(TALLOC_CTX *mem_ctx)
445 return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "nss", &nss_methods);