9804 hal-set-property should support --direct option
[unleashed.git] / usr / src / cmd / idmap / idmapd / directory_provider_nsswitch.c
blob1d2d790cecb85a3923a64533d7f2f1a3e717e73a
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Retrieve directory information for standard UNIX users/groups.
28 * (NB: not just from files, but all nsswitch sources.)
31 #include <pwd.h>
32 #include <grp.h>
33 #include <malloc.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <netdb.h>
37 #include <libuutil.h>
38 #include <note.h>
39 #include <errno.h>
40 #include "idmapd.h"
41 #include "directory.h"
42 #include "directory_private.h"
43 #include <rpcsvc/idmap_prot.h>
44 #include "directory_server_impl.h"
45 #include "sidutil.h"
47 static directory_error_t machine_sid_dav(directory_values_rpc *lvals,
48 unsigned int rid);
49 static directory_error_t directory_provider_nsswitch_populate(
50 directory_entry_rpc *pent, struct passwd *pwd, struct group *grp,
51 idmap_utf8str_list *attrs);
54 * Retrieve information by name.
55 * Called indirectly through the directory_provider_static structure.
57 static
58 directory_error_t
59 directory_provider_nsswitch_get(
60 directory_entry_rpc *del,
61 idmap_utf8str_list *ids,
62 idmap_utf8str types,
63 idmap_utf8str_list *attrs)
65 int i;
67 RDLOCK_CONFIG();
69 /* 6835280 spurious lint error if the strlen is in the declaration */
70 int host_name_len = strlen(_idmapdstate.hostname);
71 char my_host_name[host_name_len + 1];
72 (void) strcpy(my_host_name, _idmapdstate.hostname);
74 /* We use len later, so this is not merely a workaround for 6835280 */
75 int machine_sid_len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
76 char my_machine_sid[machine_sid_len + 1];
77 (void) strcpy(my_machine_sid, _idmapdstate.cfg->pgcfg.machine_sid);
79 UNLOCK_CONFIG();
81 for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
82 struct passwd *pwd = NULL;
83 struct group *grp = NULL;
84 directory_error_t de;
85 int type;
88 * Extract the type for this particular ID.
89 * Advance to the next type, if it's there, else keep
90 * using this type until we run out of IDs.
92 type = *types;
93 if (*(types+1) != '\0')
94 types++;
97 * If this entry has already been handled, one way or another,
98 * skip it.
100 if (del[i].status != DIRECTORY_NOT_FOUND)
101 continue;
103 char *id = ids->idmap_utf8str_list_val[i];
105 if (type == DIRECTORY_ID_SID[0]) {
107 * Is it our SID?
108 * Check whether the first part matches, then a "-",
109 * then a single RID.
111 if (strncasecmp(id, my_machine_sid, machine_sid_len) !=
113 continue;
114 if (id[machine_sid_len] != '-')
115 continue;
116 char *p;
117 uint32_t rid =
118 strtoul(id + machine_sid_len + 1, &p, 10);
119 if (*p != '\0')
120 continue;
122 if (rid < LOCALRID_UID_MIN) {
123 /* Builtin, not handled here */
124 continue;
127 if (rid <= LOCALRID_UID_MAX) {
128 /* User */
129 errno = 0;
130 pwd = getpwuid(rid - LOCALRID_UID_MIN);
131 if (pwd == NULL) {
132 if (errno == 0) /* Not found */
133 continue;
134 char buf[40];
135 int err = errno;
136 (void) snprintf(buf, sizeof (buf),
137 "%d", err);
138 directory_entry_set_error(&del[i],
139 directory_error("errno.getpwuid",
140 "getpwuid: %2 (%1)",
141 buf, strerror(err), NULL));
142 continue;
144 } else if (rid >= LOCALRID_GID_MIN &&
145 rid <= LOCALRID_GID_MAX) {
146 /* Group */
147 errno = 0;
148 grp = getgrgid(rid - LOCALRID_GID_MIN);
149 if (grp == NULL) {
150 if (errno == 0) /* Not found */
151 continue;
152 char buf[40];
153 int err = errno;
154 (void) snprintf(buf, sizeof (buf),
155 "%d", err);
156 directory_entry_set_error(&del[i],
157 directory_error("errno.getgrgid",
158 "getgrgid: %2 (%1)",
159 buf, strerror(err), NULL));
160 continue;
162 } else
163 continue;
165 } else {
166 int id_len = strlen(id);
167 char name[id_len + 1];
168 char domain[id_len + 1];
170 split_name(name, domain, id);
172 if (domain[0] != '\0') {
173 if (!domain_eq(domain, my_host_name))
174 continue;
178 * If the caller has requested user or group
179 * information specifically, we only set one of
180 * pwd or grp.
181 * If the caller has requested either type, we try
182 * both in the hopes of getting one.
183 * Note that directory_provider_nsswitch_populate
184 * considers it to be an error if both are set.
186 if (type != DIRECTORY_ID_GROUP[0]) {
187 /* prep for not found / error case */
188 errno = 0;
190 pwd = getpwnam(name);
191 if (pwd == NULL && errno != 0) {
192 char buf[40];
193 int err = errno;
194 (void) snprintf(buf, sizeof (buf),
195 "%d", err);
196 directory_entry_set_error(&del[i],
197 directory_error("errno.getpwnam",
198 "getpwnam: %2 (%1)",
199 buf, strerror(err), NULL));
200 continue;
204 if (type != DIRECTORY_ID_USER[0]) {
205 /* prep for not found / error case */
206 errno = 0;
208 grp = getgrnam(name);
209 if (grp == NULL && errno != 0) {
210 char buf[40];
211 int err = errno;
212 (void) snprintf(buf, sizeof (buf),
213 "%d", err);
214 directory_entry_set_error(&del[i],
215 directory_error("errno.getgrnam",
216 "getgrnam: %2 (%1)",
217 buf, strerror(err), NULL));
218 continue;
224 * Didn't find it, don't populate the structure.
225 * Another provider might populate it.
227 if (pwd == NULL && grp == NULL)
228 continue;
230 de = directory_provider_nsswitch_populate(&del[i], pwd, grp,
231 attrs);
232 if (de != NULL) {
233 directory_entry_set_error(&del[i], de);
234 de = NULL;
235 continue;
239 return (NULL);
243 * Given a pwd structure or a grp structure, and a list of attributes that
244 * were requested, populate the structure to return to the caller.
246 static
247 directory_error_t
248 directory_provider_nsswitch_populate(
249 directory_entry_rpc *pent,
250 struct passwd *pwd,
251 struct group *grp,
252 idmap_utf8str_list *attrs)
254 int j;
255 directory_values_rpc *llvals;
256 int nattrs;
259 * If it wasn't for this case, everything would be a lot simpler.
260 * UNIX allows users and groups with the same name. Windows doesn't.
262 if (pwd != NULL && grp != NULL) {
263 return directory_error("Ambiguous.Name",
264 "Ambiguous name, is both a user and a group",
265 NULL);
268 nattrs = attrs->idmap_utf8str_list_len;
270 llvals = calloc(nattrs, sizeof (directory_values_rpc));
271 if (llvals == NULL)
272 goto nomem;
274 pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
275 pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
276 pent->status = DIRECTORY_FOUND;
278 for (j = 0; j < nattrs; j++) {
279 directory_values_rpc *val;
280 char *a;
281 directory_error_t de;
284 * We're going to refer to these a lot, so make a shorthand
285 * copy.
287 a = attrs->idmap_utf8str_list_val[j];
288 val = &llvals[j];
291 * Start by assuming no errors and that we don't have
292 * the information
294 val->found = FALSE;
295 de = NULL;
297 if (pwd != NULL) {
299 * Handle attributes for user entries.
301 if (uu_strcaseeq(a, "cn")) {
302 const char *p = pwd->pw_name;
303 de = str_list_dav(val, &p, 1);
304 } else if (uu_strcaseeq(a, "objectClass")) {
305 static const char *objectClasses[] = {
306 "top",
307 "posixAccount",
309 de = str_list_dav(val, objectClasses,
310 UU_NELEM(objectClasses));
311 } else if (uu_strcaseeq(a, "gidNumber")) {
312 de = uint_list_dav(val, &pwd->pw_gid, 1);
313 } else if (uu_strcaseeq(a, "objectSid")) {
314 de = machine_sid_dav(val,
315 pwd->pw_uid + LOCALRID_UID_MIN);
316 } else if (uu_strcaseeq(a, "displayName")) {
317 const char *p = pwd->pw_gecos;
318 de = str_list_dav(val, &p, 1);
319 } else if (uu_strcaseeq(a, "distinguishedName")) {
320 char *dn;
321 RDLOCK_CONFIG();
322 (void) asprintf(&dn,
323 "uid=%s,ou=people,dc=%s",
324 pwd->pw_name, _idmapdstate.hostname);
325 UNLOCK_CONFIG();
326 if (dn == NULL)
327 goto nomem;
328 const char *cdn = dn;
329 de = str_list_dav(val, &cdn, 1);
330 free(dn);
331 } else if (uu_strcaseeq(a, "uid")) {
332 const char *p = pwd->pw_name;
333 de = str_list_dav(val, &p, 1);
334 } else if (uu_strcaseeq(a, "uidNumber")) {
335 de = uint_list_dav(val, &pwd->pw_uid, 1);
336 } else if (uu_strcaseeq(a, "gecos")) {
337 const char *p = pwd->pw_gecos;
338 de = str_list_dav(val, &p, 1);
339 } else if (uu_strcaseeq(a, "homeDirectory")) {
340 const char *p = pwd->pw_dir;
341 de = str_list_dav(val, &p, 1);
342 } else if (uu_strcaseeq(a, "loginShell")) {
343 const char *p = pwd->pw_shell;
344 de = str_list_dav(val, &p, 1);
345 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
346 char *canon;
347 RDLOCK_CONFIG();
348 (void) asprintf(&canon, "%s@%s",
349 pwd->pw_name, _idmapdstate.hostname);
350 UNLOCK_CONFIG();
351 if (canon == NULL)
352 goto nomem;
353 const char *ccanon = canon;
354 de = str_list_dav(val, &ccanon, 1);
355 free(canon);
356 } else if (uu_strcaseeq(a, "x-sun-provider")) {
357 const char *provider = "UNIX-passwd";
358 de = str_list_dav(val, &provider, 1);
360 } else if (grp != NULL) {
362 * Handle attributes for group entries.
364 if (uu_strcaseeq(a, "cn")) {
365 const char *p = grp->gr_name;
366 de = str_list_dav(val, &p, 1);
367 } else if (uu_strcaseeq(a, "objectClass")) {
368 static const char *objectClasses[] = {
369 "top",
370 "posixGroup",
372 de = str_list_dav(val, objectClasses,
373 UU_NELEM(objectClasses));
374 } else if (uu_strcaseeq(a, "gidNumber")) {
375 de = uint_list_dav(val, &grp->gr_gid, 1);
376 } else if (uu_strcaseeq(a, "objectSid")) {
377 de = machine_sid_dav(val,
378 grp->gr_gid + LOCALRID_GID_MIN);
379 } else if (uu_strcaseeq(a, "displayName")) {
380 const char *p = grp->gr_name;
381 de = str_list_dav(val, &p, 1);
382 } else if (uu_strcaseeq(a, "distinguishedName")) {
383 char *dn;
384 RDLOCK_CONFIG();
385 (void) asprintf(&dn,
386 "cn=%s,ou=group,dc=%s",
387 grp->gr_name, _idmapdstate.hostname);
388 UNLOCK_CONFIG();
389 if (dn == NULL)
390 goto nomem;
391 const char *cdn = dn;
392 de = str_list_dav(val, &cdn, 1);
393 free(dn);
394 } else if (uu_strcaseeq(a, "memberUid")) {
396 * NEEDSWORK: There is probably a non-cast
397 * way to do this, but I don't immediately
398 * see it.
400 const char * const *members =
401 (const char * const *)grp->gr_mem;
402 de = str_list_dav(val, members, 0);
403 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
404 char *canon;
405 RDLOCK_CONFIG();
406 (void) asprintf(&canon, "%s@%s",
407 grp->gr_name, _idmapdstate.hostname);
408 UNLOCK_CONFIG();
409 if (canon == NULL)
410 goto nomem;
411 const char *ccanon = canon;
412 de = str_list_dav(val, &ccanon, 1);
413 free(canon);
414 } else if (uu_strcaseeq(a, "x-sun-provider")) {
415 const char *provider = "UNIX-group";
416 de = str_list_dav(val, &provider, 1);
420 if (de != NULL)
421 return (de);
424 return (NULL);
426 nomem:
427 return (directory_error("ENOMEM.users",
428 "No memory allocating return value for user lookup", NULL));
432 * Populate a directory attribute value with a SID based on our machine SID
433 * and the specified RID.
435 * It's a bit perverse that we must take a text-format SID and turn it into
436 * a binary-format SID, only to have the caller probably turn it back into
437 * text format, but SIDs are carried across LDAP in binary format.
439 static
440 directory_error_t
441 machine_sid_dav(directory_values_rpc *lvals, unsigned int rid)
443 sid_t *sid;
444 directory_error_t de;
446 RDLOCK_CONFIG();
447 int len = strlen(_idmapdstate.cfg->pgcfg.machine_sid);
448 char buf[len + 100]; /* 100 is enough space for any RID */
449 (void) snprintf(buf, sizeof (buf), "%s-%u",
450 _idmapdstate.cfg->pgcfg.machine_sid, rid);
451 UNLOCK_CONFIG();
453 sid = sid_fromstr(buf);
454 if (sid == NULL)
455 goto nomem;
457 sid_to_le(sid);
459 de = bin_list_dav(lvals, sid, 1, sid_len(sid));
460 sid_free(sid);
461 return (de);
463 nomem:
464 return (directory_error("ENOMEM.machine_sid_dav",
465 "Out of memory allocating return value for lookup", NULL));
468 struct directory_provider_static directory_provider_nsswitch = {
469 "files",
470 directory_provider_nsswitch_get,