xref: /freebsd/lib/libc/net/nscache.c (revision 4c0d7cdf5d3b64e235140553601c0dd5827429a7)
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 #include <nsswitch.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include "un-namespace.h"
36 #include "nscachedcli.h"
37 #include "nscache.h"
38 
39 #define NSS_CACHE_KEY_INITIAL_SIZE	(256)
40 #define NSS_CACHE_KEY_SIZE_LIMIT	(NSS_CACHE_KEY_INITIAL_SIZE << 4)
41 
42 #define NSS_CACHE_BUFFER_INITIAL_SIZE	(1024)
43 #define NSS_CACHE_BUFFER_SIZE_LIMIT	(NSS_CACHE_BUFFER_INITIAL_SIZE << 8)
44 
45 #define CACHED_SOCKET_PATH 		"/var/run/nscd"
46 
47 int
48 __nss_cache_handler(void *retval, void *mdata, va_list ap)
49 {
50 	return (NS_UNAVAIL);
51 }
52 
53 int
54 __nss_common_cache_read(void *retval, void *mdata, va_list ap)
55 {
56 	struct cached_connection_params params;
57 	cached_connection connection;
58 
59 	char *buffer;
60 	size_t buffer_size, size;
61 
62 	nss_cache_info const *cache_info;
63 	nss_cache_data *cache_data;
64 	va_list ap_new;
65 	int res;
66 
67 	cache_data = (nss_cache_data *)mdata;
68 	cache_info = cache_data->info;
69 
70 	memset(&params, 0, sizeof(struct cached_connection_params));
71 	params.socket_path = CACHED_SOCKET_PATH;
72 
73 	cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE);
74 	memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE);
75 	cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE;
76 	va_copy(ap_new, ap);
77 
78 	do {
79 		size = cache_data->key_size;
80 		res = cache_info->id_func(cache_data->key, &size, ap_new,
81 		    cache_info->mdata);
82 		va_end(ap_new);
83 		if (res == NS_RETURN) {
84 			if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT)
85 				break;
86 
87 			cache_data->key_size <<= 1;
88 			cache_data->key = realloc(cache_data->key,
89 			    cache_data->key_size);
90 			memset(cache_data->key, 0, cache_data->key_size);
91 			va_copy(ap_new, ap);
92 		}
93 	} while (res == NS_RETURN);
94 
95 	if (res != NS_SUCCESS) {
96 		free(cache_data->key);
97 		cache_data->key = NULL;
98 		cache_data->key_size = 0;
99 		return (res);
100 	} else
101 		cache_data->key_size = size;
102 
103 	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
104 	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
105 	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
106 
107 	do {
108 		connection = __open_cached_connection(&params);
109 		if (connection == NULL) {
110 			res = -1;
111 			break;
112 		}
113 		res = __cached_read(connection, cache_info->entry_name,
114 		    cache_data->key, cache_data->key_size, buffer,
115 		    &buffer_size);
116 		__close_cached_connection(connection);
117 		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
118 			buffer = (char *)realloc(buffer, buffer_size);
119 			memset(buffer, 0, buffer_size);
120 		}
121 	} while (res == -2);
122 
123 	if (res == 0) {
124 		if (buffer_size == 0) {
125 			free(buffer);
126 			free(cache_data->key);
127 			cache_data->key = NULL;
128 			cache_data->key_size = 0;
129 			return (NS_RETURN);
130 		}
131 
132 		va_copy(ap_new, ap);
133 		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
134 		    ap_new, cache_info->mdata);
135 		va_end(ap_new);
136 
137 		if (res != NS_SUCCESS) {
138 			free(buffer);
139 			free(cache_data->key);
140 			cache_data->key = NULL;
141 			cache_data->key_size = 0;
142 			return (res);
143 		} else
144 			res = 0;
145 	}
146 
147 	if (res == 0) {
148 		free(cache_data->key);
149 		cache_data->key = NULL;
150 		cache_data->key_size = 0;
151 	}
152 
153 	free(buffer);
154 	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
155 }
156 
157 int
158 __nss_common_cache_write(void *retval, void *mdata, va_list ap)
159 {
160 	struct cached_connection_params params;
161 	cached_connection connection;
162 
163 	char *buffer;
164 	size_t buffer_size;
165 
166 	nss_cache_info const *cache_info;
167 	nss_cache_data *cache_data;
168 	va_list ap_new;
169 	int res;
170 
171 	cache_data = (nss_cache_data *)mdata;
172 	cache_info = cache_data->info;
173 
174 	if (cache_data->key == NULL)
175 		return (NS_UNAVAIL);
176 
177 	memset(&params, 0, sizeof(struct cached_connection_params));
178 	params.socket_path = CACHED_SOCKET_PATH;
179 
180 	connection = __open_cached_connection(&params);
181 	if (connection == NULL) {
182 		free(cache_data->key);
183 		return (NS_UNAVAIL);
184 	}
185 
186 	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
187 	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
188 	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
189 
190 	do {
191 		size_t size;
192 
193 		size = buffer_size;
194 		va_copy(ap_new, ap);
195 		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
196 		    cache_info->mdata);
197 		va_end(ap_new);
198 
199 		if (res == NS_RETURN) {
200 			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
201 				break;
202 
203 			buffer_size <<= 1;
204 			buffer = (char *)realloc(buffer, buffer_size);
205 			memset(buffer, 0, buffer_size);
206 		}
207 	} while (res == NS_RETURN);
208 
209 	if (res != NS_SUCCESS) {
210 		__close_cached_connection(connection);
211 		free(cache_data->key);
212 		free(buffer);
213 		return (res);
214 	}
215 
216 	res = __cached_write(connection, cache_info->entry_name,
217 	    cache_data->key, cache_data->key_size, buffer, buffer_size);
218 	__close_cached_connection(connection);
219 
220 	free(cache_data->key);
221 	free(buffer);
222 
223 	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
224 }
225 
226 int
227 __nss_common_cache_write_negative(void *mdata)
228 {
229 	struct cached_connection_params params;
230 	cached_connection connection;
231 	int res;
232 
233 	nss_cache_info const *cache_info;
234 	nss_cache_data *cache_data;
235 
236 	cache_data = (nss_cache_data *)mdata;
237 	cache_info = cache_data->info;
238 
239 	if (cache_data->key == NULL)
240 		return (NS_UNAVAIL);
241 
242 	memset(&params, 0, sizeof(struct cached_connection_params));
243 	params.socket_path = CACHED_SOCKET_PATH;
244 
245 	connection = __open_cached_connection(&params);
246 	if (connection == NULL) {
247 		free(cache_data->key);
248 		return (NS_UNAVAIL);
249 	}
250 
251 	res = __cached_write(connection, cache_info->entry_name,
252 	    cache_data->key, cache_data->key_size, NULL, 0);
253 	__close_cached_connection(connection);
254 
255 	free(cache_data->key);
256 	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
257 }
258 
259 int
260 __nss_mp_cache_read(void *retval, void *mdata, va_list ap)
261 {
262 	struct cached_connection_params params;
263 	cached_mp_read_session rs;
264 
265 	char *buffer;
266 	size_t buffer_size;
267 
268 	nss_cache_info const *cache_info;
269 	nss_cache_data *cache_data;
270 	va_list ap_new;
271 	int res;
272 
273 	cache_data = (nss_cache_data *)mdata;
274 	cache_info = cache_data->info;
275 
276 	if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION)
277 		return (NS_UNAVAIL);
278 
279 	rs = cache_info->get_mp_rs_func();
280 	if (rs == INVALID_CACHED_MP_READ_SESSION) {
281 		memset(&params, 0, sizeof(struct cached_connection_params));
282 		params.socket_path = CACHED_SOCKET_PATH;
283 
284 		rs = __open_cached_mp_read_session(&params,
285 		    cache_info->entry_name);
286 		if (rs == INVALID_CACHED_MP_READ_SESSION)
287 			return (NS_UNAVAIL);
288 
289 		cache_info->set_mp_rs_func(rs);
290 	}
291 
292 	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
293 	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
294 	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
295 
296 	do {
297 		res = __cached_mp_read(rs, buffer, &buffer_size);
298 		if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
299 			buffer = (char *)realloc(buffer, buffer_size);
300 			memset(buffer, 0, buffer_size);
301 		}
302 	} while (res == -2);
303 
304 	if (res == 0) {
305 		va_copy(ap_new, ap);
306 		res = cache_info->unmarshal_func(buffer, buffer_size, retval,
307 		    ap_new, cache_info->mdata);
308 		va_end(ap_new);
309 
310 		if (res != NS_SUCCESS) {
311 			free(buffer);
312 			return (res);
313 		} else
314 			res = 0;
315 	} else {
316 		free(buffer);
317 		__close_cached_mp_read_session(rs);
318 		rs = INVALID_CACHED_MP_READ_SESSION;
319 		cache_info->set_mp_rs_func(rs);
320 		return (res == -1 ? NS_RETURN : NS_UNAVAIL);
321 	}
322 
323 	free(buffer);
324 	return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
325 }
326 
327 int
328 __nss_mp_cache_write(void *retval, void *mdata, va_list ap)
329 {
330 	struct cached_connection_params params;
331 	cached_mp_write_session ws;
332 
333 	char *buffer;
334 	size_t buffer_size;
335 
336 	nss_cache_info const *cache_info;
337 	nss_cache_data *cache_data;
338 	va_list ap_new;
339 	int res;
340 
341 	cache_data = (nss_cache_data *)mdata;
342 	cache_info = cache_data->info;
343 
344 	ws = cache_info->get_mp_ws_func();
345 	if (ws == INVALID_CACHED_MP_WRITE_SESSION) {
346 		memset(&params, 0, sizeof(struct cached_connection_params));
347 		params.socket_path = CACHED_SOCKET_PATH;
348 
349 		ws = __open_cached_mp_write_session(&params,
350 		    cache_info->entry_name);
351 		if (ws == INVALID_CACHED_MP_WRITE_SESSION)
352 			return (NS_UNAVAIL);
353 
354 		cache_info->set_mp_ws_func(ws);
355 	}
356 
357 	buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
358 	buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
359 	memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
360 
361 	do {
362 		size_t size;
363 
364 		size = buffer_size;
365 		va_copy(ap_new, ap);
366 		res = cache_info->marshal_func(buffer, &size, retval, ap_new,
367 		    cache_info->mdata);
368 		va_end(ap_new);
369 
370 		if (res == NS_RETURN) {
371 			if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
372 				break;
373 
374 			buffer_size <<= 1;
375 			buffer = (char *)realloc(buffer, buffer_size);
376 			memset(buffer, 0, buffer_size);
377 		}
378 	} while (res == NS_RETURN);
379 
380 	if (res != NS_SUCCESS) {
381 		free(buffer);
382 		return (res);
383 	}
384 
385 	res = __cached_mp_write(ws, buffer, buffer_size);
386 
387 	free(buffer);
388 	return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
389 }
390 
391 int
392 __nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap)
393 {
394 	cached_mp_write_session ws;
395 
396 	nss_cache_info const *cache_info;
397 	nss_cache_data *cache_data;
398 
399 	cache_data = (nss_cache_data *)mdata;
400 	cache_info = cache_data->info;
401 
402 	ws = cache_info->get_mp_ws_func();
403 	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
404 		__close_cached_mp_write_session(ws);
405 		ws = INVALID_CACHED_MP_WRITE_SESSION;
406 		cache_info->set_mp_ws_func(ws);
407 	}
408 	return (NS_UNAVAIL);
409 }
410 
411 int
412 __nss_mp_cache_end(void *retval, void *mdata, va_list ap)
413 {
414 	cached_mp_write_session ws;
415 	cached_mp_read_session rs;
416 
417 	nss_cache_info const *cache_info;
418 	nss_cache_data *cache_data;
419 
420 	cache_data = (nss_cache_data *)mdata;
421 	cache_info = cache_data->info;
422 
423 	ws = cache_info->get_mp_ws_func();
424 	if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
425 		__abandon_cached_mp_write_session(ws);
426 		ws = INVALID_CACHED_MP_WRITE_SESSION;
427 		cache_info->set_mp_ws_func(ws);
428 	}
429 
430 	rs = cache_info->get_mp_rs_func();
431 	if (rs != INVALID_CACHED_MP_READ_SESSION) {
432 		__close_cached_mp_read_session(rs);
433 		rs = INVALID_CACHED_MP_READ_SESSION;
434 		cache_info->set_mp_rs_func(rs);
435 	}
436 
437 	return (NS_UNAVAIL);
438 }
439