1 /*
2  * Copyright (c) 2001, 2002, 2003, 2004, 2005  Netli, Inc.
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  * $Id: ncnf_app.c,v 1.1 2005/05/26 12:08:19 vlm Exp $
27  */
28 #include "headers.h"
29 #include "ncnf_app.h"
30 #include "ncnf_app_int.h"
31 #include "ncnf_int.h"
32 #include "ncnf_find.h"
33 
34 /*
35  * Fetch the entity from the tree by the given sysid.
36  */
37 ncnf_obj *
NCNF_APP_resolve_sysid(ncnf_obj * root,const char * sysid)38 NCNF_APP_resolve_sysid(ncnf_obj *root, const char *sysid) {
39 	ncnf_sf_svect *sv;
40 	int token;
41 	ncnf_obj *cur;
42 
43 	/* Don't take nothing */
44 	if(root == NULL || sysid == NULL || sysid[0] == '\0') {
45 		errno = EINVAL;
46 		return NULL;
47 	}
48 
49 	/* We do want only the root object */
50 	if(ncnf_obj_type(root)) {
51 		errno = EINVAL;
52 		return NULL;
53 	}
54 
55 	/* Split path by tokens */
56 	sv = ncnf_sf_split(sysid, "@", 0);
57 	if(sv == NULL)
58 		return NULL;
59 
60 	/* Walk down the tree in search of the next token */
61 	for(cur = root, token = sv->count - 1; token >= 0; token--) {
62 		cur = ncnf_get_obj(cur,
63 			NULL, sv->list[token],
64 			NCNF_FIRST_OBJECT);
65 		if(cur == NULL)
66 			break;
67 	}
68 
69 	ncnf_sf_sfree(sv);
70 
71 	if(cur == NULL) {
72 		errno = ESRCH;
73 		return NULL;
74 	} else if(cur == root) {
75 		errno = EINVAL;	/* Invalid empty path specified */
76 		return NULL;
77 	}
78 
79 	return cur;
80 }
81 
82 /*
83  * Fetch the entity from the tree by the given path.
84  */
85 ncnf_obj *
NCNF_APP_resolve_path(ncnf_obj * root,const char * config_path)86 NCNF_APP_resolve_path(ncnf_obj *root, const char *config_path) {
87 	ncnf_sf_svect *sv;
88 	unsigned int token;
89 	ncnf_obj *cur;
90 
91 	/* Don't take nothing */
92 	if(root == NULL || config_path == NULL || config_path[0] == '\0') {
93 		errno = EINVAL;
94 		return NULL;
95 	}
96 
97 	/* We do want only the root object */
98 	if(ncnf_obj_type(root)) {
99 		errno = EINVAL;
100 		return NULL;
101 	}
102 
103 	/* Split path by tokens */
104 	sv = ncnf_sf_split(config_path, "/", 0);
105 	if(sv == NULL)
106 		return NULL;
107 
108 	/* Walk down the tree in search of the next token */
109 	for(cur = root, token = 0; token < sv->count; token++) {
110 		cur = ncnf_get_obj(cur,
111 			NULL, sv->list[token],
112 			NCNF_FIRST_OBJECT);
113 		if(cur == NULL)
114 			break;
115 	}
116 
117 	ncnf_sf_sfree(sv);
118 
119 	if(cur == NULL) {
120 		errno = ESRCH;
121 		return NULL;
122 	} else if(cur == root) {
123 		errno = EINVAL;	/* Invalid empty path specified */
124 		return NULL;
125 	}
126 
127 	return cur;
128 }
129 
130 
131 static void
_figure_out_ids(ncnf_obj * process,uid_t * uid,gid_t * gid)132 _figure_out_ids(ncnf_obj *process, uid_t *uid, gid_t *gid) {
133 	int i;
134 
135 	if(ncnf_get_attr_int(process, "uid", &i) == 0)
136 		*uid = i;
137 	else
138 		*uid = -1;
139 
140 	if(ncnf_get_attr_int(process, "gid", &i) == 0)
141 		*gid = i;
142 	else
143 		*gid = -1;
144 
145 }
146 
147 /*
148  * Do basic initialization of the process environment.
149  */
150 int
NCNF_APP_initialize_process(ncnf_obj * process)151 NCNF_APP_initialize_process(ncnf_obj *process) {
152 	char *s;
153 	int ret = 0;
154 	uid_t new_uid = -1;
155 	gid_t new_gid = -1;
156 	uid_t saved_euid = -1;
157 	gid_t saved_egid = -1;
158 	int tmp;
159 
160 
161 	if(process == NULL
162 		|| ncnf_obj_type(process) == NULL
163 		|| strcmp(ncnf_obj_type(process), "process")) {
164 		errno = EINVAL;
165 		return -1;
166 	}
167 
168 
169 	/*
170 	 * Figure out the effective ID's.
171 	 */
172 	_figure_out_ids(process, &new_uid, &new_gid);
173 
174 	/*
175 	 * Create temporary process permissions.
176 	 */
177 	if(new_gid != -1) {
178 		saved_egid = getegid();
179 		setegid(new_gid);
180 	}
181 	if(new_uid != -1) {
182 		saved_euid = geteuid();
183 		seteuid(new_uid);
184 	}
185 
186 	s = ncnf_get_attr(process, "chroot");
187 	if(s && chroot(s)) {
188 		_ncnf_debug_print(0,
189 			"Chroot(\"%s\") failed: %s",
190 			s, strerror(errno)
191 		);
192 		ret = -1;
193 		goto finish;
194 	}
195 
196 	s = ncnf_get_attr(process, "chdir");
197 	if(s && chdir(s)) {
198 		_ncnf_debug_print(0,
199 			"Chdir(\"%s\") failed: %s",
200 			s, strerror(errno)
201 		);
202 		ret = -1;
203 		goto finish;
204 	}
205 
206 	/*
207 	 * Create and initialize the pid file.
208 	 */
209 	if(ncnf_lazy_notificator(process, "pidfile",
210 		__na_pidfile_notificator, NULL)) {
211 		/*
212 		 * If pidfile is not initialized,
213 		 * initialize logging anyway.
214 		 */
215 		/* EPERM? */
216 		ret = -1;
217 	}
218 
219 	/*
220 	 * This parameter sets the name of a program used for
221 	 * asynchronous NCNF validation.
222 	 */
223 	(void)ncnf_lazy_notificator(process, "reload-ncnf-validator",
224 		__na_reload_ncnf_validator_notificator, NULL);
225 	(void)ncnf_lazy_notificator(process, "reload-ncnf-validator-ncql",
226 		__na_reload_ncnf_validator_ncql_notificator, NULL);
227 
228 	/*
229 	 * Disable swapping and core dumping if configured so.
230 	 */
231 	if(ncnf_get_attr_int(process, "do-not-swap", &tmp)) {
232 		if(0) {
233 		_ncnf_debug_print(0,
234 			"do-not-swap is not given, skipping mlockall()");
235 		}
236 	} else if(tmp == 0) {
237 		_ncnf_debug_print(0,
238 			"do-not-swap is DISABLED, skipping mlockall()");
239 	} else if(
240 #ifdef	__linux__
241 		mlockall(MCL_CURRENT | MCL_FUTURE)
242 #else	/* __linux__ */
243 		1
244 #endif	/* __linux__ */
245 	) {
246 		_ncnf_debug_print(1,
247 			"Security measure failure: mlockall(): %s",
248 			strerror(errno));
249 		ret = -1;	/* Caller must exit() */
250 	} else {
251 		_ncnf_debug_print(0, "mlockall() performed");
252 	}
253 
254 finish:
255 
256 	/*
257 	 * Give away temporary permissions.
258 	 */
259 
260 	if(saved_egid != -1)
261 		setegid(saved_egid);
262 	if(saved_euid != -1)
263 		seteuid(saved_euid);
264 
265 	return ret;
266 }
267 
268 bstr_t
NCNF_APP_construct_id(ncnf_obj * obj)269 NCNF_APP_construct_id(ncnf_obj *obj) {
270 	bstr_t b;
271 
272 	b = str2bstr(NULL, 15);
273 	if(b) {
274 		int wrote = ncnf_construct_path(obj, "@", 1,
275 			ncnf_obj_name, b, bstr_len(b));
276 		if(wrote > bstr_len(b)) {
277 			bstr_free(b);
278 			b = str2bstr(NULL, wrote);
279 			if(b) {
280 				wrote = ncnf_construct_path(obj, "@", 1,
281 					ncnf_obj_name, b, bstr_len(b) + 1);
282 				assert(wrote <= bstr_len(b));
283 			}
284 		}
285 	}
286 
287 	return b;
288 }
289 
290 /*
291  * Update pidfile when pid is being changed (after fork())
292  */
293 int
NCNF_APP_pidfile_update(ncnf_obj * process)294 NCNF_APP_pidfile_update(ncnf_obj *process) {
295 	return __na_pidfile_update(process, getpid());
296 }
297 
298 /*
299  * Update pidfile when process is finishing.
300  */
301 int
NCNF_APP_pidfile_finishing(ncnf_obj * process)302 NCNF_APP_pidfile_finishing(ncnf_obj *process) {
303 	return __na_pidfile_update(process, 0);
304 }
305 
306 int
NCNF_APP_pidfile_write(int pfd,pid_t pid)307 NCNF_APP_pidfile_write(int pfd, pid_t pid) {
308 	return __na_write_pid_file(pfd, pid);
309 }
310 
311 void
NCNF_APP_pidfile_manual_handler(void (* onUnload)(int pfd,const char * filename))312 NCNF_APP_pidfile_manual_handler(
313 	void (*onUnload)(int pfd, const char *filename)) {
314 	__na_pidfile_manual_handler(onUnload);
315 }
316 
317 
318 void (*NCNF_APP_pidfile_open_failed_callback)(char *filename, int is_firsttime)
319 	= __na_default_pidfile_open_failed_callback;
320 
321 
322 /*
323  * Establish process permissions and environment.
324  */
325 int
NCNF_APP_set_permissions(ncnf_obj * process,enum ncnf_app_perm_set set)326 NCNF_APP_set_permissions(ncnf_obj *process, enum ncnf_app_perm_set set) {
327 	int was_all;
328 
329 	if(process == NULL) {
330 		errno = EINVAL;
331 		return -1;
332 	}
333 
334 	if(set == NAPS_ALL)
335 		was_all = 1;
336 	else
337 		was_all = 0;
338 
339 
340 	if(set & NAPS_SETGID) {
341 		int id_numeric = -1;
342 		int id_literal = -1;
343 		int i;
344 		char *s;
345 
346 		if(ncnf_get_attr_int(process, "gid", &i) == 0)
347 			id_numeric = i;
348 
349 		s = ncnf_get_attr(process, "group");
350 		if(s) {
351 			struct group *grp = getgrnam(s);
352 			if(grp)
353 				id_literal = grp->gr_gid;
354 		}
355 
356 		if(id_numeric != -1 || id_literal != -1) {
357 
358 			if(id_numeric != -1 && id_literal != -1) {
359 				if(id_numeric != id_literal)
360 					/* Numbers should be exactly the same, if set. */
361 					return -1;
362 			}
363 
364 			i = (id_literal == -1) ? id_numeric : id_literal;
365 
366 			if(setgid(i) == -1)
367 				return -1;
368 		}
369 
370 		set &= ~NAPS_SETGID;
371 	}
372 
373 	if(set & NAPS_SETUID) {
374 		int id_numeric = -1;
375 		int id_literal = -1;
376 		int i;
377 		char *s;
378 
379 		if(ncnf_get_attr_int(process, "uid", &i) == 0)
380 			id_numeric = i;
381 
382 		s = ncnf_get_attr(process, "user");
383 		if(s) {
384 			struct passwd *pw = getpwnam(s);
385 			if(pw)
386 				id_literal = pw->pw_uid;
387 		}
388 
389 		if(id_numeric != -1 || id_literal != -1) {
390 
391 			if(id_numeric != -1 && id_literal != -1) {
392 				if(id_numeric != id_literal)
393 					/* Numbers should be exactly the same, if set. */
394 					return -1;
395 			}
396 
397 			i = (id_literal == -1) ? id_numeric : id_literal;
398 
399 			if(setuid(i) == -1)
400 				return -1;
401 		}
402 
403 		set &= ~NAPS_SETUID;
404 	}
405 
406 
407 	/*
408 	 * Check if all options were used.
409 	 */
410 
411 	if(set && !was_all) {
412 		/* some options left */
413 		errno = EINVAL;
414 		return -1;
415 	}
416 
417 	return 0;
418 }
419 
420 /*
421  * Universal function to retrieve a list of configuration objects
422  * at the specified configuration tree level.
423  */
424 ncnf_obj *
NCNF_APP_find_objects(ncnf_obj * start_level,char * types_tree,int (* opt_filter)(ncnf_obj *,void *),void * opt_key)425 NCNF_APP_find_objects(ncnf_obj *start_level,
426 	char *types_tree,
427 	int (*opt_filter)(ncnf_obj *, void *),
428 	void *opt_key) {
429 
430 	if(start_level == NULL || types_tree == NULL) {
431 		errno = EINVAL;
432 		return NULL;
433 	}
434 
435 	return (ncnf_obj *)_na_find_objects(
436 		(struct ncnf_obj_s *)start_level,
437 		types_tree,
438 		(int (*)(struct ncnf_obj_s *, void *))(opt_filter),
439 		opt_key);
440 }
441