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
48static directory_error_t copy_directory_attribute_value(
49    directory_attribute_value_t *dav,
50    directory_values_rpc *dav_rpc);
51static directory_error_t copy_directory_entry(directory_entry_t *ent,
52    directory_entry_rpc *ent_rpc);
53static void directory_results_free(directory_results_rpc *dr);
54static directory_datum_t directory_datum(void *data, size_t len);
55static void directory_datum_free(directory_datum_t d);
56
57/*
58 * This is the actual implementation of the opaque directory_t structure.
59 */
60struct directory {
61	CLIENT	*client;
62};
63
64/*
65 * Set up a directory search context.
66 */
67directory_error_t
68directory_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
92nomem:
93	de = directory_error("ENOMEM.directory_open",
94	    "Insufficient memory setting up directory access", NULL);
95err:
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 */
105void
106directory_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 */
121directory_error_t
122directory_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
190err:
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 */
199void
200directory_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 */
239static
240directory_datum_t
241directory_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 */
257size_t
258directory_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
267static
268void
269directory_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 */
277static
278directory_error_t
279copy_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 */
322static
323directory_error_t
324copy_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 */
361static
362void
363directory_results_free(directory_results_rpc *dr)
364{
365	xdr_free(xdr_directory_results_rpc, (char *)&dr);
366}
367