xref: /freebsd/lib/libc/net/nscache.c (revision 0a36787e4c1fa0cf77dcf83be0867178476e372b)
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 
50 int
51 __nss_cache_handler(void *retval, void *mdata, va_list ap)
52 {
53 	return (NS_UNAVAIL);
54 }
55 
56 int
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 
160 int
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 
229 int
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 
262 int
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 
330 int
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 
394 int
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 
414 int
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