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 * Server-side support for directory information lookup functions.
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <malloc.h>
34#include <sys/types.h>
35#include <netdb.h>
36#include <pthread.h>
37#include <unistd.h>
38#include <string.h>
39#include <libuutil.h>
40#include <note.h>
41#include "idmapd.h"
42#include "directory.h"
43#include "directory_private.h"
44#include <rpcsvc/idmap_prot.h>
45#include "directory_library_impl.h"
46#include "directory_server_impl.h"
47#include "sized_array.h"
48
49/*
50 * Here's a list of all of the modules that provide directory
51 * information.  In the fullness of time this should probably be
52 * a plugin-able switch mechanism.
53 * Note that the list is in precedence order.
54 */
55extern struct directory_provider_static directory_provider_builtin;
56extern struct directory_provider_static directory_provider_nsswitch;
57extern struct directory_provider_static directory_provider_ad;
58struct directory_provider_static *providers[] = {
59	&directory_provider_builtin,
60	&directory_provider_nsswitch,
61	&directory_provider_ad,
62};
63
64/*
65 * This is the entry point for all directory lookup service requests.
66 */
67bool_t
68directory_get_common_1_svc(
69    idmap_utf8str_list ids,
70    idmap_utf8str types,
71    idmap_utf8str_list attrs,
72    directory_results_rpc *result,
73    struct svc_req *req)
74{
75	NOTE(ARGUNUSED(req))
76	int nids;
77	directory_entry_rpc *entries;
78	directory_error_t de;
79	int i;
80
81	nids = ids.idmap_utf8str_list_len;
82
83	entries = (directory_entry_rpc *)
84	    calloc(nids, sizeof (directory_entry_rpc));
85	if (entries == NULL)
86		goto nomem;
87	result->directory_results_rpc_u.entries.entries_val = entries;
88	result->directory_results_rpc_u.entries.entries_len = nids;
89	result->failed = FALSE;
90
91	for (i = 0; i < nids; i++) {
92		if (strlen(ids.idmap_utf8str_list_val[i]) >
93		    IDMAP_MAX_NAME_LEN) {
94			directory_entry_set_error(&entries[i],
95			    directory_error("invalid_arg.id.too_long",
96			    "Identifier too long", NULL));
97		}
98	}
99
100	for (i = 0; i < UU_NELEM(providers); i++) {
101		de = providers[i]->get(entries, &ids, types,
102		    &attrs);
103		if (de != NULL)
104			goto err;
105	}
106
107	return (TRUE);
108
109nomem:
110	de = directory_error("ENOMEM.get_common",
111	    "Insufficient memory retrieving directory data", NULL);
112
113err:
114	xdr_free(xdr_directory_results_rpc, (char *)result);
115	result->failed = TRUE;
116	return (
117	    directory_error_to_rpc(&result->directory_results_rpc_u.err, de));
118}
119
120/*
121 * Split name into {domain, name}.
122 * Suggest allocating name and domain on the stack, same size as id,
123 * using variable length arrays.
124 */
125void
126split_name(char *name, char *domain, char *id)
127{
128	char *p;
129
130	if ((p = strchr(id, '@')) != NULL) {
131		(void) strlcpy(name, id, p - id + 1);
132		(void) strcpy(domain, p + 1);
133	} else if ((p = strchr(id, '\\')) != NULL) {
134		(void) strcpy(name, p + 1);
135		(void) strlcpy(domain, id, p - id + 1);
136	} else {
137		(void) strcpy(name, id);
138		(void) strcpy(domain, "");
139	}
140}
141
142/*
143 * Given a list of strings, return a set of directory attribute values.
144 *
145 * Mark that the attribute was found.
146 *
147 * Note that the terminating \0 is *not* included in the result, because
148 * that's the way that strings come from LDAP.
149 * (Note also that the client side stuff adds in a terminating \0.)
150 *
151 * Note that on error the array may have been partially populated and will
152 * need to be cleaned up by the caller.  This is normally not a problem
153 * because the caller will need to clean up several such arrays.
154 */
155directory_error_t
156str_list_dav(directory_values_rpc *lvals, const char * const *str_list, int n)
157{
158	directory_value_rpc *dav;
159	int i;
160
161	if (n == 0) {
162		for (n = 0; str_list[n] != NULL; n++)
163			/* LOOP */;
164	}
165
166	dav = calloc(n, sizeof (directory_value_rpc));
167	if (dav == NULL)
168		goto nomem;
169
170	lvals->directory_values_rpc_u.values.values_val = dav;
171	lvals->directory_values_rpc_u.values.values_len = n;
172	lvals->found = TRUE;
173
174	for (i = 0; i < n; i++) {
175		int len;
176
177		len = strlen(str_list[i]);
178		dav[i].directory_value_rpc_val = uu_memdup(str_list[i], len);
179		if (dav[i].directory_value_rpc_val == NULL)
180			goto nomem;
181		dav[i].directory_value_rpc_len = len;
182	}
183
184	return (NULL);
185
186nomem:
187	return (directory_error("ENOMEM.str_list_dav",
188	    "Insufficient memory copying values"));
189}
190
191/*
192 * Given a list of unsigned integers, return a set of string directory
193 * attribute values.
194 *
195 * Mark that the attribute was found.
196 *
197 * Note that the terminating \0 is *not* included in the result, because
198 * that's the way that strings come from LDAP.
199 * (Note also that the client side stuff adds in a terminating \0.)
200 *
201 * Note that on error the array may have been partially populated and will
202 * need to be cleaned up by the caller.  This is normally not a problem
203 * because the caller will need to clean up several such arrays.
204 */
205directory_error_t
206uint_list_dav(directory_values_rpc *lvals, const unsigned int *array, int n)
207{
208	directory_value_rpc *dav;
209	int i;
210
211	dav = calloc(n, sizeof (directory_value_rpc));
212	if (dav == NULL)
213		goto nomem;
214
215	lvals->directory_values_rpc_u.values.values_val = dav;
216	lvals->directory_values_rpc_u.values.values_len = n;
217	lvals->found = TRUE;
218
219	for (i = 0; i < n; i++) {
220		char buf[100];	/* larger than any integer */
221		int len;
222
223		(void) snprintf(buf, sizeof (buf), "%u", array[i]);
224
225		len = strlen(buf);
226		dav[i].directory_value_rpc_val = uu_memdup(buf, len);
227		if (dav[i].directory_value_rpc_val == NULL)
228			goto nomem;
229		dav[i].directory_value_rpc_len = len;
230	}
231
232	return (NULL);
233
234nomem:
235	return (directory_error("ENOMEM.uint_list_dav",
236	    "Insufficient memory copying values"));
237}
238
239/*
240 * Given a list of fixed-length binary chunks, return a set of binary
241 * directory attribute values.
242 *
243 * Mark that the attribute was found.
244 *
245 * Note that on error the array may have been partially populated and will
246 * need to be cleaned up by the caller.  This is normally not a problem
247 * because the caller will need to clean up several such arrays.
248 */
249directory_error_t
250bin_list_dav(directory_values_rpc *lvals, const void *array, int n, size_t sz)
251{
252	directory_value_rpc *dav;
253	char *inbuf = (char *)array;
254	int i;
255
256	dav = calloc(n, sizeof (directory_value_rpc));
257	if (dav == NULL)
258		goto nomem;
259
260	lvals->directory_values_rpc_u.values.values_val = dav;
261	lvals->directory_values_rpc_u.values.values_len = n;
262	lvals->found = TRUE;
263
264	for (i = 0; i < n; i++) {
265		dav[i].directory_value_rpc_val = uu_memdup(inbuf, sz);
266		if (dav[i].directory_value_rpc_val == NULL)
267			goto nomem;
268		dav[i].directory_value_rpc_len = sz;
269		inbuf += sz;
270	}
271
272	return (NULL);
273
274nomem:
275	return (directory_error("ENOMEM.bin_list_dav",
276	    "Insufficient memory copying values"));
277}
278
279/*
280 * Set up to return an error on a particular directory entry.
281 * Note that the caller need not (and in fact must not) free
282 * the directory_error_t; it will be freed when the directory entry
283 * list is freed.
284 */
285void
286directory_entry_set_error(directory_entry_rpc *ent, directory_error_t de)
287{
288	xdr_free(xdr_directory_entry_rpc, (char *)&ent);
289	ent->status = DIRECTORY_ERROR;
290	(void) directory_error_to_rpc(&ent->directory_entry_rpc_u.err, de);
291}
292