b9c94b55eac5f34d95e198425e98ac2a4fcd8fb9
[vlc.git] / src / network / rootbind.c
1 /*****************************************************************************
2  * rootbind.c: bind to reserved ports through the root wrapper
3  *****************************************************************************
4  * Copyright © 2005-2008 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #if HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #if !defined (WIN32) && !defined (SYS_BEOS)
26 # define ENABLE_ROOTWRAP 1
27 #endif
28
29 #include <stddef.h>
30 struct sockaddr;
31 int rootwrap_bind (int, int, int, const struct sockaddr *, size_t);
32
33 #include <errno.h>
34
35 #ifdef ENABLE_ROOTWRAP
36
37 #include <string.h>
38 #include <stdlib.h>
39
40 #include <sys/types.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <sys/uio.h>
44 #include <sys/un.h>
45 #include <netinet/in.h>
46 #include <pthread.h>
47
48 /* Required yet non-standard cmsg functions */
49 #ifndef CMSG_ALIGN
50 # define CMSG_ALIGN(len) (((len) + sizeof(intptr_t)-1) & ~(sizeof(intptr_t)-1))
51 #endif
52 #ifndef CMSG_SPACE
53 # define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len))
54 #endif
55 #ifndef CMSG_LEN
56 # define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
57 #endif
58
59 /**
60  * Receive a file descriptor from another process
61  */
62 static int recv_fd (int p)
63 {
64     struct msghdr hdr;
65     struct iovec iov;
66     struct cmsghdr *cmsg;
67     int val, fd;
68     char buf[CMSG_SPACE (sizeof (fd))];
69
70     hdr.msg_name = NULL;
71     hdr.msg_namelen = 0;
72     hdr.msg_iov = &iov;
73     hdr.msg_iovlen = 1;
74     hdr.msg_control = buf;
75     hdr.msg_controllen = sizeof (buf);
76
77     iov.iov_base = &val;
78     iov.iov_len = sizeof (val);
79
80     if (recvmsg (p, &hdr, 0) != sizeof (val))
81         return -1;
82
83     for (cmsg = CMSG_FIRSTHDR (&hdr); cmsg != NULL;
84          cmsg = CMSG_NXTHDR (&hdr, cmsg))
85     {
86         if ((cmsg->cmsg_level == SOL_SOCKET)
87          && (cmsg->cmsg_type = SCM_RIGHTS)
88          && (cmsg->cmsg_len >= CMSG_LEN (sizeof (fd))))
89         {
90             memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
91             return fd;
92         }
93     }
94
95     errno = val;
96     return -1;
97 }
98
99 /**
100  * Tries to obtain a bound TCP socket from the root process
101  */
102 int rootwrap_bind (int family, int socktype, int protocol,
103                    const struct sockaddr *addr, size_t alen)
104 {
105     /* can't use libvlc */
106     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
107     struct sockaddr_storage ss;
108     int fd, sock = -1;
109
110     const char *sockenv = getenv ("VLC_ROOTWRAP_SOCK");
111     if (sockenv != NULL)
112         sock = atoi (sockenv);
113     if (sock == -1)
114     {
115         errno = EACCES;
116         return -1;
117     }
118
119     switch (family)
120     {
121         case AF_INET:
122             if (alen < sizeof (struct sockaddr_in))
123             {
124                 errno = EINVAL;
125                 return -1;
126             }
127             break;
128
129 #ifdef AF_INET6
130         case AF_INET6:
131             if (alen < sizeof (struct sockaddr_in6))
132             {
133                 errno = EINVAL;
134                 return -1;
135             }
136             break;
137 #endif
138
139         default:
140             errno = EAFNOSUPPORT;
141             return -1;
142     }
143
144     if (family != addr->sa_family)
145     {
146         errno = EAFNOSUPPORT;
147         return -1;
148     }
149
150     /* Only TCP is implemented at the moment */
151     if ((socktype != SOCK_STREAM)
152      || (protocol && (protocol != IPPROTO_TCP)))
153     {
154         errno = EACCES;
155         return -1;
156     }
157
158     memset (&ss, 0, sizeof (ss));
159     memcpy (&ss, addr, (alen > sizeof (ss)) ? sizeof (ss) : alen);
160
161     pthread_mutex_lock (&mutex);
162     if (send (sock, &ss, sizeof (ss), 0) != sizeof (ss))
163         return -1;
164
165     fd = recv_fd (sock);
166     pthread_mutex_unlock (&mutex);
167     return fd;
168 }
169
170 #else
171 int rootwrap_bind (int family, int socktype, int protocol,
172                    const struct sockaddr *addr, size_t alen)
173 {
174     (void)family;
175     (void)socktype;
176     (void)protocol;
177     (void)addr;
178     (void)alen;
179     errno = EACCES;
180     return -1;
181 }
182
183 #endif /* ENABLE_ROOTWRAP */