xref: /freebsd/lib/libc/net/nscache.c (revision 49b49cda41feabe3439f7318e8bf40e3896c7bf4)
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 
48 int
49 __nss_cache_handler(void *retval, void *mdata, va_list ap)
50 {
51 	return (NS_UNAVAIL);
52 }
53 
54 int
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 
158 int
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 
227 int
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 
260 int
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 
328 int
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 
392 int
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 
412 int
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