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