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