/src/systemd/src/basic/socket-label.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | | |
3 | | #include <errno.h> |
4 | | #include <netinet/in.h> |
5 | | #include <stdbool.h> |
6 | | #include <stddef.h> |
7 | | #include <string.h> |
8 | | #include <sys/socket.h> |
9 | | #include <sys/un.h> |
10 | | #include <unistd.h> |
11 | | |
12 | | #include "alloc-util.h" |
13 | | #include "fd-util.h" |
14 | | #include "fs-util.h" |
15 | | #include "log.h" |
16 | | #include "macro.h" |
17 | | #include "missing.h" |
18 | | #include "mkdir.h" |
19 | | #include "selinux-util.h" |
20 | | #include "socket-util.h" |
21 | | #include "umask-util.h" |
22 | | |
23 | | int socket_address_listen( |
24 | | const SocketAddress *a, |
25 | | int flags, |
26 | | int backlog, |
27 | | SocketAddressBindIPv6Only only, |
28 | | const char *bind_to_device, |
29 | | bool reuse_port, |
30 | | bool free_bind, |
31 | | bool transparent, |
32 | | mode_t directory_mode, |
33 | | mode_t socket_mode, |
34 | 0 | const char *label) { |
35 | 0 |
|
36 | 0 | _cleanup_close_ int fd = -1; |
37 | 0 | const char *p; |
38 | 0 | int r; |
39 | 0 |
|
40 | 0 | assert(a); |
41 | 0 |
|
42 | 0 | r = socket_address_verify(a, true); |
43 | 0 | if (r < 0) |
44 | 0 | return r; |
45 | 0 | |
46 | 0 | if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported()) |
47 | 0 | return -EAFNOSUPPORT; |
48 | 0 | |
49 | 0 | if (label) { |
50 | 0 | r = mac_selinux_create_socket_prepare(label); |
51 | 0 | if (r < 0) |
52 | 0 | return r; |
53 | 0 | } |
54 | 0 | |
55 | 0 | fd = socket(socket_address_family(a), a->type | flags, a->protocol); |
56 | 0 | r = fd < 0 ? -errno : 0; |
57 | 0 |
|
58 | 0 | if (label) |
59 | 0 | mac_selinux_create_socket_clear(); |
60 | 0 |
|
61 | 0 | if (r < 0) |
62 | 0 | return r; |
63 | 0 | |
64 | 0 | if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { |
65 | 0 | r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, only == SOCKET_ADDRESS_IPV6_ONLY); |
66 | 0 | if (r < 0) |
67 | 0 | return r; |
68 | 0 | } |
69 | 0 | |
70 | 0 | if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) { |
71 | 0 | if (bind_to_device) { |
72 | 0 | r = socket_bind_to_ifname(fd, bind_to_device); |
73 | 0 | if (r < 0) |
74 | 0 | return r; |
75 | 0 | } |
76 | 0 | |
77 | 0 | if (reuse_port) { |
78 | 0 | r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true); |
79 | 0 | if (r < 0) |
80 | 0 | log_warning_errno(r, "SO_REUSEPORT failed: %m"); |
81 | 0 | } |
82 | 0 |
|
83 | 0 | if (free_bind) { |
84 | 0 | r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true); |
85 | 0 | if (r < 0) |
86 | 0 | log_warning_errno(r, "IP_FREEBIND failed: %m"); |
87 | 0 | } |
88 | 0 |
|
89 | 0 | if (transparent) { |
90 | 0 | r = setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, true); |
91 | 0 | if (r < 0) |
92 | 0 | log_warning_errno(r, "IP_TRANSPARENT failed: %m"); |
93 | 0 | } |
94 | 0 | } |
95 | 0 |
|
96 | 0 | r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true); |
97 | 0 | if (r < 0) |
98 | 0 | return r; |
99 | 0 | |
100 | 0 | p = socket_address_get_path(a); |
101 | 0 | if (p) { |
102 | 0 | /* Create parents */ |
103 | 0 | (void) mkdir_parents_label(p, directory_mode); |
104 | 0 |
|
105 | 0 | /* Enforce the right access mode for the socket */ |
106 | 0 | RUN_WITH_UMASK(~socket_mode) { |
107 | 0 | r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); |
108 | 0 | if (r == -EADDRINUSE) { |
109 | 0 | /* Unlink and try again */ |
110 | 0 |
|
111 | 0 | if (unlink(p) < 0) |
112 | 0 | return r; /* didn't work, return original error */ |
113 | 0 | |
114 | 0 | r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); |
115 | 0 | } |
116 | 0 | if (r < 0) |
117 | 0 | return r; |
118 | 0 | } |
119 | 0 | } else { |
120 | 0 | if (bind(fd, &a->sockaddr.sa, a->size) < 0) |
121 | 0 | return -errno; |
122 | 0 | } |
123 | 0 | |
124 | 0 | if (socket_address_can_accept(a)) |
125 | 0 | if (listen(fd, backlog) < 0) |
126 | 0 | return -errno; |
127 | 0 | |
128 | 0 | /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable |
129 | 0 | * gets notified */ |
130 | 0 | if (p) |
131 | 0 | (void) touch(p); |
132 | 0 |
|
133 | 0 | r = fd; |
134 | 0 | fd = -1; |
135 | 0 |
|
136 | 0 | return r; |
137 | 0 | } |
138 | | |
139 | 0 | int make_socket_fd(int log_level, const char* address, int type, int flags) { |
140 | 0 | SocketAddress a; |
141 | 0 | int fd, r; |
142 | 0 |
|
143 | 0 | r = socket_address_parse(&a, address); |
144 | 0 | if (r < 0) |
145 | 0 | return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); |
146 | 0 | |
147 | 0 | a.type = type; |
148 | 0 |
|
149 | 0 | fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, |
150 | 0 | NULL, false, false, false, 0755, 0644, NULL); |
151 | 0 | if (fd < 0 || log_get_max_level() >= log_level) { |
152 | 0 | _cleanup_free_ char *p = NULL; |
153 | 0 |
|
154 | 0 | r = socket_address_print(&a, &p); |
155 | 0 | if (r < 0) |
156 | 0 | return log_error_errno(r, "socket_address_print(): %m"); |
157 | 0 | |
158 | 0 | if (fd < 0) |
159 | 0 | log_error_errno(fd, "Failed to listen on %s: %m", p); |
160 | 0 | else |
161 | 0 | log_full(log_level, "Listening on %s", p); |
162 | 0 | } |
163 | 0 |
|
164 | 0 | return fd; |
165 | 0 | } |