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