]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/socket-label.c
Merge pull request #10892 from mbiebl/revert-systemctl-runtime-unmask-breakage
[thirdparty/systemd.git] / src / basic / socket-label.c
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 const char *label) {
35
36 _cleanup_close_ int fd = -1;
37 const char *p;
38 int r;
39
40 assert(a);
41
42 r = socket_address_verify(a, true);
43 if (r < 0)
44 return r;
45
46 if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
47 return -EAFNOSUPPORT;
48
49 if (label) {
50 r = mac_selinux_create_socket_prepare(label);
51 if (r < 0)
52 return r;
53 }
54
55 fd = socket(socket_address_family(a), a->type | flags, a->protocol);
56 r = fd < 0 ? -errno : 0;
57
58 if (label)
59 mac_selinux_create_socket_clear();
60
61 if (r < 0)
62 return r;
63
64 if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
65 r = setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, only == SOCKET_ADDRESS_IPV6_ONLY);
66 if (r < 0)
67 return r;
68 }
69
70 if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) {
71 if (bind_to_device)
72 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
73 return -errno;
74
75 if (reuse_port) {
76 r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true);
77 if (r < 0)
78 log_warning_errno(r, "SO_REUSEPORT failed: %m");
79 }
80
81 if (free_bind) {
82 r = setsockopt_int(fd, IPPROTO_IP, IP_FREEBIND, true);
83 if (r < 0)
84 log_warning_errno(r, "IP_FREEBIND failed: %m");
85 }
86
87 if (transparent) {
88 r = setsockopt_int(fd, IPPROTO_IP, IP_TRANSPARENT, true);
89 if (r < 0)
90 log_warning_errno(r, "IP_TRANSPARENT failed: %m");
91 }
92 }
93
94 r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, true);
95 if (r < 0)
96 return r;
97
98 p = socket_address_get_path(a);
99 if (p) {
100 /* Create parents */
101 (void) mkdir_parents_label(p, directory_mode);
102
103 /* Enforce the right access mode for the socket */
104 RUN_WITH_UMASK(~socket_mode) {
105 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
106 if (r == -EADDRINUSE) {
107 /* Unlink and try again */
108
109 if (unlink(p) < 0)
110 return r; /* didn't work, return original error */
111
112 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
113 }
114 if (r < 0)
115 return r;
116 }
117 } else {
118 if (bind(fd, &a->sockaddr.sa, a->size) < 0)
119 return -errno;
120 }
121
122 if (socket_address_can_accept(a))
123 if (listen(fd, backlog) < 0)
124 return -errno;
125
126 /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable
127 * gets notified */
128 if (p)
129 (void) touch(p);
130
131 r = fd;
132 fd = -1;
133
134 return r;
135 }
136
137 int make_socket_fd(int log_level, const char* address, int type, int flags) {
138 SocketAddress a;
139 int fd, r;
140
141 r = socket_address_parse(&a, address);
142 if (r < 0)
143 return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address);
144
145 a.type = type;
146
147 fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
148 NULL, false, false, false, 0755, 0644, NULL);
149 if (fd < 0 || log_get_max_level() >= log_level) {
150 _cleanup_free_ char *p = NULL;
151
152 r = socket_address_print(&a, &p);
153 if (r < 0)
154 return log_error_errno(r, "socket_address_print(): %m");
155
156 if (fd < 0)
157 log_error_errno(fd, "Failed to listen on %s: %m", p);
158 else
159 log_full(log_level, "Listening on %s", p);
160 }
161
162 return fd;
163 }