| 75 | | * Tries to find a real non-root user to use |
|---|
| 76 | | */ |
|---|
| 77 | | static struct passwd *guess_user (void) |
|---|
| 78 | | { |
|---|
| 79 | | const char *name; |
|---|
| 80 | | struct passwd *pw; |
|---|
| 81 | | uid_t uid; |
|---|
| 82 | | |
|---|
| 83 | | /* Try real UID */ |
|---|
| 84 | | uid = getuid (); |
|---|
| 85 | | if (uid) |
|---|
| 86 | | if ((pw = getpwuid (uid)) != NULL) |
|---|
| 87 | | return pw; |
|---|
| 88 | | |
|---|
| 89 | | /* Try sudo */ |
|---|
| 90 | | name = getenv ("SUDO_USER"); |
|---|
| 91 | | if (name != NULL) |
|---|
| 92 | | if ((pw = getpwnam (name)) != NULL) |
|---|
| 93 | | return pw; |
|---|
| 94 | | |
|---|
| 95 | | /* Try VLC_USER */ |
|---|
| 96 | | name = getenv ("VLC_USER"); |
|---|
| 97 | | if (name != NULL) |
|---|
| 98 | | if ((pw = getpwnam (name)) != NULL) |
|---|
| 99 | | return pw; |
|---|
| 100 | | |
|---|
| 101 | | /* Try vlc */ |
|---|
| 102 | | if ((pw = getpwnam ("vlc")) != NULL) |
|---|
| 103 | | return pw; |
|---|
| 104 | | |
|---|
| 105 | | return getpwuid (0); |
|---|
| 106 | | } |
|---|
| 107 | | |
|---|
| 108 | | |
|---|
| 109 | | static int is_allowed_port (uint16_t port) |
|---|
| 110 | | { |
|---|
| 111 | | port = ntohs (port); |
|---|
| 112 | | |
|---|
| 113 | | return (port == 80) || (port == 443) || (port == 554); |
|---|
| 114 | | } |
|---|
| 115 | | |
|---|
| 116 | | |
|---|
| 117 | | static int send_err (int fd, int err) |
|---|
| 118 | | { |
|---|
| 119 | | return send (fd, &err, sizeof (err), 0) == sizeof (err) ? 0 : -1; |
|---|
| 120 | | } |
|---|
| 121 | | |
|---|
| 122 | | /** |
|---|
| 123 | | * Ugly POSIX(?) code to pass a file descriptor to another process |
|---|
| | 62 | * Send a file descriptor to another process |
|---|
| 221 | | static int rootwrap_sock = -1; |
|---|
| 222 | | static pid_t rootwrap_pid = -1; |
|---|
| 223 | | |
|---|
| 224 | | static void close_rootwrap (void) |
|---|
| 225 | | { |
|---|
| 226 | | close (rootwrap_sock); |
|---|
| 227 | | waitpid (rootwrap_pid, NULL, 0); |
|---|
| 228 | | } |
|---|
| 229 | | |
|---|
| 230 | | void rootwrap (void) |
|---|
| 231 | | { |
|---|
| 232 | | struct rlimit lim; |
|---|
| 233 | | int fd, pair[2]; |
|---|
| 234 | | struct passwd *pw; |
|---|
| 235 | | uid_t u; |
|---|
| 236 | | |
|---|
| 237 | | u = geteuid (); |
|---|
| 238 | | /* Are we running with root privileges? */ |
|---|
| 239 | | if (u != 0) |
|---|
| 240 | | { |
|---|
| 241 | | setuid (u); |
|---|
| 242 | | return; |
|---|
| 243 | | } |
|---|
| 244 | | |
|---|
| 245 | | /* Make sure 0, 1 and 2 are opened, and only these. */ |
|---|
| 246 | | if (getrlimit (RLIMIT_NOFILE, &lim)) |
|---|
| 247 | | exit (1); |
|---|
| 248 | | |
|---|
| 249 | | for (fd = 3; ((unsigned)fd) < lim.rlim_cur; fd++) |
|---|
| 250 | | close (fd); |
|---|
| 251 | | |
|---|
| 252 | | fd = dup (2); |
|---|
| 253 | | if (fd <= 2) |
|---|
| 254 | | exit (1); |
|---|
| 255 | | close (fd); |
|---|
| 256 | | |
|---|
| 257 | | fputs ("starting VLC root wrapper...", stderr); |
|---|
| 258 | | |
|---|
| 259 | | pw = guess_user (); |
|---|
| 260 | | if (pw == NULL) |
|---|
| 261 | | return; /* Should we rather print an error and exit ? */ |
|---|
| 262 | | |
|---|
| 263 | | u = pw->pw_uid, |
|---|
| 264 | | fprintf (stderr, " using UID %u (%s)\n", (unsigned)u, pw->pw_name); |
|---|
| 265 | | if (u == 0) |
|---|
| 266 | | { |
|---|
| 267 | | fputs ("***************************************\n" |
|---|
| 268 | | "* Running VLC as root is discouraged. *\n" |
|---|
| 269 | | "***************************************\n" |
|---|
| 270 | | "\n" |
|---|
| 271 | | " It is potentially dangerous, " |
|---|
| 272 | | "and might not even work properly.\n", stderr); |
|---|
| 273 | | return; |
|---|
| 274 | | } |
|---|
| 275 | | |
|---|
| 276 | | /* GID */ |
|---|
| 277 | | initgroups (pw->pw_name, pw->pw_gid); |
|---|
| 278 | | setgid (pw->pw_gid); |
|---|
| | 156 | /* TODO? |
|---|
| | 157 | * - use libcap if available, |
|---|
| | 158 | * - call chroot |
|---|
| | 159 | */ |
|---|
| | 160 | |
|---|
| | 161 | int main (int argc, char *argv[]) |
|---|
| | 162 | { |
|---|
| | 163 | /* Support for dynamically opening RTSP, HTTP and HTTP/SSL ports */ |
|---|
| | 164 | int pair[2]; |
|---|
| 301 | | |
|---|
| 302 | | default: |
|---|
| 303 | | close (pair[1]); |
|---|
| 304 | | rootwrap_sock = pair[0]; |
|---|
| 305 | | break; |
|---|
| 306 | | } |
|---|
| 307 | | |
|---|
| 308 | | nofork: |
|---|
| 309 | | /* UID */ |
|---|
| 310 | | setuid (u); |
|---|
| 311 | | |
|---|
| 312 | | atexit (close_rootwrap); |
|---|
| 313 | | } |
|---|
| 314 | | |
|---|
| 315 | | |
|---|
| 316 | | /** |
|---|
| 317 | | * Ugly POSIX(?) code to receive a file descriptor from another process |
|---|
| 318 | | */ |
|---|
| 319 | | static int recv_fd (int p) |
|---|
| 320 | | { |
|---|
| 321 | | struct msghdr hdr; |
|---|
| 322 | | struct iovec iov; |
|---|
| 323 | | struct cmsghdr *cmsg; |
|---|
| 324 | | int val, fd; |
|---|
| 325 | | char buf[CMSG_SPACE (sizeof (fd))]; |
|---|
| 326 | | |
|---|
| 327 | | hdr.msg_name = NULL; |
|---|
| 328 | | hdr.msg_namelen = 0; |
|---|
| 329 | | hdr.msg_iov = &iov; |
|---|
| 330 | | hdr.msg_iovlen = 1; |
|---|
| 331 | | hdr.msg_control = buf; |
|---|
| 332 | | hdr.msg_controllen = sizeof (buf); |
|---|
| 333 | | |
|---|
| 334 | | iov.iov_base = &val; |
|---|
| 335 | | iov.iov_len = sizeof (val); |
|---|
| 336 | | |
|---|
| 337 | | if (recvmsg (p, &hdr, 0) != sizeof (val)) |
|---|
| 338 | | return -1; |
|---|
| 339 | | |
|---|
| 340 | | for (cmsg = CMSG_FIRSTHDR (&hdr); cmsg != NULL; |
|---|
| 341 | | cmsg = CMSG_NXTHDR (&hdr, cmsg)) |
|---|
| 342 | | { |
|---|
| 343 | | if ((cmsg->cmsg_level == SOL_SOCKET) |
|---|
| 344 | | && (cmsg->cmsg_type = SCM_RIGHTS) |
|---|
| 345 | | && (cmsg->cmsg_len >= CMSG_LEN (sizeof (fd)))) |
|---|
| 346 | | { |
|---|
| 347 | | memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd)); |
|---|
| 348 | | return fd; |
|---|
| 352 | | return -1; |
|---|
| 353 | | } |
|---|
| 354 | | |
|---|
| 355 | | /** |
|---|
| 356 | | * Tries to obtain a bound TCP socket from the root process |
|---|
| 357 | | */ |
|---|
| 358 | | int rootwrap_bind (int family, int socktype, int protocol, |
|---|
| 359 | | const struct sockaddr *addr, size_t alen) |
|---|
| 360 | | { |
|---|
| 361 | | /* can't use libvlc */ |
|---|
| 362 | | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
|---|
| 363 | | |
|---|
| 364 | | struct sockaddr_storage ss; |
|---|
| 365 | | int fd; |
|---|
| 366 | | |
|---|
| 367 | | if (rootwrap_sock == -1) |
|---|
| 368 | | { |
|---|
| 369 | | errno = EACCES; |
|---|
| 370 | | return -1; |
|---|
| 371 | | } |
|---|
| 372 | | |
|---|
| 373 | | switch (family) |
|---|
| 374 | | { |
|---|
| 375 | | case AF_INET: |
|---|
| 376 | | if (alen < sizeof (struct sockaddr_in)) |
|---|
| 377 | | { |
|---|
| 378 | | errno = EINVAL; |
|---|
| 379 | | return -1; |
|---|
| 380 | | } |
|---|
| 381 | | break; |
|---|
| 382 | | |
|---|
| 383 | | #ifdef AF_INET6 |
|---|
| 384 | | case AF_INET6: |
|---|
| 385 | | if (alen < sizeof (struct sockaddr_in6)) |
|---|
| 386 | | { |
|---|
| 387 | | errno = EINVAL; |
|---|
| 388 | | return -1; |
|---|
| 389 | | } |
|---|
| 390 | | break; |
|---|
| 391 | | #endif |
|---|
| 392 | | |
|---|
| 393 | | default: |
|---|
| 394 | | errno = EAFNOSUPPORT; |
|---|
| 395 | | return -1; |
|---|
| 396 | | } |
|---|
| 397 | | |
|---|
| 398 | | if (family != addr->sa_family) |
|---|
| 399 | | { |
|---|
| 400 | | errno = EAFNOSUPPORT; |
|---|
| 401 | | return -1; |
|---|
| 402 | | } |
|---|
| 403 | | |
|---|
| 404 | | /* Only TCP is implemented at the moment */ |
|---|
| 405 | | if ((socktype != SOCK_STREAM) |
|---|
| 406 | | || (protocol && (protocol != IPPROTO_TCP))) |
|---|
| 407 | | { |
|---|
| 408 | | errno = EACCES; |
|---|
| 409 | | return -1; |
|---|
| 410 | | } |
|---|
| 411 | | |
|---|
| 412 | | memset (&ss, 0, sizeof (ss)); |
|---|
| 413 | | memcpy (&ss, addr, alen > sizeof (ss) ? sizeof (ss) : alen); |
|---|
| 414 | | |
|---|
| 415 | | pthread_mutex_lock (&mutex); |
|---|
| 416 | | if (send (rootwrap_sock, &ss, sizeof (ss), 0) != sizeof (ss)) |
|---|
| 417 | | return -1; |
|---|
| 418 | | |
|---|
| 419 | | fd = recv_fd (rootwrap_sock); |
|---|
| 420 | | pthread_mutex_unlock (&mutex); |
|---|
| 421 | | |
|---|
| 422 | | if (fd != -1) |
|---|
| 423 | | { |
|---|
| 424 | | int val; |
|---|
| 425 | | |
|---|
| 426 | | val = fcntl (fd, F_GETFL, 0); |
|---|
| 427 | | fcntl (fd, F_SETFL, ((val != -1) ? val : 0) | O_NONBLOCK); |
|---|
| 428 | | } |
|---|
| 429 | | |
|---|
| 430 | | return fd; |
|---|
| 431 | | } |
|---|
| 432 | | |
|---|
| 433 | | #else |
|---|
| 434 | | # include <stddef.h> |
|---|
| 435 | | |
|---|
| 436 | | struct sockaddr; |
|---|
| 437 | | |
|---|
| 438 | | void rootwrap (void) |
|---|
| 439 | | { |
|---|
| 440 | | } |
|---|
| 441 | | |
|---|
| 442 | | int rootwrap_bind (int family, int socktype, int protocol, |
|---|
| 443 | | const struct sockaddr *addr, size_t alen) |
|---|
| 444 | | { |
|---|
| 445 | | (void)family; |
|---|
| 446 | | (void)socktype; |
|---|
| 447 | | (void)protocol; |
|---|
| 448 | | (void)addr; |
|---|
| 449 | | (void)alen; |
|---|
| 450 | | return -1; |
|---|
| 451 | | } |
|---|
| 452 | | |
|---|
| 453 | | #endif /* ENABLE_ROOTWRAP */ |
|---|
| | 194 | close (pair[1]); |
|---|
| | 195 | pair[1] = -1; |
|---|
| | 196 | |
|---|
| | 197 | char buf[21]; |
|---|
| | 198 | snprintf (buf, sizeof (buf), "%d", pair[0]); |
|---|
| | 199 | setenv ("VLC_ROOTWRAP_SOCK", buf, 1); |
|---|
| | 200 | |
|---|
| | 201 | /* Support for real-time priorities */ |
|---|
| | 202 | #ifdef RLIMIT_RTPRIO |
|---|
| | 203 | struct rlimit rlim; |
|---|
| | 204 | rlim.rlim_max = rlim.rlim_cur = sched_get_priority_min (SCHED_RR) + 24; |
|---|
| | 205 | setrlimit (RLIMIT_RTPRIO, &rlim); |
|---|
| | 206 | #endif |
|---|
| | 207 | |
|---|
| | 208 | setuid (getuid ()); |
|---|
| | 209 | |
|---|
| | 210 | if (!setuid (0)) /* sanity check: we cannot get root back */ |
|---|
| | 211 | exit (1); |
|---|
| | 212 | |
|---|
| | 213 | /* Yeah, the user can force to execute just about anything from here. |
|---|
| | 214 | * But we've dropped privileges, so it does not matter. */ |
|---|
| | 215 | if (strlen (argv[0]) < sizeof ("-wrapper")) |
|---|
| | 216 | goto error; |
|---|
| | 217 | argv[0][strlen (argv[0]) - strlen ("-wrapper")] = '\0'; |
|---|
| | 218 | |
|---|
| | 219 | (void)argc; |
|---|
| | 220 | if (execvp (argv[0], argv)) |
|---|
| | 221 | perror (argv[0]); |
|---|
| | 222 | |
|---|
| | 223 | error: |
|---|
| | 224 | close (pair[0]); |
|---|
| | 225 | if (pair[1] != -1) |
|---|
| | 226 | close (pair[1]); |
|---|
| | 227 | return 1; |
|---|
| | 228 | } |
|---|