xref: /freebsd/usr.sbin/nscd/config.c (revision fcb560670601b2a4d87bb31d7531c8dcc37ee71b)
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 <sys/stat.h>
32 #include <sys/time.h>
33 
34 #include <assert.h>
35 #include <math.h>
36 #include <nsswitch.h>
37 #include <pthread.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include "config.h"
43 #include "debug.h"
44 #include "log.h"
45 
46 /*
47  * Default entries, which always exist in the configuration
48  */
49 const char *c_default_entries[6] = {
50 	NSDB_PASSWD,
51 	NSDB_GROUP,
52 	NSDB_HOSTS,
53 	NSDB_SERVICES,
54 	NSDB_PROTOCOLS,
55 	NSDB_RPC
56 	};
57 
58 static int configuration_entry_cmp(const void *, const void *);
59 static int configuration_entry_sort_cmp(const void *, const void *);
60 static int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
61 static int configuration_entry_cache_mp_cmp(const void *, const void *);
62 static int configuration_entry_cache_mp_part_cmp(const void *, const void *);
63 static struct configuration_entry *create_configuration_entry(const char *,
64 	struct timeval const *, struct timeval const *,
65 	struct common_cache_entry_params const *,
66 	struct common_cache_entry_params const *,
67 	struct mp_cache_entry_params const *);
68 
69 static int
70 configuration_entry_sort_cmp(const void *e1, const void *e2)
71 {
72 	return (strcmp((*((struct configuration_entry **)e1))->name,
73 		(*((struct configuration_entry **)e2))->name
74 		));
75 }
76 
77 static int
78 configuration_entry_cmp(const void *e1, const void *e2)
79 {
80 	return (strcmp((const char *)e1,
81 		(*((struct configuration_entry **)e2))->name
82 		));
83 }
84 
85 static int
86 configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
87 {
88 	return (strcmp((*((cache_entry *)e1))->params->entry_name,
89 		(*((cache_entry *)e2))->params->entry_name
90 		));
91 }
92 
93 static int
94 configuration_entry_cache_mp_cmp(const void *e1, const void *e2)
95 {
96 	return (strcmp((const char *)e1,
97 		(*((cache_entry *)e2))->params->entry_name
98 		));
99 }
100 
101 static int
102 configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
103 {
104 	return (strncmp((const char *)e1,
105 		(*((cache_entry *)e2))->params->entry_name,
106 		strlen((const char *)e1)
107 		));
108 }
109 
110 static struct configuration_entry *
111 create_configuration_entry(const char *name,
112 	struct timeval const *common_timeout,
113 	struct timeval const *mp_timeout,
114 	struct common_cache_entry_params const *positive_params,
115 	struct common_cache_entry_params const *negative_params,
116 	struct mp_cache_entry_params const *mp_params)
117 {
118 	struct configuration_entry *retval;
119 	size_t	size;
120 	int res;
121 
122 	TRACE_IN(create_configuration_entry);
123 	assert(name != NULL);
124 	assert(positive_params != NULL);
125 	assert(negative_params != NULL);
126 	assert(mp_params != NULL);
127 
128 	retval = calloc(1,
129 		sizeof(*retval));
130 	assert(retval != NULL);
131 
132 	res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
133 	if (res != 0) {
134 		free(retval);
135 		LOG_ERR_2("create_configuration_entry",
136 			"can't create positive cache lock");
137 		TRACE_OUT(create_configuration_entry);
138 		return (NULL);
139 	}
140 
141 	res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
142 	if (res != 0) {
143 		pthread_mutex_destroy(&retval->positive_cache_lock);
144 		free(retval);
145 		LOG_ERR_2("create_configuration_entry",
146 			"can't create negative cache lock");
147 		TRACE_OUT(create_configuration_entry);
148 		return (NULL);
149 	}
150 
151 	res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
152 	if (res != 0) {
153 		pthread_mutex_destroy(&retval->positive_cache_lock);
154 		pthread_mutex_destroy(&retval->negative_cache_lock);
155 		free(retval);
156 		LOG_ERR_2("create_configuration_entry",
157 			"can't create negative cache lock");
158 		TRACE_OUT(create_configuration_entry);
159 		return (NULL);
160 	}
161 
162 	memcpy(&retval->positive_cache_params, positive_params,
163 		sizeof(struct common_cache_entry_params));
164 	memcpy(&retval->negative_cache_params, negative_params,
165 		sizeof(struct common_cache_entry_params));
166 	memcpy(&retval->mp_cache_params, mp_params,
167 		sizeof(struct mp_cache_entry_params));
168 
169 	size = strlen(name);
170 	retval->name = calloc(1, size + 1);
171 	assert(retval->name != NULL);
172 	memcpy(retval->name, name, size);
173 
174 	memcpy(&retval->common_query_timeout, common_timeout,
175 		sizeof(struct timeval));
176 	memcpy(&retval->mp_query_timeout, mp_timeout,
177 		sizeof(struct timeval));
178 
179 	asprintf(&retval->positive_cache_params.cep.entry_name, "%s+", name);
180 	assert(retval->positive_cache_params.cep.entry_name != NULL);
181 
182 	asprintf(&retval->negative_cache_params.cep.entry_name, "%s-", name);
183 	assert(retval->negative_cache_params.cep.entry_name != NULL);
184 
185 	asprintf(&retval->mp_cache_params.cep.entry_name, "%s*", name);
186 	assert(retval->mp_cache_params.cep.entry_name != NULL);
187 
188 	TRACE_OUT(create_configuration_entry);
189 	return (retval);
190 }
191 
192 /*
193  * Creates configuration entry and fills it with default values
194  */
195 struct configuration_entry *
196 create_def_configuration_entry(const char *name)
197 {
198 	struct common_cache_entry_params positive_params, negative_params;
199 	struct mp_cache_entry_params mp_params;
200 	struct timeval default_common_timeout, default_mp_timeout;
201 
202 	struct configuration_entry *res = NULL;
203 
204 	TRACE_IN(create_def_configuration_entry);
205 	memset(&positive_params, 0,
206 		sizeof(struct common_cache_entry_params));
207 	positive_params.cep.entry_type = CET_COMMON;
208 	positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
209 	positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
210 	positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
211 	positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
212 	positive_params.confidence_threshold = DEFAULT_POSITIVE_CONF_THRESH;
213 	positive_params.policy = CPT_LRU;
214 
215 	memcpy(&negative_params, &positive_params,
216 		sizeof(struct common_cache_entry_params));
217 	negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
218 	negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
219 	negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
220 	negative_params.confidence_threshold = DEFAULT_NEGATIVE_CONF_THRESH;
221 	negative_params.policy = CPT_FIFO;
222 
223 	memset(&default_common_timeout, 0, sizeof(struct timeval));
224 	default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
225 
226 	memset(&default_mp_timeout, 0, sizeof(struct timeval));
227 	default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
228 
229 	memset(&mp_params, 0,
230 		sizeof(struct mp_cache_entry_params));
231 	mp_params.cep.entry_type = CET_MULTIPART;
232 	mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
233 	mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
234 	mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
235 
236 	res = create_configuration_entry(name, &default_common_timeout,
237 		&default_mp_timeout, &positive_params, &negative_params,
238 		&mp_params);
239 
240 	TRACE_OUT(create_def_configuration_entry);
241 	return (res);
242 }
243 
244 void
245 destroy_configuration_entry(struct configuration_entry *entry)
246 {
247 	TRACE_IN(destroy_configuration_entry);
248 	assert(entry != NULL);
249 	pthread_mutex_destroy(&entry->positive_cache_lock);
250 	pthread_mutex_destroy(&entry->negative_cache_lock);
251 	pthread_mutex_destroy(&entry->mp_cache_lock);
252 	free(entry->name);
253 	free(entry->positive_cache_params.cep.entry_name);
254 	free(entry->negative_cache_params.cep.entry_name);
255 	free(entry->mp_cache_params.cep.entry_name);
256 	free(entry->mp_cache_entries);
257 	free(entry);
258 	TRACE_OUT(destroy_configuration_entry);
259 }
260 
261 int
262 add_configuration_entry(struct configuration *config,
263 	struct configuration_entry *entry)
264 {
265 	TRACE_IN(add_configuration_entry);
266 	assert(entry != NULL);
267 	assert(entry->name != NULL);
268 	if (configuration_find_entry(config, entry->name) != NULL) {
269 		TRACE_OUT(add_configuration_entry);
270 		return (-1);
271 	}
272 
273 	if (config->entries_size == config->entries_capacity) {
274 		struct configuration_entry **new_entries;
275 
276 		config->entries_capacity *= 2;
277 		new_entries = calloc(1,
278 			sizeof(*new_entries) *
279 			config->entries_capacity);
280 		assert(new_entries != NULL);
281 		memcpy(new_entries, config->entries,
282 			sizeof(struct configuration_entry *) *
283 		        config->entries_size);
284 
285 		free(config->entries);
286 		config->entries = new_entries;
287 	}
288 
289 	config->entries[config->entries_size++] = entry;
290 	qsort(config->entries, config->entries_size,
291 		sizeof(struct configuration_entry *),
292 		configuration_entry_sort_cmp);
293 
294 	TRACE_OUT(add_configuration_entry);
295 	return (0);
296 }
297 
298 size_t
299 configuration_get_entries_size(struct configuration *config)
300 {
301 	TRACE_IN(configuration_get_entries_size);
302 	assert(config != NULL);
303 	TRACE_OUT(configuration_get_entries_size);
304 	return (config->entries_size);
305 }
306 
307 struct configuration_entry *
308 configuration_get_entry(struct configuration *config, size_t index)
309 {
310 	TRACE_IN(configuration_get_entry);
311 	assert(config != NULL);
312 	assert(index < config->entries_size);
313 	TRACE_OUT(configuration_get_entry);
314 	return (config->entries[index]);
315 }
316 
317 struct configuration_entry *
318 configuration_find_entry(struct configuration *config,
319 	const char *name)
320 {
321 	struct configuration_entry	**retval;
322 
323 	TRACE_IN(configuration_find_entry);
324 
325 	retval = bsearch(name, config->entries, config->entries_size,
326 		sizeof(struct configuration_entry *), configuration_entry_cmp);
327 	TRACE_OUT(configuration_find_entry);
328 
329 	return ((retval != NULL) ? *retval : NULL);
330 }
331 
332 /*
333  * All multipart cache entries are stored in the configuration_entry in the
334  * sorted array (sorted by names). The 3 functions below manage this array.
335  */
336 
337 int
338 configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
339 	cache_entry c_entry)
340 {
341 	cache_entry *new_mp_entries, *old_mp_entries;
342 
343 	TRACE_IN(configuration_entry_add_mp_cache_entry);
344 	++config_entry->mp_cache_entries_size;
345 	new_mp_entries = malloc(sizeof(*new_mp_entries) *
346 		config_entry->mp_cache_entries_size);
347 	assert(new_mp_entries != NULL);
348 	new_mp_entries[0] = c_entry;
349 
350 	if (config_entry->mp_cache_entries_size - 1 > 0) {
351 		memcpy(new_mp_entries + 1,
352 		    config_entry->mp_cache_entries,
353 		    (config_entry->mp_cache_entries_size - 1) *
354 		    sizeof(cache_entry));
355 	}
356 
357 	old_mp_entries = config_entry->mp_cache_entries;
358 	config_entry->mp_cache_entries = new_mp_entries;
359 	free(old_mp_entries);
360 
361 	qsort(config_entry->mp_cache_entries,
362 		config_entry->mp_cache_entries_size,
363 		sizeof(cache_entry),
364 		configuration_entry_cache_mp_sort_cmp);
365 
366 	TRACE_OUT(configuration_entry_add_mp_cache_entry);
367 	return (0);
368 }
369 
370 cache_entry
371 configuration_entry_find_mp_cache_entry(
372 	struct configuration_entry *config_entry, const char *mp_name)
373 {
374 	cache_entry *result;
375 
376 	TRACE_IN(configuration_entry_find_mp_cache_entry);
377 	result = bsearch(mp_name, config_entry->mp_cache_entries,
378 		config_entry->mp_cache_entries_size,
379 		sizeof(cache_entry), configuration_entry_cache_mp_cmp);
380 
381 	if (result == NULL) {
382 		TRACE_OUT(configuration_entry_find_mp_cache_entry);
383 		return (NULL);
384 	} else {
385 		TRACE_OUT(configuration_entry_find_mp_cache_entry);
386 		return (*result);
387 	}
388 }
389 
390 /*
391  * Searches for all multipart entries with names starting with mp_name.
392  * Needed for cache flushing.
393  */
394 int
395 configuration_entry_find_mp_cache_entries(
396 	struct configuration_entry *config_entry, const char *mp_name,
397 	cache_entry **start, cache_entry **finish)
398 {
399 	cache_entry *result;
400 
401 	TRACE_IN(configuration_entry_find_mp_cache_entries);
402 	result = bsearch(mp_name, config_entry->mp_cache_entries,
403 		config_entry->mp_cache_entries_size,
404 		sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
405 
406 	if (result == NULL) {
407 		TRACE_OUT(configuration_entry_find_mp_cache_entries);
408 		return (-1);
409 	}
410 
411 	*start = result;
412 	*finish = result + 1;
413 
414 	while (*start != config_entry->mp_cache_entries) {
415 	    if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
416 		*start = *start - 1;
417 	    else
418 		break;
419 	}
420 
421 	while (*finish != config_entry->mp_cache_entries +
422 		config_entry->mp_cache_entries_size) {
423 
424 	    if (configuration_entry_cache_mp_part_cmp(
425 		mp_name, *finish) == 0)
426 	    	*finish = *finish + 1;
427 	    else
428 		break;
429 	}
430 
431 	TRACE_OUT(configuration_entry_find_mp_cache_entries);
432 	return (0);
433 }
434 
435 /*
436  * Configuration entry uses rwlock to handle access to its fields.
437  */
438 void
439 configuration_lock_rdlock(struct configuration *config)
440 {
441     TRACE_IN(configuration_lock_rdlock);
442     pthread_rwlock_rdlock(&config->rwlock);
443     TRACE_OUT(configuration_lock_rdlock);
444 }
445 
446 void
447 configuration_lock_wrlock(struct configuration *config)
448 {
449     TRACE_IN(configuration_lock_wrlock);
450     pthread_rwlock_wrlock(&config->rwlock);
451     TRACE_OUT(configuration_lock_wrlock);
452 }
453 
454 void
455 configuration_unlock(struct configuration *config)
456 {
457     TRACE_IN(configuration_unlock);
458     pthread_rwlock_unlock(&config->rwlock);
459     TRACE_OUT(configuration_unlock);
460 }
461 
462 /*
463  * Configuration entry uses 3 mutexes to handle cache operations. They are
464  * acquired by configuration_lock_entry and configuration_unlock_entry
465  * functions.
466  */
467 void
468 configuration_lock_entry(struct configuration_entry *entry,
469 	enum config_entry_lock_type lock_type)
470 {
471 	TRACE_IN(configuration_lock_entry);
472 	assert(entry != NULL);
473 
474 	switch (lock_type) {
475 	case CELT_POSITIVE:
476 		pthread_mutex_lock(&entry->positive_cache_lock);
477 		break;
478 	case CELT_NEGATIVE:
479 		pthread_mutex_lock(&entry->negative_cache_lock);
480 		break;
481 	case CELT_MULTIPART:
482 		pthread_mutex_lock(&entry->mp_cache_lock);
483 		break;
484 	default:
485 		/* should be unreachable */
486 		break;
487 	}
488 	TRACE_OUT(configuration_lock_entry);
489 }
490 
491 void
492 configuration_unlock_entry(struct configuration_entry *entry,
493 	enum config_entry_lock_type lock_type)
494 {
495 	TRACE_IN(configuration_unlock_entry);
496 	assert(entry != NULL);
497 
498 	switch (lock_type) {
499 	case CELT_POSITIVE:
500 		pthread_mutex_unlock(&entry->positive_cache_lock);
501 		break;
502 	case CELT_NEGATIVE:
503 		pthread_mutex_unlock(&entry->negative_cache_lock);
504 		break;
505 	case CELT_MULTIPART:
506 		pthread_mutex_unlock(&entry->mp_cache_lock);
507 		break;
508 	default:
509 		/* should be unreachable */
510 		break;
511 	}
512 	TRACE_OUT(configuration_unlock_entry);
513 }
514 
515 struct configuration *
516 init_configuration(void)
517 {
518 	struct configuration	*retval;
519 
520 	TRACE_IN(init_configuration);
521 	retval = calloc(1, sizeof(*retval));
522 	assert(retval != NULL);
523 
524 	retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
525 	retval->entries = calloc(1,
526 		sizeof(*retval->entries) *
527 		retval->entries_capacity);
528 	assert(retval->entries != NULL);
529 
530 	pthread_rwlock_init(&retval->rwlock, NULL);
531 
532 	TRACE_OUT(init_configuration);
533 	return (retval);
534 }
535 
536 void
537 fill_configuration_defaults(struct configuration *config)
538 {
539 	size_t	len, i;
540 
541 	TRACE_IN(fill_configuration_defaults);
542 	assert(config != NULL);
543 
544 	if (config->socket_path != NULL)
545 		free(config->socket_path);
546 
547 	len = strlen(DEFAULT_SOCKET_PATH);
548 	config->socket_path = calloc(1, len + 1);
549 	assert(config->socket_path != NULL);
550 	memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
551 
552 	len = strlen(DEFAULT_PIDFILE_PATH);
553 	config->pidfile_path = calloc(1, len + 1);
554 	assert(config->pidfile_path != NULL);
555 	memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
556 
557 	config->socket_mode =  S_IFSOCK | S_IRUSR | S_IWUSR |
558 		S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
559 	config->force_unlink = 1;
560 
561 	config->query_timeout = DEFAULT_QUERY_TIMEOUT;
562 	config->threads_num = DEFAULT_THREADS_NUM;
563 
564 	for (i = 0; i < config->entries_size; ++i)
565 		destroy_configuration_entry(config->entries[i]);
566 	config->entries_size = 0;
567 
568 	TRACE_OUT(fill_configuration_defaults);
569 }
570 
571 void
572 destroy_configuration(struct configuration *config)
573 {
574 	unsigned int i;
575 
576 	TRACE_IN(destroy_configuration);
577 	assert(config != NULL);
578 	free(config->pidfile_path);
579 	free(config->socket_path);
580 
581 	for (i = 0; i < config->entries_size; ++i)
582 		destroy_configuration_entry(config->entries[i]);
583 	free(config->entries);
584 
585 	pthread_rwlock_destroy(&config->rwlock);
586 	free(config);
587 	TRACE_OUT(destroy_configuration);
588 }
589