1/*-
2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include "namespace.h"
32#define _NS_PRIVATE
33#include <nsswitch.h>
34#include <stdlib.h>
35#include <string.h>
36#include "un-namespace.h"
37#include "nscachedcli.h"
38#include "nscache.h"
39
40#define NSS_CACHE_KEY_INITIAL_SIZE	(256)
41#define NSS_CACHE_KEY_SIZE_LIMIT	(NSS_CACHE_KEY_INITIAL_SIZE << 4)
42
43#define NSS_CACHE_BUFFER_INITIAL_SIZE	(1024)
44#define NSS_CACHE_BUFFER_SIZE_LIMIT	(NSS_CACHE_BUFFER_INITIAL_SIZE << 8)
45
46#define CACHED_SOCKET_PATH 		"/var/run/nscd"
47
48int
49__nss_cache_handler(void *retval, void *mdata, va_list ap)
50{
51	return (NS_UNAVAIL);
52}
53
54int
55__nss_common_cache_read(void *retval, void *mdata, va_list ap)
56{
57	struct cached_connection_params params;
58	cached_connection connection;
59
60	char *buffer;
61	size_t buffer_size, size;
62
63	nss_cache_info const *cache_info;
64	nss_cache_data *cache_data;
65	va_list ap_new;
66	int res;
67
68	cache_data = (nss_cache_data *)mdata;
69	cache_info = cache_data->info;
70
71	memset(&params, 0, sizeof(struct cached_connection_params));
72	params.socket_path = CACHED_SOCKET_PATH;
73
74	cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE);
75	memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE);
76	cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE;
77	va_copy(ap_new, ap);
78
79	do {
80		size = cache_data->key_size;
81		res = cache_info->id_func(cache_data->key, &size, ap_new,
82		    cache_info->mdata);
83		va_end(ap_new);
84		if (res == NS_RETURN) {
85			if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT)
86				break;
87
88			cache_data->key_size <<= 1;
89			cache_data->key = realloc(cache_data->key,
90			    cache_data->key_size);
91			memset(cache_data->key, 0, cache_data->key_size);
92			va_copy(ap_new, ap);
93		}
94	} while (res == NS_RETURN);
95
96	if (res != NS_SUCCESS) {
97		free(cache_data->key);
98		cache_data->key = NULL;
99		cache_data->key_size = 0;
100		return (res);
101	} else
102		cache_data->key_size = size;
103
104	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
105	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
106	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
107
108	do {
109		connection = __open_cached_connection(&params);
110		if (connection == NULL) {
111			res = -1;
112			break;
113		}
114		res = __cached_read(connection, cache_info->entry_name,
115		    cache_data->key, cache_data->key_size, buffer,
116		    &buffer_size);
117		__close_cached_connection(connection);
118		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
119			buffer = (char *)realloc(buffer, buffer_size);
120			memset(buffer, 0, buffer_size);
121		}
122	} while (res == -2);
123
124	if (res == 0) {
125		if (buffer_size == 0) {
126			free(buffer);
127			free(cache_data->key);
128			cache_data->key = NULL;
129			cache_data->key_size = 0;
130			return (NS_RETURN);
131		}
132
133		va_copy(ap_new, ap);
134		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
135		    ap_new, cache_info->mdata);
136		va_end(ap_new);
137
138		if (res != NS_SUCCESS) {
139			free(buffer);
140			free(cache_data->key);
141			cache_data->key = NULL;
142			cache_data->key_size = 0;
143			return (res);
144		} else
145			res = 0;
146	}
147
148	if (res == 0) {
149		free(cache_data->key);
150		cache_data->key = NULL;
151		cache_data->key_size = 0;
152	}
153
154	free(buffer);
155	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
156}
157
158int
159__nss_common_cache_write(void *retval, void *mdata, va_list ap)
160{
161	struct cached_connection_params params;
162	cached_connection connection;
163
164	char *buffer;
165	size_t buffer_size;
166
167	nss_cache_info const *cache_info;
168	nss_cache_data *cache_data;
169	va_list ap_new;
170	int res;
171
172	cache_data = (nss_cache_data *)mdata;
173	cache_info = cache_data->info;
174
175	if (cache_data->key == NULL)
176		return (NS_UNAVAIL);
177
178	memset(&params, 0, sizeof(struct cached_connection_params));
179	params.socket_path = CACHED_SOCKET_PATH;
180
181	connection = __open_cached_connection(&params);
182	if (connection == NULL) {
183		free(cache_data->key);
184		return (NS_UNAVAIL);
185	}
186
187	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
188	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
189	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
190
191	do {
192		size_t size;
193
194		size = buffer_size;
195		va_copy(ap_new, ap);
196		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
197		    cache_info->mdata);
198		va_end(ap_new);
199
200		if (res == NS_RETURN) {
201			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
202				break;
203
204			buffer_size <<= 1;
205			buffer = (char *)realloc(buffer, buffer_size);
206			memset(buffer, 0, buffer_size);
207		}
208	} while (res == NS_RETURN);
209
210	if (res != NS_SUCCESS) {
211		__close_cached_connection(connection);
212		free(cache_data->key);
213		free(buffer);
214		return (res);
215	}
216
217	res = __cached_write(connection, cache_info->entry_name,
218	    cache_data->key, cache_data->key_size, buffer, buffer_size);
219	__close_cached_connection(connection);
220
221	free(cache_data->key);
222	free(buffer);
223
224	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
225}
226
227int
228__nss_common_cache_write_negative(void *mdata)
229{
230	struct cached_connection_params params;
231	cached_connection connection;
232	int res;
233
234	nss_cache_info const *cache_info;
235	nss_cache_data *cache_data;
236
237	cache_data = (nss_cache_data *)mdata;
238	cache_info = cache_data->info;
239
240	if (cache_data->key == NULL)
241		return (NS_UNAVAIL);
242
243	memset(&params, 0, sizeof(struct cached_connection_params));
244	params.socket_path = CACHED_SOCKET_PATH;
245
246	connection = __open_cached_connection(&params);
247	if (connection == NULL) {
248		free(cache_data->key);
249		return (NS_UNAVAIL);
250	}
251
252	res = __cached_write(connection, cache_info->entry_name,
253	    cache_data->key, cache_data->key_size, NULL, 0);
254	__close_cached_connection(connection);
255
256	free(cache_data->key);
257	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
258}
259
260int
261__nss_mp_cache_read(void *retval, void *mdata, va_list ap)
262{
263	struct cached_connection_params params;
264	cached_mp_read_session rs;
265
266	char *buffer;
267	size_t buffer_size;
268
269	nss_cache_info const *cache_info;
270	nss_cache_data *cache_data;
271	va_list ap_new;
272	int res;
273
274	cache_data = (nss_cache_data *)mdata;
275	cache_info = cache_data->info;
276
277	if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION)
278		return (NS_UNAVAIL);
279
280	rs = cache_info->get_mp_rs_func();
281	if (rs == INVALID_CACHED_MP_READ_SESSION) {
282		memset(&params, 0, sizeof(struct cached_connection_params));
283		params.socket_path = CACHED_SOCKET_PATH;
284
285		rs = __open_cached_mp_read_session(&params,
286		    cache_info->entry_name);
287		if (rs == INVALID_CACHED_MP_READ_SESSION)
288			return (NS_UNAVAIL);
289
290		cache_info->set_mp_rs_func(rs);
291	}
292
293	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
294	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
295	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
296
297	do {
298		res = __cached_mp_read(rs, buffer, &buffer_size);
299		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
300			buffer = (char *)realloc(buffer, buffer_size);
301			memset(buffer, 0, buffer_size);
302		}
303	} while (res == -2);
304
305	if (res == 0) {
306		va_copy(ap_new, ap);
307		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
308		    ap_new, cache_info->mdata);
309		va_end(ap_new);
310
311		if (res != NS_SUCCESS) {
312			free(buffer);
313			return (res);
314		} else
315			res = 0;
316	} else {
317		free(buffer);
318		__close_cached_mp_read_session(rs);
319		rs = INVALID_CACHED_MP_READ_SESSION;
320		cache_info->set_mp_rs_func(rs);
321		return (res == -1 ? NS_RETURN : NS_UNAVAIL);
322	}
323
324	free(buffer);
325	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
326}
327
328int
329__nss_mp_cache_write(void *retval, void *mdata, va_list ap)
330{
331	struct cached_connection_params params;
332	cached_mp_write_session ws;
333
334	char *buffer;
335	size_t buffer_size;
336
337	nss_cache_info const *cache_info;
338	nss_cache_data *cache_data;
339	va_list ap_new;
340	int res;
341
342	cache_data = (nss_cache_data *)mdata;
343	cache_info = cache_data->info;
344
345	ws = cache_info->get_mp_ws_func();
346	if (ws == INVALID_CACHED_MP_WRITE_SESSION) {
347		memset(&params, 0, sizeof(struct cached_connection_params));
348		params.socket_path = CACHED_SOCKET_PATH;
349
350		ws = __open_cached_mp_write_session(&params,
351		    cache_info->entry_name);
352		if (ws == INVALID_CACHED_MP_WRITE_SESSION)
353			return (NS_UNAVAIL);
354
355		cache_info->set_mp_ws_func(ws);
356	}
357
358	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
359	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
360	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
361
362	do {
363		size_t size;
364
365		size = buffer_size;
366		va_copy(ap_new, ap);
367		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
368		    cache_info->mdata);
369		va_end(ap_new);
370
371		if (res == NS_RETURN) {
372			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
373				break;
374
375			buffer_size <<= 1;
376			buffer = (char *)realloc(buffer, buffer_size);
377			memset(buffer, 0, buffer_size);
378		}
379	} while (res == NS_RETURN);
380
381	if (res != NS_SUCCESS) {
382		free(buffer);
383		return (res);
384	}
385
386	res = __cached_mp_write(ws, buffer, buffer_size);
387
388	free(buffer);
389	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
390}
391
392int
393__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap)
394{
395	cached_mp_write_session ws;
396
397	nss_cache_info const *cache_info;
398	nss_cache_data *cache_data;
399
400	cache_data = (nss_cache_data *)mdata;
401	cache_info = cache_data->info;
402
403	ws = cache_info->get_mp_ws_func();
404	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
405		__close_cached_mp_write_session(ws);
406		ws = INVALID_CACHED_MP_WRITE_SESSION;
407		cache_info->set_mp_ws_func(ws);
408	}
409	return (NS_UNAVAIL);
410}
411
412int
413__nss_mp_cache_end(void *retval, void *mdata, va_list ap)
414{
415	cached_mp_write_session ws;
416	cached_mp_read_session rs;
417
418	nss_cache_info const *cache_info;
419	nss_cache_data *cache_data;
420
421	cache_data = (nss_cache_data *)mdata;
422	cache_info = cache_data->info;
423
424	ws = cache_info->get_mp_ws_func();
425	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
426		__abandon_cached_mp_write_session(ws);
427		ws = INVALID_CACHED_MP_WRITE_SESSION;
428		cache_info->set_mp_ws_func(ws);
429	}
430
431	rs = cache_info->get_mp_rs_func();
432	if (rs != INVALID_CACHED_MP_READ_SESSION) {
433		__close_cached_mp_read_session(rs);
434		rs = INVALID_CACHED_MP_READ_SESSION;
435		cache_info->set_mp_rs_func(rs);
436	}
437
438	return (NS_UNAVAIL);
439}
440