chiark / gitweb /
core: don't consider umask for SocketMode=
[elogind.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 <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <sys/stat.h>
26 #include <stddef.h>
27
28 #include "macro.h"
29 #include "util.h"
30 #include "mkdir.h"
31 #include "missing.h"
32 #include "selinux-util.h"
33 #include "socket-util.h"
34
35 int socket_address_listen(
36                 const SocketAddress *a,
37                 int flags,
38                 int backlog,
39                 SocketAddressBindIPv6Only only,
40                 const char *bind_to_device,
41                 bool free_bind,
42                 bool transparent,
43                 mode_t directory_mode,
44                 mode_t socket_mode,
45                 const char *label) {
46
47         _cleanup_close_ int fd = -1;
48         int r, one;
49
50         assert(a);
51
52         r = socket_address_verify(a);
53         if (r < 0)
54                 return r;
55
56         if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
57                 return -EAFNOSUPPORT;
58
59         if (label) {
60                 r = mac_selinux_create_socket_prepare(label);
61                 if (r < 0)
62                         return r;
63         }
64
65         fd = socket(socket_address_family(a), a->type | flags, a->protocol);
66         r = fd < 0 ? -errno : 0;
67
68         if (label)
69                 mac_selinux_create_socket_clear();
70
71         if (r < 0)
72                 return r;
73
74         if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
75                 int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
76
77                 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
78                         return -errno;
79         }
80
81         if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
82                 if (bind_to_device)
83                         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
84                                 return -errno;
85
86                 if (free_bind) {
87                         one = 1;
88                         if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
89                                 log_warning_errno(errno, "IP_FREEBIND failed: %m");
90                 }
91
92                 if (transparent) {
93                         one = 1;
94                         if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
95                                 log_warning_errno(errno, "IP_TRANSPARENT failed: %m");
96                 }
97         }
98
99         one = 1;
100         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
101                 return -errno;
102
103         if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
104                 mode_t old_mask;
105
106                 /* Create parents */
107                 mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
108
109                 /* Enforce the right access mode for the socket */
110                 old_mask = umask(~ socket_mode);
111
112                 r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
113
114                 if (r < 0 && errno == EADDRINUSE) {
115                         /* Unlink and try again */
116                         unlink(a->sockaddr.un.sun_path);
117                         r = bind(fd, &a->sockaddr.sa, a->size);
118                 }
119
120                 umask(old_mask);
121         } else
122                 r = bind(fd, &a->sockaddr.sa, a->size);
123
124         if (r < 0)
125                 return -errno;
126
127         if (socket_address_can_accept(a))
128                 if (listen(fd, backlog) < 0)
129                         return -errno;
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 flags) {
138         SocketAddress a;
139         int fd, r;
140
141         r = socket_address_parse(&a, address);
142         if (r < 0) {
143                 log_error("Failed to parse socket address \"%s\": %s",
144                           address, strerror(-r));
145                 return r;
146         }
147
148         fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT,
149                                    NULL, false, false, 0755, 0644, NULL);
150         if (fd < 0 || log_get_max_level() >= log_level) {
151                 _cleanup_free_ char *p = NULL;
152
153                 r = socket_address_print(&a, &p);
154                 if (r < 0)
155                         return log_error_errno(r, "socket_address_print(): %m");
156
157                 if (fd < 0)
158                         log_error_errno(fd, "Failed to listen on %s: %m", p);
159                 else
160                         log_full(log_level, "Listening on %s", p);
161         }
162
163         return fd;
164 }