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  * Directory lookup functions.  These are shims that translate from the API
29  * into the RPC protocol.
30  */
31 
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <malloc.h>
37 #include <sys/types.h>
38 #include <netdb.h>
39 #include <pthread.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include "directory.h"
43 #include "directory_private.h"
44 #include <rpcsvc/idmap_prot.h>
45 #include "directory_library_impl.h"
46 #include "sized_array.h"
47 
48 static directory_error_t copy_directory_attribute_value(
49     directory_attribute_value_t *dav,
50     directory_values_rpc *dav_rpc);
51 static directory_error_t copy_directory_entry(directory_entry_t *ent,
52     directory_entry_rpc *ent_rpc);
53 static void directory_results_free(directory_results_rpc *dr);
54 static directory_datum_t directory_datum(void *data, size_t len);
55 static void directory_datum_free(directory_datum_t d);
56 
57 /*
58  * This is the actual implementation of the opaque directory_t structure.
59  */
60 struct directory {
61 	CLIENT	*client;
62 };
63 
64 /*
65  * Set up a directory search context.
66  */
67 directory_error_t
directory_open(directory_t * ret)68 directory_open(directory_t *ret)
69 {
70 	directory_t d;
71 	directory_error_t de;
72 	char host[] = "localhost";
73 
74 	*ret = NULL;
75 
76 	d = calloc(1, sizeof (*d));
77 	if (d == NULL)
78 		goto nomem;
79 
80 	d->client = clnt_door_create(IDMAP_PROG, IDMAP_V1, 0);
81 	if (d->client == NULL) {
82 		de = directory_error("clnt_create.directory_open",
83 		    "Error: %1",
84 		    clnt_spcreateerror(host),
85 		    NULL);
86 		goto err;
87 	}
88 
89 	*ret = d;
90 	return (NULL);
91 
92 nomem:
93 	de = directory_error("ENOMEM.directory_open",
94 	    "Insufficient memory setting up directory access", NULL);
95 err:
96 	directory_close(d);
97 	return (de);
98 }
99 
100 /*
101  * Tear down a directory search context.
102  *
103  * Does nothing if d==NULL.
104  */
105 void
directory_close(directory_t d)106 directory_close(directory_t d)
107 {
108 	if (d == NULL)
109 		return;
110 
111 	if (d->client != NULL)
112 		clnt_destroy(d->client);
113 
114 	free(d);
115 }
116 
117 /*
118  * Given a list of identifiers, a list of their types, and a list of attributes,
119  * return the information.
120  */
121 directory_error_t
directory_get_v(directory_t d,directory_entry_list_t * ret,char ** ids,int nids,char * types,char ** attr_list)122 directory_get_v(
123     directory_t d,
124     directory_entry_list_t *ret,
125     char **ids,
126     int nids,
127     char *types,
128     char **attr_list)
129 {
130 	int nattrs;
131 	directory_entry_list_t del;
132 	directory_error_t de;
133 	directory_results_rpc dr;
134 	idmap_utf8str_list sl_ids;
135 	idmap_utf8str_list sl_attrs;
136 	directory_entry_rpc *users;
137 	int i;
138 	enum clnt_stat cs;
139 
140 	*ret = NULL;
141 	del = NULL;
142 
143 	if (nids == 0) {
144 		for (nids = 0; ids[nids] != NULL; nids++)
145 			/* LOOP */;
146 	}
147 
148 	for (nattrs = 0; attr_list[nattrs] != NULL; nattrs++)
149 		/* LOOP */;
150 
151 	sl_ids.idmap_utf8str_list_len = nids;
152 	sl_ids.idmap_utf8str_list_val = ids;
153 	sl_attrs.idmap_utf8str_list_len = nattrs;
154 	sl_attrs.idmap_utf8str_list_val = attr_list;
155 
156 	(void) memset(&dr, 0, sizeof (dr));
157 	cs = directory_get_common_1(sl_ids, types, sl_attrs, &dr, d->client);
158 	if (cs != RPC_SUCCESS) {
159 		char errbuf[100];	/* well long enough for any integer */
160 		(void) sprintf(errbuf, "%d", cs);
161 		de = directory_error("RPC.Get_common",
162 		    "Get_common RPC (%1)%2", errbuf,
163 		    clnt_sperror(d->client, ""), NULL);
164 		goto err;
165 	}
166 
167 	if (dr.failed) {
168 		de = directory_error_from_rpc(
169 		    &dr.directory_results_rpc_u.err);
170 		goto err;
171 	}
172 
173 	assert(dr.directory_results_rpc_u.entries.entries_len == nids);
174 
175 	users = dr.directory_results_rpc_u.entries.entries_val;
176 
177 	del = sized_array(nids, sizeof (directory_entry_t));
178 
179 	for (i = 0; i < nids; i++) {
180 		de = copy_directory_entry(&del[i], &users[i]);
181 		if (de != NULL)
182 			goto err;
183 	}
184 
185 	directory_results_free(&dr);
186 
187 	*ret = del;
188 	return (NULL);
189 
190 err:
191 	directory_results_free(&dr);
192 	directory_free(del);
193 	return (de);
194 }
195 
196 /*
197  * Free the results from a directory_get_*() request.
198  */
199 void
directory_free(directory_entry_list_t del)200 directory_free(directory_entry_list_t del)
201 {
202 	directory_entry_t *ent;
203 	directory_attribute_value_t dav;
204 	int i;
205 	int j;
206 	int k;
207 
208 	if (del == NULL)
209 		return;
210 
211 	/* For each directory entry returned */
212 	for (i = 0; i < sized_array_n(del); i++) {
213 		ent = &del[i];
214 
215 		if (ent->attrs != NULL) {
216 			/* For each attribute */
217 			for (j = 0; j < sized_array_n(ent->attrs); j++) {
218 				dav = ent->attrs[j];
219 				if (dav != NULL) {
220 					for (k = 0; k < sized_array_n(dav); k++)
221 						directory_datum_free(dav[k]);
222 
223 					sized_array_free(dav);
224 				}
225 			}
226 			sized_array_free(ent->attrs);
227 		}
228 
229 		directory_error_free(ent->err);
230 	}
231 
232 	sized_array_free(del);
233 }
234 
235 /*
236  * Create a directory datum.  Note that we allocate an extra byte and
237  * zero it, so that strings get null-terminated.  Return NULL on error.
238  */
239 static
240 directory_datum_t
directory_datum(void * data,size_t len)241 directory_datum(void *data, size_t len)
242 {
243 	void *p;
244 
245 	p = sized_array(len + 1, 1);
246 	if (p == NULL)
247 		return (NULL);
248 	(void) memcpy(p, data, len);
249 	*((char *)p + len) = '\0';
250 	return (p);
251 }
252 
253 /*
254  * Return the size of a directory_datum_t.  Note that this does not include
255  * the terminating \0, so it represents the value as returned by LDAP.
256  */
257 size_t
directory_datum_len(directory_datum_t d)258 directory_datum_len(directory_datum_t d)
259 {
260 	/*
261 	 * Deduct the terminal \0, so that binary data gets the
262 	 * expected length.
263 	 */
264 	return (sized_array_n(d) - 1);
265 }
266 
267 static
268 void
directory_datum_free(directory_datum_t d)269 directory_datum_free(directory_datum_t d)
270 {
271 	sized_array_free(d);
272 }
273 
274 /*
275  * Unmarshall an RPC directory entry into an API directory entry.
276  */
277 static
278 directory_error_t
copy_directory_entry(directory_entry_t * ent,directory_entry_rpc * ent_rpc)279 copy_directory_entry(
280     directory_entry_t *ent,
281     directory_entry_rpc *ent_rpc)
282 {
283 	int nattrs;
284 	int i;
285 	directory_error_t de;
286 
287 	/* If the entry wasn't found, leave the entry attrs and err NULL. */
288 	if (ent_rpc->status == DIRECTORY_NOT_FOUND)
289 		return (NULL);
290 
291 	if (ent_rpc->status == DIRECTORY_ERROR) {
292 		ent->err = directory_error_from_rpc(
293 		    &ent_rpc->directory_entry_rpc_u.err);
294 		return (NULL);
295 	}
296 
297 	nattrs = ent_rpc->directory_entry_rpc_u.attrs.attrs_len;
298 
299 	ent->attrs = sized_array(nattrs, sizeof (directory_attribute_value_t));
300 	if (ent->attrs == NULL) {
301 		return (directory_error("ENOMEM.copy_directory_entry",
302 		    "Insufficient memory copying directory entry", NULL));
303 	}
304 	for (i = 0; i < nattrs; i++) {
305 		de = copy_directory_attribute_value(&ent->attrs[i],
306 		    &ent_rpc->directory_entry_rpc_u.attrs.attrs_val[i]);
307 		if (de != NULL)
308 			return (de);
309 	}
310 
311 	return (NULL);
312 }
313 
314 /*
315  * Unmarshall an RPC directory attribute value into the API equivalent.
316  *
317  * Note that on error some entries may have been copied, and so
318  * the caller needs to clean up dav.  This is normally not a problem
319  * since the caller will have called this function several times and
320  * will need to clean up the results from the other calls too.
321  */
322 static
323 directory_error_t
copy_directory_attribute_value(directory_attribute_value_t * dav,directory_values_rpc * dav_rpc)324 copy_directory_attribute_value(
325     directory_attribute_value_t *dav,
326     directory_values_rpc *dav_rpc)
327 {
328 	int i;
329 	int nvals;
330 	directory_value_rpc *vals;
331 
332 	/* If it wasn't found, leave the corresponding entry NULL */
333 	if (!dav_rpc->found)
334 		return (NULL);
335 
336 	nvals = dav_rpc->directory_values_rpc_u.values.values_len;
337 	*dav = sized_array(nvals + 1, sizeof (directory_datum_t));
338 	if (*dav == NULL) {
339 		return (directory_error("ENOMEM.copy_directory_attribute_value",
340 		    "Insufficient memory copying directory entry", NULL));
341 	}
342 
343 	vals = dav_rpc->directory_values_rpc_u.values.values_val;
344 	for (i = 0; i < nvals; i++) {
345 		(*dav)[i] = directory_datum(vals[i].directory_value_rpc_val,
346 		    vals[i].directory_value_rpc_len);
347 		if ((*dav)[i] == NULL) {
348 			return (directory_error(
349 			    "ENOMEM.copy_directory_attribute_value",
350 			    "Insufficient memory copying directory entry",
351 			    NULL));
352 		}
353 	}
354 
355 	return (NULL);
356 }
357 
358 /*
359  * Free the results of a directory RPC request.
360  */
361 static
362 void
directory_results_free(directory_results_rpc * dr)363 directory_results_free(directory_results_rpc *dr)
364 {
365 	xdr_free(xdr_directory_results_rpc, (char *)&dr);
366 }
367