chiark / gitweb /
sd-daemon: verify socket family, too
[elogind.git] / src / sd-daemon.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   Copyright 2010 Lennart Poettering
5
6   Permission is hereby granted, free of charge, to any person
7   obtaining a copy of this software and associated documentation files
8   (the "Software"), to deal in the Software without restriction,
9   including without limitation the rights to use, copy, modify, merge,
10   publish, distribute, sublicense, and/or sell copies of the Software,
11   and to permit persons to whom the Software is furnished to do so,
12   subject to the following conditions:
13
14   The above copyright notice and this permission notice shall be
15   included in all copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   SOFTWARE.
25 ***/
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <netinet/in.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <string.h>
36
37 #include "sd-daemon.h"
38
39 int sd_listen_fds(int unset_environment) {
40
41 #ifdef DISABLE_SYSTEMD
42         return 0;
43 #else
44         int r;
45         const char *e;
46         char *p = NULL;
47         unsigned long l;
48
49         if (!(e = getenv("LISTEN_PID"))) {
50                 r = 0;
51                 goto finish;
52         }
53
54         errno = 0;
55         l = strtoul(e, &p, 10);
56
57         if (errno != 0) {
58                 r = -errno;
59                 goto finish;
60         }
61
62         if (!p || *p || l <= 0) {
63                 r = -EINVAL;
64                 goto finish;
65         }
66
67         /* Is this for us? */
68         if (getpid() != (pid_t) l) {
69                 r = 0;
70                 goto finish;
71         }
72
73         if (!(e = getenv("LISTEN_FDS"))) {
74                 r = 0;
75                 goto finish;
76         }
77
78         errno = 0;
79         l = strtoul(e, &p, 10);
80
81         if (errno != 0) {
82                 r = -errno;
83                 goto finish;
84         }
85
86         if (!p || *p) {
87                 r = -EINVAL;
88                 goto finish;
89         }
90
91         r = (int) l;
92
93 finish:
94         if (unset_environment) {
95                 unsetenv("LISTEN_PID");
96                 unsetenv("LISTEN_FDS");
97         }
98
99         return r;
100 #endif
101 }
102
103 int sd_is_fifo(int fd, const char *path) {
104         struct stat st_fd;
105
106         if (fd < 0)
107                 return -EINVAL;
108
109         memset(&st_fd, 0, sizeof(st_fd));
110         if (fstat(fd, &st_fd) < 0)
111                 return -errno;
112
113         if (!S_ISFIFO(st_fd.st_mode))
114                 return 0;
115
116         if (path) {
117                 struct stat st_path;
118
119                 memset(&st_path, 0, sizeof(st_path));
120                 if (fstat(fd, &st_path) < 0) {
121
122                         if (errno == ENOENT || errno == ENOTDIR)
123                                 return 0;
124
125                         return -errno;
126                 }
127
128                 return
129                         st_path.st_dev == st_fd.st_dev &&
130                         st_path.st_ino == st_fd.st_ino;
131         }
132
133         return 1;
134 }
135
136 static int sd_is_socket_internal(int fd, int type, int listening) {
137         struct stat st_fd;
138
139         if (fd < 0 || type < 0)
140                 return -EINVAL;
141
142         if (fstat(fd, &st_fd) < 0)
143                 return -errno;
144
145         if (!S_ISSOCK(st_fd.st_mode))
146                 return 0;
147
148         if (type != 0) {
149                 int other_type = 0;
150                 socklen_t l = sizeof(other_type);
151
152                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
153                         return -errno;
154
155                 if (l != sizeof(other_type))
156                         return -EINVAL;
157
158                 if (other_type != type)
159                         return 0;
160         }
161
162         if (listening >= 0) {
163                 int accepting = 0;
164                 socklen_t l = sizeof(accepting);
165
166                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
167                         return -errno;
168
169                 if (l != sizeof(accepting))
170                         return -EINVAL;
171
172                 if (!accepting != !listening)
173                         return 0;
174         }
175
176         return 1;
177 }
178
179 union sockaddr_union {
180         struct sockaddr sa;
181         struct sockaddr_in in4;
182         struct sockaddr_in6 in6;
183         struct sockaddr_un un;
184         struct sockaddr_storage storage;
185 };
186
187 int sd_is_socket(int fd, int family, int type, int listening) {
188         int r;
189
190         if (family < 0)
191                 return -EINVAL;
192
193         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
194                 return r;
195
196         if (family > 0) {
197                 union sockaddr_union sockaddr;
198                 socklen_t l;
199
200                 memset(&sockaddr, 0, sizeof(sockaddr));
201                 l = sizeof(sockaddr);
202
203                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
204                         return -errno;
205
206                 if (l < sizeof(sa_family_t))
207                         return -EINVAL;
208
209                 return sockaddr.sa.sa_family == family;
210         }
211
212         return 1;
213 }
214
215 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
216         union sockaddr_union sockaddr;
217         socklen_t l;
218         int r;
219
220         if (family != 0 && family != AF_INET && family != AF_INET6)
221                 return -EINVAL;
222
223         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
224                 return r;
225
226         memset(&sockaddr, 0, sizeof(sockaddr));
227         l = sizeof(sockaddr);
228
229         if (getsockname(fd, &sockaddr.sa, &l) < 0)
230                 return -errno;
231
232         if (l < sizeof(sa_family_t))
233                 return -EINVAL;
234
235         if (sockaddr.sa.sa_family != AF_INET &&
236             sockaddr.sa.sa_family != AF_INET6)
237                 return 0;
238
239         if (family > 0)
240                 if (sockaddr.sa.sa_family != family)
241                         return 0;
242
243         if (port > 0) {
244                 if (sockaddr.sa.sa_family == AF_INET) {
245                         if (l < sizeof(struct sockaddr_in))
246                                 return -EINVAL;
247
248                         return htons(port) == sockaddr.in4.sin_port;
249                 } else {
250                         if (l < sizeof(struct sockaddr_in6))
251                                 return -EINVAL;
252
253                         return htons(port) == sockaddr.in6.sin6_port;
254                 }
255         }
256
257         return 1;
258 }
259
260 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
261         union sockaddr_union sockaddr;
262         socklen_t l;
263         int r;
264
265         if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
266                 return r;
267
268         memset(&sockaddr, 0, sizeof(sockaddr));
269         l = sizeof(sockaddr);
270
271         if (getsockname(fd, &sockaddr.sa, &l) < 0)
272                 return -errno;
273
274         if (l < sizeof(sa_family_t))
275                 return -EINVAL;
276
277         if (sockaddr.sa.sa_family != AF_UNIX)
278                 return 0;
279
280         if (path) {
281                 if (length <= 0)
282                         length = strlen(path);
283
284                 if (length <= 0)
285                         /* Unnamed socket */
286                         return l == sizeof(sa_family_t);
287
288                 if (path[0])
289                         /* Normal path socket */
290                         return
291                                 (l >= sizeof(sa_family_t) + length + 1) &&
292                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
293                 else
294                         /* Abstract namespace socket */
295                         return
296                                 (l == sizeof(sa_family_t) + length) &&
297                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
298         }
299
300         return 1;
301 }