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