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]
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.)
41 #include "directory.h"
42 #include "directory_private.h"
43 #include <rpcsvc/idmap_prot.h>
44 #include "directory_server_impl.h"
47 static directory_error_t
machine_sid_dav(directory_values_rpc
*lvals
,
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.
59 directory_provider_nsswitch_get(
60 directory_entry_rpc
*del
,
61 idmap_utf8str_list
*ids
,
63 idmap_utf8str_list
*attrs
)
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
);
81 for (i
= 0; i
< ids
->idmap_utf8str_list_len
; i
++) {
82 struct passwd
*pwd
= NULL
;
83 struct group
*grp
= NULL
;
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.
93 if (*(types
+1) != '\0')
97 * If this entry has already been handled, one way or another,
100 if (del
[i
].status
!= DIRECTORY_NOT_FOUND
)
103 char *id
= ids
->idmap_utf8str_list_val
[i
];
105 if (type
== DIRECTORY_ID_SID
[0]) {
108 * Check whether the first part matches, then a "-",
111 if (strncasecmp(id
, my_machine_sid
, machine_sid_len
) !=
114 if (id
[machine_sid_len
] != '-')
118 strtoul(id
+ machine_sid_len
+ 1, &p
, 10);
122 if (rid
< LOCALRID_UID_MIN
) {
123 /* Builtin, not handled here */
127 if (rid
<= LOCALRID_UID_MAX
) {
130 pwd
= getpwuid(rid
- LOCALRID_UID_MIN
);
132 if (errno
== 0) /* Not found */
136 (void) snprintf(buf
, sizeof (buf
),
138 directory_entry_set_error(&del
[i
],
139 directory_error("errno.getpwuid",
141 buf
, strerror(err
), NULL
));
144 } else if (rid
>= LOCALRID_GID_MIN
&&
145 rid
<= LOCALRID_GID_MAX
) {
148 grp
= getgrgid(rid
- LOCALRID_GID_MIN
);
150 if (errno
== 0) /* Not found */
154 (void) snprintf(buf
, sizeof (buf
),
156 directory_entry_set_error(&del
[i
],
157 directory_error("errno.getgrgid",
159 buf
, strerror(err
), NULL
));
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
))
178 * If the caller has requested user or group
179 * information specifically, we only set one of
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 */
190 pwd
= getpwnam(name
);
191 if (pwd
== NULL
&& errno
!= 0) {
194 (void) snprintf(buf
, sizeof (buf
),
196 directory_entry_set_error(&del
[i
],
197 directory_error("errno.getpwnam",
199 buf
, strerror(err
), NULL
));
204 if (type
!= DIRECTORY_ID_USER
[0]) {
205 /* prep for not found / error case */
208 grp
= getgrnam(name
);
209 if (grp
== NULL
&& errno
!= 0) {
212 (void) snprintf(buf
, sizeof (buf
),
214 directory_entry_set_error(&del
[i
],
215 directory_error("errno.getgrnam",
217 buf
, strerror(err
), NULL
));
224 * Didn't find it, don't populate the structure.
225 * Another provider might populate it.
227 if (pwd
== NULL
&& grp
== NULL
)
230 de
= directory_provider_nsswitch_populate(&del
[i
], pwd
, grp
,
233 directory_entry_set_error(&del
[i
], de
);
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.
248 directory_provider_nsswitch_populate(
249 directory_entry_rpc
*pent
,
252 idmap_utf8str_list
*attrs
)
255 directory_values_rpc
*llvals
;
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",
268 nattrs
= attrs
->idmap_utf8str_list_len
;
270 llvals
= calloc(nattrs
, sizeof (directory_values_rpc
));
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
;
281 directory_error_t de
;
284 * We're going to refer to these a lot, so make a shorthand
287 a
= attrs
->idmap_utf8str_list_val
[j
];
291 * Start by assuming no errors and that we don't have
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
[] = {
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")) {
323 "uid=%s,ou=people,dc=%s",
324 pwd
->pw_name
, _idmapdstate
.hostname
);
328 const char *cdn
= dn
;
329 de
= str_list_dav(val
, &cdn
, 1);
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")) {
348 (void) asprintf(&canon
, "%s@%s",
349 pwd
->pw_name
, _idmapdstate
.hostname
);
353 const char *ccanon
= canon
;
354 de
= str_list_dav(val
, &ccanon
, 1);
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
[] = {
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")) {
386 "cn=%s,ou=group,dc=%s",
387 grp
->gr_name
, _idmapdstate
.hostname
);
391 const char *cdn
= dn
;
392 de
= str_list_dav(val
, &cdn
, 1);
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
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")) {
406 (void) asprintf(&canon
, "%s@%s",
407 grp
->gr_name
, _idmapdstate
.hostname
);
411 const char *ccanon
= canon
;
412 de
= str_list_dav(val
, &ccanon
, 1);
414 } else if (uu_strcaseeq(a
, "x-sun-provider")) {
415 const char *provider
= "UNIX-group";
416 de
= str_list_dav(val
, &provider
, 1);
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.
441 machine_sid_dav(directory_values_rpc
*lvals
, unsigned int rid
)
444 directory_error_t de
;
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
);
453 sid
= sid_fromstr(buf
);
459 de
= bin_list_dav(lvals
, sid
, 1, sid_len(sid
));
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
= {
470 directory_provider_nsswitch_get
,