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