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