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 2010 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 != IDMAP_SENTINEL_PID &&
190 			    wksid->is_user) {
191 				de = uint_list_dav(val, &wksid->pid, 1);
192 			}
193 		} else if (strcaseeq(a, "gidNumber")) {
194 			if (wksid->pid != IDMAP_SENTINEL_PID &&
195 			    !wksid->is_user) {
196 				de = uint_list_dav(val, &wksid->pid, 1);
197 			}
198 		} else if (strcaseeq(a, "displayName") || strcaseeq(a, "cn")) {
199 			de = str_list_dav(val, &wksid->winname, 1);
200 		} else if (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 (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 				    NELEM(objectClasses));
226 			} else {
227 				static const char *objectClasses[] = {
228 					"top",
229 					"group",
230 				};
231 				de = str_list_dav(val, objectClasses,
232 				    NELEM(objectClasses));
233 			}
234 		} else if (strcaseeq(a, "objectSid")) {
235 			de = sid_dav(val, wksid);
236 		} else if (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 (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 (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
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