1 /*
2  * main.c - contains the main() function
3  *
4  * Copyright (C) 1998 Brad M. Garcia <garsh@home.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 
21 #include <sys/types.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <netdb.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <grp.h>
34 #include <pwd.h>
35 #include <dirent.h>
36 #include <limits.h>
37 
38 #include "relay.h"
39 #include "cache.h"
40 #include "common.h"
41 #include "args.h"
42 #include "master.h"
43 #include "domnode.h"
44 #include "lib.h"
45 #include "qid.h"
46 #include "query.h"
47 #include "dns.h"
48 
49 static int is_writeable (const struct stat* st);
50 static int user_groups_contain (gid_t file_gid);
51 
52 
53 
54 #ifndef __CYGWIN__
init_dnrd_uid(void)55 static void init_dnrd_uid(void) {
56 	char *ep;
57 
58 	/* find the uid */
59 	daemonuid = (uid_t)strtoul(dnrd_user, &ep, 10);
60 	if (dnrd_user[0] == '\0') /* should never happen */
61 		log_err_exit(-1, "Please specify a valid user with -u");
62 
63 	if (*ep) { /* dnrd_user is not numeric */
64 		struct passwd *pwent;
65 		if ((pwent = getpwnam(dnrd_user))) {
66 			if ((daemonuid = pwent->pw_uid) == 0)
67 				log_err_exit(-1, "Please specify a non-root user (uid != 0) with -u");
68 			if ((daemongid = pwent->pw_gid) == 0)
69 				log_err_exit(-1, "Please specify a non-root user group (gid != 0)");
70 		} else {
71 			perror("getpwnam");
72 			log_err_exit(-1, "Could not become \"%s\" user. Please create the user "
73 									 "account or specify a valid user with  the -u option.",
74 									 dnrd_user);
75 		}
76 	}
77 
78 }
79 #endif /* __CYGWIN__ */
80 
81 
82 
83 /***************************************************************************/
dnrd_root_sanity_check(void)84 static void dnrd_root_sanity_check(void) {
85 	DIR               *dirp;
86 	int                rslt;
87 	struct dirent     *direntry;
88 	struct stat        st;
89 
90 	if (chdir(dnrd_root))
91 		log_err_exit(-1, "Could not chdir to %s, %s", dnrd_root, strerror(errno));
92 
93 	dirp = opendir(dnrd_root);
94 	if (!dirp)
95 		log_err_exit(-1, "The directory %s must be created before "
96 								 "dnrd will run", dnrd_root);
97 
98 	rslt = stat(dnrd_root, &st);
99 #ifdef __CYGWIN__
100 	if (is_writeable (&st))
101 		log_err_exit(-1, "The %s directory must not be writeable", dnrd_root);
102 #else
103 	if (st.st_uid != 0)
104 		log_err_exit(-1, "The %s directory must be owned by root", dnrd_root);
105 	if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
106 		log_err_exit(-1, "The %s directory should only be user writable",
107 								 dnrd_root);
108 #endif
109 
110 	while ((direntry = readdir(dirp)) != NULL) {
111 		if (!strcmp(direntry->d_name, ".") ||
112 				!strcmp(direntry->d_name, "..")) {
113 	    continue;
114 		}
115 
116 		rslt = stat(direntry->d_name, &st);
117 
118 		if (rslt) continue;
119 		if (S_ISDIR(st.st_mode))
120 	    log_err_exit(-1, "The %s directory must not contain subdirectories",
121 									 dnrd_root);
122 		if ((st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH|S_IWGRP|S_IWOTH)) != 0)
123 	    log_err_exit(-1, "A file in %s has either execute "
124 									 "permissions or non-user write permission.  Please do a "
125 									 "\"chmod a-x,go-w\" on all files in this directory",
126 									 dnrd_root);
127 #ifdef __CYGWIN__
128 		if (is_writeable (&st))
129 	    log_err_exit(-1, "No files in %s may be writeable", dnrd_root);
130 #else
131 		if (st.st_uid != 0)
132 	    log_err_exit(-1, "All files in %s must be owned by root", dnrd_root);
133 #endif
134 	}
135 	closedir(dirp);
136 }
137 
138 
139 
140 /***************************************************************************/
init_socket(void)141 void init_socket(void) {
142     struct servent    *servent;   /* Let's be good and find the port numbers
143 				     the right way */
144 
145     /*
146      * Pretend we don't know that we want port 53
147      */
148     servent = getservbyname("domain", "udp");
149     if (servent != getservbyname("domain", "tcp"))
150 			log_err_exit(-1, "domain ports for udp & tcp differ. "
151 									 "Check /etc/services");
152     recv_addr.sin_port = servent ? servent->s_port : htons(53);
153 
154     /*
155      * Setup our DNS query reception socket.
156      */
157     if ((isock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
158 			log_err_exit(-1, "isock: Couldn't open socket");
159 		}
160     else {
161 			int opt = 1;
162 			setsockopt(isock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
163     }
164 
165     if (bind(isock, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) < 0)
166 			log_err_exit(-1, "isock: Couldn't bind local address");
167 
168     /*
169      * Setup our DNS tcp proxy socket.
170      */
171 #ifdef ENABLE_TCP
172     if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
173 			log_err_exit(-1, "tcpsock: Couldn't open socket");
174     }
175     else {
176 	int opt = 1;
177 	setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
178     }
179     if (bind(tcpsock, (struct sockaddr *)&recv_addr, sizeof(recv_addr)) < 0)
180 			log_err_exit(-1, "tcpsock: Couldn't bind local address");
181     if (listen(tcpsock, 5) != 0)
182 			log_err_exit(-1, "tcpsock: Can't listen");
183 #endif
184 }
185 
186 
187 /***************************************************************************
188  * main() - startup the program.
189  *
190  * In:      argc - number of command-line arguments.
191  *          argv - string array containing command-line arguments.
192  *
193  * Returns: 0 on exit, -1 on error.
194  *
195  * Abstract: We set up the signal handler, parse arguments,
196  *           turn into a daemon, write our pid to /var/run/dnrd.pid,
197  *           setup our sockets, and then parse packets until we die.
198  ****************************************************************************/
main(int argc,char * argv[])199 int main(int argc, char *argv[])
200 {
201   /*    int                i;*/
202 #ifdef ENABLE_PIDFILE
203 	FILE              *filep;
204 #endif
205 	domnode_t *p;
206 	srvnode_t *s;
207 	char *tmpstr;
208 
209 	/*
210 	 * Initialization in common.h of recv_addr is broken, causing at
211 	 * least the '-a' switch not to work.  Instead of assuming
212 	 * positions of fields in the struct across platforms I thought it
213 	 * safer to do a standard initialization in main().
214 	 */
215 	memset(&recv_addr, 0, sizeof(recv_addr));
216 	recv_addr.sin_family = AF_INET;
217 
218 	openlog(progname, LOG_PID, LOG_DAEMON);
219 
220 	/* create the domain list */
221 	domain_list = alloc_domnode();
222 
223 	/* get the dnrd_root from environment */
224 	if ((tmpstr = getenv("DNRD_ROOT")))
225 		strncpy(dnrd_root, tmpstr, sizeof(dnrd_root));
226 
227 	/*
228 	 * Parse the command line.
229      */
230 	parse_args(argc, argv);
231 
232 	/* we change to the dnrd-root dir */
233 	chdir(dnrd_root);
234 
235 #ifdef ENABLE_PIDFILE
236 	/* Kill any currently running copies of the program. */
237 	kill_current();
238 	/*
239 	 * Write our pid to the appropriate file.
240 	 * Just open the file here.  We'll write to it after we fork.
241 	 */
242 	if ( !(filep = fopen(pid_file, "w")) )
243 		log_err_exit(-1, "can't write to %s.  "
244 								 "Check that dnrd was started by root.", pid_file);
245 #endif
246 
247 	/*
248 	 * Setup the thread synchronization semaphore
249 	 */
250 	if (sem_init(&dnrd_sem, 0, 1) == -1)
251 		log_err_exit(-1, "Couldn't initialize semaphore");
252 
253 	init_socket();
254 
255 	/* Initialise our cache */
256 	cache_init();
257 
258 	/* init the qid pool */
259 	qid_init_pool();
260 
261 #ifndef EXCLUDE_MASTER
262 	/* Initialise out master DNS */
263 	master_init();
264 #endif
265 
266 	/* init query list */
267 	query_init();
268 
269 	/* init dns validation table */
270 	init_dns();
271 
272 #ifndef __CYGWIN__
273 	/* we need to find the uid and gid from /etc/passwd before we chroot. */
274 	init_dnrd_uid();
275 #endif /* __CYGWIN__ */
276 
277 	/*
278 	 * Change our root and current working directories to
279 	 * /usr/local/etc/dnrd.  Also, so some sanity checking on that
280 	 * directory first.
281 	 */
282 	dnrd_root_sanity_check();
283 	if (chroot(dnrd_root)) {
284 		log_err_exit(-1, "couldn't chroot to %s, %s",	dnrd_root,
285 								 strerror(errno));
286 	} else log_debug(1, "chrooting to %s", dnrd_root);
287 
288 	/*
289 	 * Change uid/gid to something other than root.
290 	 */
291 
292 	/* drop supplementary groups */
293 	if (setgroups(0, NULL) < 0)
294 		log_err_exit(-1, "can't drop supplementary groups");
295 
296 #ifndef __CYGWIN__ /** { **/
297 	/*
298 	 * Switch uid/gid to something safer than root.
299 	 */
300 	log_debug(1, "setting uid to %i", daemonuid);
301 	if (setuid(daemonuid) < 0)
302 		log_err_exit(-1, "Could not switch to uid %i", daemonuid);
303 #endif /** } __CYGWIN__ **/
304 
305 	/*
306 	 * Setup our DNS query forwarding socket.
307 	 */
308 	p=domain_list;
309 	do {
310 		s=p->srvlist;
311 		while ((s=s->next) != p->srvlist) {
312 			s->addr.sin_family = AF_INET;
313 			s->addr.sin_port   = htons(53);
314 		}
315 		/* set the first server as current */
316 		p->current = p->srvlist->next;
317 	} while ((p=p->next) != domain_list);
318 
319 #ifndef __CYGWIN__ /** { **/
320 	/*
321 	 * Now it's time to become a daemon.
322 	 */
323 	if ((!opt_debug) || (!gotterminal)) {
324 		pid_t pid = fork();
325 		if (pid < 0) log_err_exit(-1, "%s: Couldn't fork\n", progname);
326 		if (pid != 0) exit(0);
327 		gotterminal = 0;
328 		setsid();
329 		chdir("/");
330 		umask(077);
331 		fclose(stdin);
332 		fclose(stdout);
333 		fclose(stderr);
334 	}
335 #endif /** } __CYGWIN__ */
336 
337 #ifdef ENABLE_PIDFILE
338 	/*
339 	 * Write our pid to the appropriate file.
340 	 * Now we actually write to it and close it.
341 	 */
342 	fprintf(filep, "%i\n", (int)getpid());
343 	fclose(filep);
344 #endif
345 
346 	/*
347 	 * Run forever.
348 	 */
349 	run();
350 	exit(0); /* to make compiler happy */
351 }
352 
353 /***************************************************************************/
is_writeable(const struct stat * st)354 static int is_writeable (const struct stat* st)
355 {
356     if (st->st_uid == getuid ())
357         return 1;
358 
359     if ((user_groups_contain (st->st_gid)) &&
360         ((st->st_mode & S_IWGRP) != 0))
361 	return 1;
362 
363     if ((st->st_mode & S_IWOTH) != 0)
364 	return 1;
365 
366     return 0;
367 }
368 
369 /***************************************************************************/
user_groups_contain(gid_t file_gid)370 static int user_groups_contain (gid_t file_gid)
371 {
372     int igroup;
373     gid_t user_gids[NGROUPS_MAX];
374     int ngroups = getgroups (NGROUPS_MAX, &user_gids[0]);
375 
376     if (ngroups < 0)
377 			log_err_exit(-1, "Couldn't get user's group list");
378 
379     for (igroup = 0 ; igroup < ngroups ; ++igroup)
380     {
381     	if (user_gids[igroup] == file_gid)
382 	    return 1;
383     }
384 
385     return 0;
386 }
387