]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/socket-label.c
815b90fdfbd9dea59e80dc44117ad99659b523a2
[thirdparty/systemd.git] / src / shared / socket-label.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <arpa/inet.h>
28 #include <stdio.h>
29 #include <net/if.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stddef.h>
33 #include <sys/ioctl.h>
34
35 #include "macro.h"
36 #include "util.h"
37 #include "mkdir.h"
38 #include "socket-util.h"
39 #include "missing.h"
40 #include "label.h"
41
42 int socket_address_listen(
43 const SocketAddress *a,
44 int flags,
45 int backlog,
46 SocketAddressBindIPv6Only only,
47 const char *bind_to_device,
48 bool free_bind,
49 bool transparent,
50 mode_t directory_mode,
51 mode_t socket_mode,
52 const char *label) {
53
54 _cleanup_close_ int fd = -1;
55 int r, one;
56
57 assert(a);
58
59 r = socket_address_verify(a);
60 if (r < 0)
61 return r;
62
63 if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
64 return -EAFNOSUPPORT;
65
66 if (label) {
67 r = mac_selinux_create_socket_prepare(label);
68 if (r < 0)
69 return r;
70 }
71
72 fd = socket(socket_address_family(a), a->type | flags, a->protocol);
73 r = fd < 0 ? -errno : 0;
74
75 if (label)
76 mac_selinux_create_socket_clear();
77
78 if (r < 0)
79 return r;
80
81 if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
82 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
83
84 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
85 return -errno;
86 }
87
88 if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
89 if (bind_to_device)
90 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
91 return -errno;
92
93 if (free_bind) {
94 one = 1;
95 if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
96 log_warning("IP_FREEBIND failed: %m");
97 }
98
99 if (transparent) {
100 one = 1;
101 if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
102 log_warning("IP_TRANSPARENT failed: %m");
103 }
104 }
105
106 one = 1;
107 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
108 return -errno;
109
110 if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
111 mode_t old_mask;
112
113 /* Create parents */
114 mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
115
116 /* Enforce the right access mode for the socket */
117 old_mask = umask(~ socket_mode);
118
119 /* Include the original umask in our mask */
120 umask(~socket_mode | old_mask);
121
122 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
123
124 if (r < 0 && errno == EADDRINUSE) {
125 /* Unlink and try again */
126 unlink(a->sockaddr.un.sun_path);
127 r = bind(fd, &a->sockaddr.sa, a->size);
128 }
129
130 umask(old_mask);
131 } else
132 r = bind(fd, &a->sockaddr.sa, a->size);
133
134 if (r < 0)
135 return -errno;
136
137 if (socket_address_can_accept(a))
138 if (listen(fd, backlog) < 0)
139 return -errno;
140
141 r = fd;
142 fd = -1;
143
144 return r;
145 }
146
147 int make_socket_fd(int log_level, const char* address, int flags) {
148 SocketAddress a;
149 int fd, r;
150
151 r = socket_address_parse(&a, address);
152 if (r < 0) {
153 log_error("Failed to parse socket address \"%s\": %s",
154 address, strerror(-r));
155 return r;
156 }
157
158 fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
159 NULL, false, false, 0755, 0644, NULL);
160 if (fd < 0 || log_get_max_level() >= log_level) {
161 _cleanup_free_ char *p = NULL;
162
163 r = socket_address_print(&a, &p);
164 if (r < 0)
165 return log_error_errno(r, "socket_address_print(): %m");
166
167 if (fd < 0)
168 log_error_errno(fd, "Failed to listen on %s: %m", p);
169 else
170 log_full(log_level, "Listening on %s", p);
171 }
172
173 return fd;
174 }