1 /*
2 ** Helper program to bind a socket to a low-numbered port.
3 **
4 ** Written by Russ Allbery <eagle@eyrie.org>
5 */
6
7 #include "portable/system.h"
8
9 #include "portable/socket.h"
10 #include <errno.h>
11 #ifdef HAVE_STREAMS_SENDFD
12 # include <stropts.h>
13 #endif
14 #include <syslog.h>
15
16 #include "inn/libinn.h"
17 #include "inn/messages.h"
18 #include "inn/newsuser.h"
19 #include "inn/vector.h"
20
21 /* Macros to set the len attribute of sockaddrs. */
22 #if HAVE_STRUCT_SOCKADDR_SA_LEN
23 # define sin_set_length(s) ((s)->sin_len = sizeof(struct sockaddr_in))
24 # define sin6_set_length(s) ((s)->sin6_len = sizeof(struct sockaddr_in6))
25 #else
26 # define sin_set_length(s) /* empty */
27 # define sin6_set_length(s) /* empty */
28 #endif
29
30 /* INND_PORT is the additional port specified at configure time to which the
31 news user should be allowed to bind. If it's not set, set it to 119 (which
32 will cause it to have no effect). I hate #ifdef in code, can you tell? */
33 #ifndef INND_PORT
34 # define INND_PORT 119
35 #endif
36
37 /* Holds the information about a network socket to bind. */
38 struct binding {
39 int fd;
40 int family;
41 char *address;
42 unsigned short port;
43 };
44
45
46 /*
47 ** Convert a string to a number with error checking, returning true if the
48 ** number was parsed correctly and false otherwise. Stores the converted
49 ** number in the second argument. Equivalent to calling strtol, but with the
50 ** base always fixed at 10, with checking of errno, ensuring that all of the
51 ** string is consumed, and checking that the resulting number is positive.
52 */
53 static bool
convert_string(const char * string,long * result)54 convert_string(const char *string, long *result)
55 {
56 char *end;
57
58 if (*string == '\0')
59 return false;
60 errno = 0;
61 *result = strtol(string, &end, 10);
62 if (errno != 0 || *end != '\0' || *result < 0)
63 return false;
64 return true;
65 }
66
67
68 /*
69 ** Parse a command-line argument into a struct binding. The command line
70 ** argument is four comma-separated values: the file descriptor, the family,
71 ** the listening address, and the port number. The caller is responsible for
72 ** freeing the address attribute of the supplied binding struct, although if
73 ** a binding struct is passed in for use and has a non-NULL address, it will
74 ** be freed first.
75 */
76 static void
parse_argument(const char * string,struct binding * binding)77 parse_argument(const char *string, struct binding *binding)
78 {
79 struct vector *spec;
80 long value;
81
82 /* Do the initial parse and allocate our data structures. */
83 spec = vector_split(string, ',', NULL);
84 if (spec->count != 4)
85 die("invalid command-line argument %s", string);
86
87 /* Get the file descriptor, address family, and port. */
88 if (!convert_string(spec->strings[0], &value))
89 die("invalid file descriptor %s in %s", spec->strings[0], string);
90 binding->fd = value;
91 if (!convert_string(spec->strings[1], &value))
92 die("invalid protocol family %s in %s", spec->strings[1], string);
93 binding->family = value;
94 if (binding->address != NULL)
95 free(binding->address);
96 binding->address = xstrdup(spec->strings[2]);
97 if (!convert_string(spec->strings[3], &value))
98 die("invalid port number %s in %s", spec->strings[3], string);
99 if (value == 0)
100 die("port may not be zero in %s", string);
101 binding->port = value;
102
103 /* Done. Clean up. */
104 vector_free(spec);
105 }
106
107
108 /*
109 ** Bind an IPv4 address, given the file descriptor, string giving the
110 ** address, and the port. The fourth argument is the full binding
111 ** specification for error reporting. Returns true on success, false if
112 ** binding failed due to permission denied. Die on any other failure.
113 */
114 static bool
bind_ipv4(int fd,const char * address,unsigned short port,const char * spec)115 bind_ipv4(int fd, const char *address, unsigned short port, const char *spec)
116 {
117 struct sockaddr_in server;
118 struct in_addr addr;
119
120 memset(&server, '\0', sizeof(server));
121 server.sin_family = AF_INET;
122 server.sin_port = htons(port);
123 if (!inet_aton(address, &addr))
124 die("invalid IPv4 address %s in %s", address, spec);
125 server.sin_addr = addr;
126 sin_set_length(&server);
127 if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
128 if (errno == EACCES)
129 return false;
130 else
131 sysdie("cannot bind socket for %s", spec);
132 }
133 return true;
134 }
135
136
137 /*
138 ** Bind an IPv6 address, given the file descriptor, string giving the
139 ** address, and the port. The fourth argument is the full binding
140 ** specification for error reporting. Returns true on success, false if
141 ** binding failed due to permission denied. Die on any other failure.
142 */
143 #ifdef HAVE_INET6
144 static bool
bind_ipv6(int fd,const char * address,unsigned short port,const char * spec)145 bind_ipv6(int fd, const char *address, unsigned short port, const char *spec)
146 {
147 struct sockaddr_in6 server;
148 struct in6_addr addr;
149
150 memset(&server, '\0', sizeof(server));
151 server.sin6_family = AF_INET6;
152 server.sin6_port = htons(port);
153 if (inet_pton(AF_INET6, address, &addr) < 1)
154 die("invalid IPv6 address %s in %s", address, spec);
155 server.sin6_addr = addr;
156 sin6_set_length(&server);
157 if (bind(fd, (struct sockaddr *) &server, sizeof(server)) < 0) {
158 if (errno == EACCES)
159 return false;
160 else
161 sysdie("cannot bind socket for %s", spec);
162 }
163 return true;
164 }
165 #endif /* HAVE_INET6 */
166
167
168 /*
169 ** Given a struct binding, bind that file descriptor. Also takes the
170 ** command-line argument for error reporting. Returns true on success, false
171 ** if binding failed due to permission denied. Die on any other failure.
172 */
173 static bool
bind_address(struct binding * binding,const char * spec)174 bind_address(struct binding *binding, const char *spec)
175 {
176 int fd = binding->fd;
177 unsigned short port = binding->port;
178 int type;
179 socklen_t length;
180
181 /* Make sure that we're allowed to bind to that port. */
182 if (port < 1024 && port != 119 && port != 433 && port != 563
183 && port != INND_PORT)
184 die("cannot bind to restricted port %hu in %s", port, spec);
185
186 /* Sanity check on the socket. */
187 length = sizeof(type);
188 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &length) < 0)
189 sysdie("cannot get socket options for file descriptor %d", fd);
190 if (type != SOCK_STREAM)
191 die("invalid file descriptor %d: not SOCK_STREAM", fd);
192
193 /* Based on the address family, parse either an IPv4 or IPv6 address. */
194 if (binding->family == AF_INET)
195 return bind_ipv4(fd, binding->address, port, spec);
196 #ifdef HAVE_INET6
197 else if (binding->family == AF_INET6)
198 return bind_ipv6(fd, binding->address, port, spec);
199 #endif
200 else
201 die("unknown protocol family %d in %s", binding->family, spec);
202 }
203
204
205 /*
206 ** Given a struct binding, create a socket for it and fill in the file
207 ** descriptor. Also takes the command-line argument for error reporting.
208 ** Dies on any failure.
209 */
210 static void
create_socket(struct binding * binding,const char * spec)211 create_socket(struct binding *binding, const char *spec)
212 {
213 int fd;
214 #if defined(SO_REUSEADDR) || defined(IPV6_V6ONLY)
215 int flag;
216 #endif
217
218 /* Create the socket. */
219 if (binding->family == AF_INET)
220 fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
221 #ifdef HAVE_INET6
222 else if (binding->family == AF_INET6)
223 fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_IP);
224 #endif
225 else
226 die("unknown protocol family %d in %s", binding->family, spec);
227 if (fd < -1)
228 sysdie("cannot create socket for %s", spec);
229
230 /* Mark it reusable if possible. */
231 #ifdef SO_REUSEADDR
232 flag = 1;
233 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0)
234 sysdie("cannot mark socket reusable for %s", spec);
235 #endif
236
237 /* Mark it IPv6 only if possible. */
238 #ifdef IPV6_V6ONLY
239 flag = 1;
240 if (binding->family == AF_INET6
241 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
242 sysdie("cannot mark socket IPv6 only for %s", spec);
243 #endif
244
245 /* Fill in the struct. */
246 binding->fd = fd;
247 }
248
249
250 /*
251 ** Given a file descriptor, attempt to pass it via stdout. Die on any error.
252 ** Currently, file descriptor passing is only implemented for systems that
253 ** use STREAMS.
254 */
255 #ifdef HAVE_STREAMS_SENDFD
256 static void
send_fd(int fd)257 send_fd(int fd)
258 {
259 if (isastream(STDOUT_FILENO) != 1)
260 die("cannot pass file descriptor: stdout is not a stream");
261 if (ioctl(STDOUT_FILENO, I_SENDFD, fd) < 0)
262 sysdie("cannot pass file descriptor");
263 }
264 #else /* !HAVE_STREAMS_SENDFD */
265 __attribute__((__noreturn__)) static void
send_fd(int fd UNUSED)266 send_fd(int fd UNUSED)
267 {
268 die("cannot pass file descriptor: STREAMS not supported");
269 }
270 #endif /* !HAVE_STREAMS_SENDFD */
271
272
273 int
main(int argc,char * argv[])274 main(int argc, char *argv[])
275 {
276 uid_t real_uid, uid;
277 int i;
278 bool done;
279 struct binding binding = {0, 0, NULL, 0};
280 bool force_sendfd = false;
281
282 /* Set up the error handlers. Errors go to stderr and to syslog with a
283 priority of LOG_CRIT. This priority level is too high, but it's chosen
284 to match innd. */
285 openlog("innbind", LOG_CONS, LOG_INN_PROG);
286 message_handlers_die(2, message_log_stderr, message_log_syslog_crit);
287 message_program_name = "innbind";
288
289 /* If we're running privileged (effective and real UIDs are different),
290 convert runasuser to a UID and exit if run by another user. Don't do
291 this if we're not running privileged to make installations that don't
292 need privileged ports easier and to make testing easier. */
293 real_uid = getuid();
294 if (real_uid != geteuid()) {
295 get_news_uid_gid(&uid, false, true);
296 if (real_uid != uid) {
297 die("must be run by runasuser (%lu), not %lu", (unsigned long) uid,
298 (unsigned long) real_uid);
299 }
300 }
301
302 /* If the first argument is -p, force creation of the socket and file
303 descriptor passing rather than even attempting to bind the socket
304 first. */
305 if (argc > 1 && strcmp(argv[1], "-p") == 0) {
306 force_sendfd = true;
307 argc--;
308 argv++;
309 }
310 if (argc < 2)
311 die("no addresses specified");
312
313 /* Walk the argument list and try to bind each argument. For each
314 successful bind, print "ok\n" to stdout. For each bind that fails with
315 permission denied, print "no\n" to stdout and then immediately attempt
316 to create a new file descriptor and pass it back over stdout. */
317 for (i = 1; i < argc; i++) {
318 parse_argument(argv[i], &binding);
319 done = false;
320 if (!force_sendfd)
321 done = bind_address(&binding, argv[i]);
322 if (done) {
323 if (write(STDOUT_FILENO, "ok\n", 3) < 3)
324 sysdie("cannot write status");
325 } else {
326 if (write(STDOUT_FILENO, "no\n", 3) < 3)
327 sysdie("cannot write status");
328 create_socket(&binding, argv[i]);
329 if (!bind_address(&binding, argv[i]))
330 sysdie("cannot bind socket for %s", argv[i]);
331 send_fd(binding.fd);
332 }
333 }
334 exit(0);
335 }
336