1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Retrieve directory information for built-in users and groups
28 */
29
30 #include <stdio.h>
31 #include <limits.h>
32 #include <sys/idmap.h>
33 #include <sys/param.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <netdb.h>
37 #include <libuutil.h>
38 #include <note.h>
39 #include "idmapd.h"
40 #include "directory.h"
41 #include "directory_private.h"
42 #include <rpcsvc/idmap_prot.h>
43 #include "directory_server_impl.h"
44 #include "sidutil.h"
45
46 static directory_error_t sid_dav(directory_values_rpc *lvals,
47 const wksids_table_t *wksid);
48 static directory_error_t directory_provider_builtin_populate(
49 directory_entry_rpc *pent, const wksids_table_t *wksid,
50 idmap_utf8str_list *attrs);
51
52 /*
53 * Retrieve information by name.
54 * Called indirectly through the directory_provider_static structure.
55 */
56 static
57 directory_error_t
directory_provider_builtin_get(directory_entry_rpc * del,idmap_utf8str_list * ids,idmap_utf8str types,idmap_utf8str_list * attrs)58 directory_provider_builtin_get(
59 directory_entry_rpc *del,
60 idmap_utf8str_list *ids,
61 idmap_utf8str types,
62 idmap_utf8str_list *attrs)
63 {
64 int i;
65
66 for (i = 0; i < ids->idmap_utf8str_list_len; i++) {
67 const wksids_table_t *wksid;
68 directory_error_t de;
69 int type;
70
71 /*
72 * Extract the type for this particular ID.
73 * Advance to the next type, if it's there, else keep
74 * using this type until we run out of IDs.
75 */
76 type = *types;
77 if (*(types+1) != '\0')
78 types++;
79
80 /*
81 * If this entry has already been handled, one way or another,
82 * skip it.
83 */
84 if (del[i].status != DIRECTORY_NOT_FOUND)
85 continue;
86
87 char *id = ids->idmap_utf8str_list_val[i];
88
89 /*
90 * End-to-end error injection point.
91 * NEEDSWORK: should probably eliminate this for production
92 */
93 if (uu_streq(id, " DEBUG BUILTIN ERROR ")) {
94 directory_entry_set_error(&del[i],
95 directory_error("Directory_provider_builtin.debug",
96 "Directory_provider_builtin: artificial error",
97 NULL));
98 continue;
99 }
100
101 if (type == DIRECTORY_ID_SID[0])
102 wksid = find_wk_by_sid(id);
103 else {
104 int idmap_id_type;
105 if (type == DIRECTORY_ID_NAME[0])
106 idmap_id_type = IDMAP_POSIXID;
107 else if (type == DIRECTORY_ID_USER[0])
108 idmap_id_type = IDMAP_UID;
109 else if (type == DIRECTORY_ID_GROUP[0])
110 idmap_id_type = IDMAP_GID;
111 else {
112 directory_entry_set_error(&del[i],
113 directory_error("invalid_arg.id_type",
114 "Invalid ID type \"%1\"",
115 types, NULL));
116 continue;
117 }
118
119 int id_len = strlen(id);
120 char name[id_len + 1];
121 char domain[id_len + 1];
122
123 split_name(name, domain, id);
124
125 wksid = find_wksid_by_name(name, domain, idmap_id_type);
126 }
127
128 if (wksid == NULL)
129 continue;
130
131 de = directory_provider_builtin_populate(&del[i], wksid, attrs);
132 if (de != NULL) {
133 directory_entry_set_error(&del[i], de);
134 de = NULL;
135 }
136 }
137
138 return (NULL);
139 }
140
141 /*
142 * Given a well-known name entry and a list of attributes that were
143 * requested, populate the structure to return to the caller.
144 */
145 static
146 directory_error_t
directory_provider_builtin_populate(directory_entry_rpc * pent,const wksids_table_t * wksid,idmap_utf8str_list * attrs)147 directory_provider_builtin_populate(
148 directory_entry_rpc *pent,
149 const wksids_table_t *wksid,
150 idmap_utf8str_list *attrs)
151 {
152 int j;
153 directory_values_rpc *llvals;
154 int nattrs;
155
156 nattrs = attrs->idmap_utf8str_list_len;
157
158 llvals = calloc(nattrs, sizeof (directory_values_rpc));
159 if (llvals == NULL)
160 goto nomem;
161
162 pent->status = DIRECTORY_FOUND;
163 pent->directory_entry_rpc_u.attrs.attrs_val = llvals;
164 pent->directory_entry_rpc_u.attrs.attrs_len = nattrs;
165
166 for (j = 0; j < nattrs; j++) {
167 directory_values_rpc *val;
168 char *a;
169 directory_error_t de;
170
171 /*
172 * We're going to refer to these a lot, so make a shorthand
173 * copy.
174 */
175 a = attrs->idmap_utf8str_list_val[j];
176 val = &llvals[j];
177
178 /*
179 * Start by assuming no errors and that we don't have
180 * the information.
181 */
182 val->found = FALSE;
183 de = NULL;
184
185 if (uu_strcaseeq(a, "uid")) {
186 de = str_list_dav(val, &wksid->winname, 1);
187 } else if (uu_strcaseeq(a, "uidNumber")) {
188 if (wksid->pid != IDMAP_SENTINEL_PID &&
189 wksid->is_user) {
190 de = uint_list_dav(val, &wksid->pid, 1);
191 }
192 } else if (uu_strcaseeq(a, "gidNumber")) {
193 if (wksid->pid != IDMAP_SENTINEL_PID &&
194 !wksid->is_user) {
195 de = uint_list_dav(val, &wksid->pid, 1);
196 }
197 } else if (uu_strcaseeq(a, "displayName") ||
198 uu_strcaseeq(a, "cn")) {
199 de = str_list_dav(val, &wksid->winname, 1);
200 } else if (uu_strcaseeq(a, "distinguishedName")) {
201 char *container;
202 if (wksid->domain == NULL) {
203 container = "Users";
204 } else {
205 container = "Builtin";
206 }
207 RDLOCK_CONFIG();
208 char *dn;
209 (void) asprintf(&dn,
210 "CN=%s,CN=%s,DC=%s",
211 wksid->winname, container, _idmapdstate.hostname);
212 UNLOCK_CONFIG();
213 const char *cdn = dn;
214 de = str_list_dav(val, &cdn, 1);
215 free(dn);
216 } else if (uu_strcaseeq(a, "objectClass")) {
217 if (wksid->is_wuser) {
218 static const char *objectClasses[] = {
219 "top",
220 "person",
221 "organizationalPerson",
222 "user",
223 };
224 de = str_list_dav(val, objectClasses,
225 UU_NELEM(objectClasses));
226 } else {
227 static const char *objectClasses[] = {
228 "top",
229 "group",
230 };
231 de = str_list_dav(val, objectClasses,
232 UU_NELEM(objectClasses));
233 }
234 } else if (uu_strcaseeq(a, "objectSid")) {
235 de = sid_dav(val, wksid);
236 } else if (uu_strcaseeq(a, "x-sun-canonicalName")) {
237 char *canon;
238
239 if (wksid->domain == NULL) {
240 RDLOCK_CONFIG();
241 (void) asprintf(&canon, "%s@%s",
242 wksid->winname, _idmapdstate.hostname);
243 UNLOCK_CONFIG();
244 } else if (uu_streq(wksid->domain, "")) {
245 canon = strdup(wksid->winname);
246 } else {
247 (void) asprintf(&canon, "%s@%s",
248 wksid->winname, wksid->domain);
249 }
250
251 if (canon == NULL)
252 goto nomem;
253 const char *ccanon = canon;
254 de = str_list_dav(val, &ccanon, 1);
255 free(canon);
256 } else if (uu_strcaseeq(a, "x-sun-provider")) {
257 const char *provider = "Builtin";
258 de = str_list_dav(val, &provider, 1);
259 }
260 if (de != NULL)
261 return (de);
262 }
263
264 return (NULL);
265
266 nomem:
267 return (directory_error("ENOMEM.users",
268 "No memory allocating return value for user lookup", NULL));
269 }
270
271 /*
272 * Given a well-known name structure, generate a binary-format SID.
273 * It's a bit perverse that we must take a text-format SID and turn it into
274 * a binary-format SID, only to have the caller probably turn it back into
275 * text format, but SIDs are carried across LDAP in binary format.
276 */
277 static
278 directory_error_t
sid_dav(directory_values_rpc * lvals,const wksids_table_t * wksid)279 sid_dav(directory_values_rpc *lvals, const wksids_table_t *wksid)
280 {
281 char *text_sid;
282 sid_t *sid;
283 directory_error_t de;
284
285 if (wksid->sidprefix == NULL) {
286 RDLOCK_CONFIG();
287 (void) asprintf(&text_sid, "%s-%d",
288 _idmapdstate.cfg->pgcfg.machine_sid,
289 wksid->rid);
290 UNLOCK_CONFIG();
291 } else {
292 (void) asprintf(&text_sid, "%s-%d",
293 wksid->sidprefix, wksid->rid);
294 }
295
296 if (text_sid == NULL)
297 goto nomem;
298
299 sid = sid_fromstr(text_sid);
300 free(text_sid);
301
302 if (sid == NULL)
303 goto nomem;
304
305 sid_to_le(sid);
306
307 de = bin_list_dav(lvals, sid, 1, sid_len(sid));
308
309 sid_free(sid);
310
311 return (de);
312
313 nomem:
314 return (directory_error("ENOMEM.sid_dav",
315 "No memory allocating SID for user lookup", NULL));
316 }
317
318 struct directory_provider_static directory_provider_builtin = {
319 "builtin",
320 directory_provider_builtin_get,
321 };
322