1 /*
2  * Copyright (c) 2011-2014, Dustin Lundquist <dustin@null-ptr.net>
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 are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND 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 COPYRIGHT HOLDER OR CONTRIBUTORS BE
18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <pwd.h>
32 #include <sys/types.h>
33 #include <unistd.h>
34 #include <grp.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/resource.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <ev.h>
41 #include "binder.h"
42 #include "config.h"
43 #include "connection.h"
44 #include "listener.h"
45 #include "resolv.h"
46 #include "logger.h"
47 
48 
49 static void usage();
50 static void daemonize(void);
51 static void write_pidfile(const char *, pid_t);
52 static void set_limits(rlim_t);
53 static void drop_perms(const char* username, const char* groupname);
54 static void perror_exit(const char *);
55 static void signal_cb(struct ev_loop *, struct ev_signal *, int revents);
56 
57 
58 static const char *sniproxy_version = PACKAGE_VERSION;
59 static const char *default_username = "daemon";
60 static struct Config *config;
61 static struct ev_signal sighup_watcher;
62 static struct ev_signal sigusr1_watcher;
63 static struct ev_signal sigint_watcher;
64 static struct ev_signal sigterm_watcher;
65 
66 
67 int
main(int argc,char ** argv)68 main(int argc, char **argv) {
69     const char *config_file = "/etc/sniproxy.conf";
70     int background_flag = 1;
71     rlim_t max_nofiles = 65536;
72     int opt;
73 
74     while ((opt = getopt(argc, argv, "fc:n:V")) != -1) {
75         switch (opt) {
76             case 'c':
77                 config_file = optarg;
78                 break;
79             case 'f': /* foreground */
80                 background_flag = 0;
81                 break;
82             case 'n':
83                 max_nofiles = strtoul(optarg, NULL, 10);
84                 break;
85             case 'V':
86                 printf("sniproxy %s\n", sniproxy_version);
87                 return EXIT_SUCCESS;
88             default:
89                 usage();
90                 return EXIT_FAILURE;
91         }
92     }
93 
94     config = init_config(config_file, EV_DEFAULT);
95     if (config == NULL) {
96         fprintf(stderr, "Unable to load %s\n", config_file);
97         usage();
98         return EXIT_FAILURE;
99     }
100 
101     /* ignore SIGPIPE, or it will kill us */
102     signal(SIGPIPE, SIG_IGN);
103 
104     if (background_flag) {
105         if (config->pidfile != NULL)
106             remove(config->pidfile);
107 
108         daemonize();
109 
110 
111         if (config->pidfile != NULL)
112             write_pidfile(config->pidfile, getpid());
113     }
114 
115     start_binder();
116 
117     set_limits(max_nofiles);
118 
119     init_listeners(&config->listeners, &config->tables, EV_DEFAULT);
120 
121     /* Drop permissions only when we can */
122     drop_perms(config->user ? config->user : default_username, config->group);
123 
124     ev_signal_init(&sighup_watcher, signal_cb, SIGHUP);
125     ev_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1);
126     ev_signal_init(&sigint_watcher, signal_cb, SIGINT);
127     ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM);
128     ev_signal_start(EV_DEFAULT, &sighup_watcher);
129     ev_signal_start(EV_DEFAULT, &sigusr1_watcher);
130     ev_signal_start(EV_DEFAULT, &sigint_watcher);
131     ev_signal_start(EV_DEFAULT, &sigterm_watcher);
132 
133     resolv_init(EV_DEFAULT, config->resolver.nameservers,
134             config->resolver.search, config->resolver.mode);
135 
136     init_connections();
137 
138     ev_run(EV_DEFAULT, 0);
139 
140     free_connections(EV_DEFAULT);
141     resolv_shutdown(EV_DEFAULT);
142 
143     free_config(config, EV_DEFAULT);
144 
145     stop_binder();
146 
147     return 0;
148 }
149 
150 static void
daemonize(void)151 daemonize(void) {
152 #ifdef HAVE_DAEMON
153     if (daemon(0,0) < 0)
154         perror_exit("daemon()");
155 #else
156     pid_t pid;
157 
158     /* daemon(0,0) part */
159     pid = fork();
160     if (pid < 0)
161         perror_exit("fork()");
162     else if (pid != 0)
163         exit(EXIT_SUCCESS);
164 
165     if (setsid() < 0)
166         perror_exit("setsid()");
167 
168     if (chdir("/") < 0)
169         perror_exit("chdir()");
170 
171     if (freopen("/dev/null", "r", stdin) == NULL)
172         perror_exit("freopen(stdin)");
173 
174     if (freopen("/dev/null", "a", stdout) == NULL)
175         perror_exit("freopen(stdout)");
176 
177     if (freopen("/dev/null", "a", stderr) == NULL)
178         perror_exit("freopen(stderr)");
179 
180     pid = fork();
181     if (pid < 0)
182         perror_exit("fork()");
183     else if (pid != 0)
184         exit(EXIT_SUCCESS);
185 #endif
186 
187     /* local part */
188     umask(022);
189     signal(SIGHUP, SIG_IGN);
190 
191     ev_default_fork();
192 
193     return;
194 }
195 
196 /**
197  * Raise file handle limit to reasonable level
198  * At some point we should make this a config parameter
199  */
200 static void
set_limits(rlim_t max_nofiles)201 set_limits(rlim_t max_nofiles) {
202     struct rlimit fd_limit = {
203         .rlim_cur = max_nofiles,
204         .rlim_max = max_nofiles,
205     };
206 
207     int result = setrlimit(RLIMIT_NOFILE, &fd_limit);
208     if (result < 0)
209         warn("Failed to set file handle limit: %s", strerror(errno));
210 }
211 
212 static void
drop_perms(const char * username,const char * groupname)213 drop_perms(const char *username, const char *groupname) {
214     /* check if we are already an unprivileged user */
215     if (getuid() != 0)
216         return;
217 
218     errno = 0;
219     struct passwd *user = getpwnam(username);
220     if (errno)
221         fatal("getpwnam(): %s", strerror(errno));
222     else if (user == NULL)
223         fatal("getpwnam(): user %s does not exist", username);
224 
225     gid_t gid = user->pw_gid;
226 
227     if (groupname != NULL) {
228       errno = 0;
229       struct group *group = getgrnam(groupname);
230       if (errno)
231         fatal("getgrnam(): %s", strerror(errno));
232       else if (group == NULL)
233         fatal("getgrnam(): group %s does not exist", groupname);
234 
235       gid = group->gr_gid;
236     }
237 
238     /* drop any supplementary groups */
239     if (setgroups(1, &gid) < 0)
240         fatal("setgroups(): %s", strerror(errno));
241 
242     /* set the main gid */
243     if (setgid(gid) < 0)
244         fatal("setgid(): %s", strerror(errno));
245 
246     if (setuid(user->pw_uid) < 0)
247         fatal("setuid(): %s", strerror(errno));
248 }
249 
250 static void
perror_exit(const char * msg)251 perror_exit(const char *msg) {
252     perror(msg);
253     exit(EXIT_FAILURE);
254 }
255 
256 static void
usage()257 usage() {
258     fprintf(stderr, "Usage: sniproxy [-c <config>] [-f] [-n <max file descriptor limit>] [-V]\n");
259 }
260 
261 static void
write_pidfile(const char * path,pid_t pid)262 write_pidfile(const char *path, pid_t pid) {
263     FILE *fp = fopen(path, "w");
264     if (fp == NULL) {
265         perror("fopen");
266         return;
267     }
268 
269     fprintf(fp, "%d\n", pid);
270 
271     fclose(fp);
272 }
273 
274 static void
signal_cb(struct ev_loop * loop,struct ev_signal * w,int revents)275 signal_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
276     if (revents & EV_SIGNAL) {
277         switch (w->signum) {
278             case SIGHUP:
279                 reopen_loggers();
280                 reload_config(config, loop);
281                 break;
282             case SIGUSR1:
283                 print_connections();
284                 break;
285             case SIGINT:
286             case SIGTERM:
287                 ev_unloop(loop, EVUNLOOP_ALL);
288         }
289     }
290 }
291