mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	Rework the access checks. Add owner field to more objects to do access control checks Make sure the object global is set in the object before signaling the new global.
		
			
				
	
	
		
			470 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			470 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Pinos
 | 
						|
 * Copyright (C) 2015 Wim Taymans <wim.taymans@gmail.com>
 | 
						|
 *
 | 
						|
 * This library is free software; you can redistribute it and/or
 | 
						|
 * modify it under the terms of the GNU Library General Public
 | 
						|
 * License as published by the Free Software Foundation; either
 | 
						|
 * version 2 of the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This library is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
 * Library General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Library General Public
 | 
						|
 * License along with this library; if not, write to the
 | 
						|
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 | 
						|
 * Boston, MA 02110-1301, USA.
 | 
						|
 */
 | 
						|
 | 
						|
#include <string.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <sys/un.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <sys/file.h>
 | 
						|
 | 
						|
#include "config.h"
 | 
						|
 | 
						|
#include "pinos/client/pinos.h"
 | 
						|
#include "pinos/client/log.h"
 | 
						|
#include "pinos/client/interfaces.h"
 | 
						|
 | 
						|
#include "pinos/server/core.h"
 | 
						|
#include "pinos/server/protocol-native.h"
 | 
						|
#include "pinos/server/node.h"
 | 
						|
#include "pinos/server/module.h"
 | 
						|
#include "pinos/server/client-node.h"
 | 
						|
#include "pinos/server/client.h"
 | 
						|
#include "pinos/server/resource.h"
 | 
						|
#include "pinos/server/link.h"
 | 
						|
#include "pinos/server/node-factory.h"
 | 
						|
#include "pinos/server/data-loop.h"
 | 
						|
#include "pinos/server/main-loop.h"
 | 
						|
 | 
						|
#ifndef UNIX_PATH_MAX
 | 
						|
#define UNIX_PATH_MAX   108
 | 
						|
#endif
 | 
						|
 | 
						|
#define LOCK_SUFFIX     ".lock"
 | 
						|
#define LOCK_SUFFIXLEN  5
 | 
						|
 | 
						|
typedef bool (*PinosDemarshalFunc) (void *object, void *data, size_t size);
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  int        fd;
 | 
						|
  int        fd_lock;
 | 
						|
  struct     sockaddr_un addr;
 | 
						|
  char       lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
 | 
						|
  PinosLoop *loop;
 | 
						|
  SpaSource *source;
 | 
						|
  char      *core_name;
 | 
						|
  SpaList    link;
 | 
						|
} Socket;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  PinosCore   *core;
 | 
						|
  SpaList      link;
 | 
						|
 | 
						|
  PinosProperties *properties;
 | 
						|
 | 
						|
  SpaList socket_list;
 | 
						|
  SpaList client_list;
 | 
						|
 | 
						|
  PinosListener before_iterate;
 | 
						|
} PinosProtocolNative;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  PinosProtocolNative *impl;
 | 
						|
  SpaList              link;
 | 
						|
  PinosClient         *client;
 | 
						|
  int                  fd;
 | 
						|
  SpaSource           *source;
 | 
						|
  PinosConnection     *connection;
 | 
						|
  PinosListener        resource_added;
 | 
						|
} PinosProtocolNativeClient;
 | 
						|
 | 
						|
static void
 | 
						|
client_destroy (PinosProtocolNativeClient *this)
 | 
						|
{
 | 
						|
  pinos_loop_destroy_source (this->impl->core->main_loop->loop,
 | 
						|
                             this->source);
 | 
						|
  pinos_client_destroy (this->client);
 | 
						|
  spa_list_remove (&this->link);
 | 
						|
 | 
						|
  pinos_connection_destroy (this->connection);
 | 
						|
  close (this->fd);
 | 
						|
  free (this);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_resource_added (PinosListener *listener,
 | 
						|
                   PinosClient   *client,
 | 
						|
                   PinosResource *resource)
 | 
						|
{
 | 
						|
  pinos_protocol_native_server_setup (resource);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
on_before_iterate (PinosListener *listener,
 | 
						|
                   PinosLoop     *loop)
 | 
						|
{
 | 
						|
  PinosProtocolNative *this = SPA_CONTAINER_OF (listener, PinosProtocolNative, before_iterate);
 | 
						|
  PinosProtocolNativeClient *client, *tmp;
 | 
						|
 | 
						|
  spa_list_for_each_safe (client, tmp, &this->client_list, link)
 | 
						|
    pinos_connection_flush (client->connection);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
connection_data (SpaSource *source,
 | 
						|
                 int        fd,
 | 
						|
                 SpaIO      mask,
 | 
						|
                 void      *data)
 | 
						|
{
 | 
						|
  PinosProtocolNativeClient *client = data;
 | 
						|
  PinosConnection *conn = client->connection;
 | 
						|
  uint8_t opcode;
 | 
						|
  uint32_t id;
 | 
						|
  uint32_t size;
 | 
						|
  PinosClient *c = client->client;
 | 
						|
  void *message;
 | 
						|
 | 
						|
  if (mask & (SPA_IO_ERR | SPA_IO_HUP)) {
 | 
						|
    pinos_log_error ("protocol-native %p: got connection error", client->impl);
 | 
						|
    client_destroy (client);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (mask & SPA_IO_IN) {
 | 
						|
    while (pinos_connection_get_next (conn, &opcode, &id, &message, &size)) {
 | 
						|
      PinosResource *resource;
 | 
						|
      const PinosDemarshalFunc *demarshal;
 | 
						|
 | 
						|
      pinos_log_trace ("protocol-native %p: got message %d from %u", client->impl, opcode, id);
 | 
						|
 | 
						|
      resource = pinos_map_lookup (&c->objects, id);
 | 
						|
      if (resource == NULL) {
 | 
						|
        pinos_log_error ("protocol-native %p: unknown resource %u", client->impl, id);
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      if (opcode >= resource->iface->n_methods) {
 | 
						|
        pinos_log_error ("protocol-native %p: invalid method %u", client->impl, opcode);
 | 
						|
        client_destroy (client);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      demarshal = resource->iface->methods;
 | 
						|
      if (!demarshal[opcode] || !demarshal[opcode] (resource, message, size)) {
 | 
						|
        pinos_log_error ("protocol-native %p: invalid message received", client->impl);
 | 
						|
        client_destroy (client);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static PinosProtocolNativeClient *
 | 
						|
client_new (PinosProtocolNative *impl,
 | 
						|
            int                  fd)
 | 
						|
{
 | 
						|
  PinosProtocolNativeClient *this;
 | 
						|
  PinosClient *client;
 | 
						|
  socklen_t len;
 | 
						|
  struct ucred ucred, *ucredp;
 | 
						|
 | 
						|
  this = calloc (1, sizeof (PinosProtocolNativeClient));
 | 
						|
  if (this == NULL)
 | 
						|
    goto no_native_client;
 | 
						|
 | 
						|
  this->impl = impl;
 | 
						|
  this->fd = fd;
 | 
						|
  this->source = pinos_loop_add_io (impl->core->main_loop->loop,
 | 
						|
                                    this->fd,
 | 
						|
                                    SPA_IO_ERR | SPA_IO_HUP,
 | 
						|
                                    false,
 | 
						|
                                    connection_data,
 | 
						|
                                    this);
 | 
						|
  if (this->source == NULL)
 | 
						|
    goto no_source;
 | 
						|
 | 
						|
  this->connection = pinos_connection_new (fd);
 | 
						|
  if (this->connection == NULL)
 | 
						|
    goto no_connection;
 | 
						|
 | 
						|
  len = sizeof (ucred);
 | 
						|
  if (getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) {
 | 
						|
    pinos_log_error ("no peercred: %m");
 | 
						|
    ucredp = NULL;
 | 
						|
  } else {
 | 
						|
    ucredp = &ucred;
 | 
						|
  }
 | 
						|
 | 
						|
  client = pinos_client_new (impl->core, ucredp, NULL);
 | 
						|
  if (client == NULL)
 | 
						|
    goto no_client;
 | 
						|
 | 
						|
  client->protocol_private = this->connection;
 | 
						|
 | 
						|
  this->client = client;
 | 
						|
 | 
						|
  spa_list_insert (impl->client_list.prev, &this->link);
 | 
						|
 | 
						|
  pinos_signal_add (&client->resource_added,
 | 
						|
                    &this->resource_added,
 | 
						|
                    on_resource_added);
 | 
						|
 | 
						|
  pinos_global_bind (impl->core->global,
 | 
						|
                     client,
 | 
						|
                     0,
 | 
						|
                     0);
 | 
						|
  return this;
 | 
						|
 | 
						|
no_client:
 | 
						|
  pinos_connection_destroy (this->connection);
 | 
						|
no_connection:
 | 
						|
  pinos_loop_destroy_source (impl->core->main_loop->loop,
 | 
						|
                             this->source);
 | 
						|
no_source:
 | 
						|
  free (this);
 | 
						|
no_native_client:
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static Socket *
 | 
						|
create_socket (void)
 | 
						|
{
 | 
						|
  Socket *s;
 | 
						|
 | 
						|
  if ((s = calloc(1, sizeof (Socket))) == NULL)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  s->fd = -1;
 | 
						|
  s->fd_lock = -1;
 | 
						|
  return s;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
destroy_socket (Socket *s)
 | 
						|
{
 | 
						|
  if (s->source)
 | 
						|
    pinos_loop_destroy_source (s->loop, s->source);
 | 
						|
  if (s->addr.sun_path[0])
 | 
						|
    unlink (s->addr.sun_path);
 | 
						|
  if (s->fd >= 0)
 | 
						|
    close (s->fd);
 | 
						|
  if (s->lock_addr[0])
 | 
						|
    unlink (s->lock_addr);
 | 
						|
  if (s->fd_lock >= 0)
 | 
						|
    close (s->fd_lock);
 | 
						|
  free (s);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
init_socket_name (Socket *s, const char *name)
 | 
						|
{
 | 
						|
  int name_size;
 | 
						|
  const char *runtime_dir;
 | 
						|
 | 
						|
  if ((runtime_dir = getenv ("XDG_RUNTIME_DIR")) == NULL) {
 | 
						|
    pinos_log_error ("XDG_RUNTIME_DIR not set in the environment");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  s->addr.sun_family = AF_LOCAL;
 | 
						|
  name_size = snprintf (s->addr.sun_path, sizeof (s->addr.sun_path),
 | 
						|
                        "%s/%s", runtime_dir, name) + 1;
 | 
						|
 | 
						|
  s->core_name = (s->addr.sun_path + name_size - 1) - strlen (name);
 | 
						|
 | 
						|
  if (name_size > (int)sizeof (s->addr.sun_path)) {
 | 
						|
    pinos_log_error ("socket path \"%s/%s\" plus null terminator exceeds 108 bytes",
 | 
						|
                     runtime_dir, name);
 | 
						|
    *s->addr.sun_path = 0;
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
lock_socket (Socket *s)
 | 
						|
{
 | 
						|
  struct stat socket_stat;
 | 
						|
 | 
						|
  snprintf (s->lock_addr, sizeof (s->lock_addr),
 | 
						|
            "%s%s", s->addr.sun_path, LOCK_SUFFIX);
 | 
						|
 | 
						|
  s->fd_lock = open (s->lock_addr, O_CREAT | O_CLOEXEC,
 | 
						|
                     (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
 | 
						|
 | 
						|
  if (s->fd_lock < 0) {
 | 
						|
    pinos_log_error ("unable to open lockfile %s check permissions", s->lock_addr);
 | 
						|
    goto err;
 | 
						|
  }
 | 
						|
 | 
						|
  if (flock (s->fd_lock, LOCK_EX | LOCK_NB) < 0) {
 | 
						|
    pinos_log_error ("unable to lock lockfile %s, maybe another daemon is running", s->lock_addr);
 | 
						|
    goto err_fd;
 | 
						|
  }
 | 
						|
 | 
						|
  if (stat (s->addr.sun_path, &socket_stat) < 0 ) {
 | 
						|
    if (errno != ENOENT) {
 | 
						|
      pinos_log_error ("did not manage to stat file %s\n", s->addr.sun_path);
 | 
						|
      goto err_fd;
 | 
						|
    }
 | 
						|
  } else if (socket_stat.st_mode & S_IWUSR ||
 | 
						|
             socket_stat.st_mode & S_IWGRP) {
 | 
						|
    unlink (s->addr.sun_path);
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
 | 
						|
err_fd:
 | 
						|
  close (s->fd_lock);
 | 
						|
  s->fd_lock = -1;
 | 
						|
err:
 | 
						|
  *s->lock_addr = 0;
 | 
						|
  *s->addr.sun_path = 0;
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
socket_data (SpaSource *source,
 | 
						|
             int        fd,
 | 
						|
             SpaIO      mask,
 | 
						|
             void      *data)
 | 
						|
{
 | 
						|
  PinosProtocolNative *impl = data;
 | 
						|
  PinosProtocolNativeClient *client;
 | 
						|
  struct sockaddr_un name;
 | 
						|
  socklen_t length;
 | 
						|
  int client_fd;
 | 
						|
 | 
						|
  length = sizeof (name);
 | 
						|
  client_fd = accept4 (fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC);
 | 
						|
  if (client_fd < 0) {
 | 
						|
    pinos_log_error ("failed to accept: %m");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  client = client_new (impl, client_fd);
 | 
						|
  if (client == NULL) {
 | 
						|
    pinos_log_error ("failed to create client");
 | 
						|
    close (client_fd);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  pinos_loop_update_io (impl->core->main_loop->loop,
 | 
						|
                        client->source,
 | 
						|
                        SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP);
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
add_socket (PinosProtocolNative *impl, Socket *s)
 | 
						|
{
 | 
						|
  socklen_t size;
 | 
						|
 | 
						|
  if ((s->fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0)
 | 
						|
    return false;
 | 
						|
 | 
						|
  size = offsetof (struct sockaddr_un, sun_path) + strlen (s->addr.sun_path);
 | 
						|
  if (bind (s->fd, (struct sockaddr *) &s->addr, size) < 0) {
 | 
						|
    pinos_log_error ("bind() failed with error: %m");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (listen (s->fd, 128) < 0) {
 | 
						|
    pinos_log_error ("listen() failed with error: %m");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  s->loop = impl->core->main_loop->loop;
 | 
						|
  s->source = pinos_loop_add_io (s->loop,
 | 
						|
                                 s->fd,
 | 
						|
                                 SPA_IO_IN,
 | 
						|
                                 false,
 | 
						|
                                 socket_data,
 | 
						|
                                 impl);
 | 
						|
  if (s->source == NULL)
 | 
						|
    return false;
 | 
						|
 | 
						|
  spa_list_insert (impl->socket_list.prev, &s->link);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PinosProtocolNative *
 | 
						|
pinos_protocol_native_new (PinosCore       *core,
 | 
						|
                           PinosProperties *properties)
 | 
						|
{
 | 
						|
  PinosProtocolNative *impl;
 | 
						|
  Socket *s;
 | 
						|
  const char *name;
 | 
						|
 | 
						|
  impl = calloc (1, sizeof (PinosProtocolNative));
 | 
						|
  pinos_log_debug ("protocol-native %p: new", impl);
 | 
						|
 | 
						|
  impl->core = core;
 | 
						|
  impl->properties = properties;
 | 
						|
 | 
						|
  name = NULL;
 | 
						|
  if (impl->properties)
 | 
						|
    name = pinos_properties_get (impl->properties, "pinos.core.name");
 | 
						|
  if (name == NULL)
 | 
						|
    name = getenv ("PINOS_CORE");
 | 
						|
  if (name == NULL)
 | 
						|
    name = "pinos-0";
 | 
						|
 | 
						|
  s = create_socket ();
 | 
						|
 | 
						|
  spa_list_init (&impl->socket_list);
 | 
						|
  spa_list_init (&impl->client_list);
 | 
						|
 | 
						|
  if (!init_socket_name (s, name))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  if (!lock_socket (s))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  if (!add_socket (impl, s))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  pinos_signal_add (&impl->core->main_loop->loop->before_iterate,
 | 
						|
                    &impl->before_iterate,
 | 
						|
                    on_before_iterate);
 | 
						|
 | 
						|
  return impl;
 | 
						|
 | 
						|
error:
 | 
						|
  destroy_socket (s);
 | 
						|
  free (impl);
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
static void
 | 
						|
pinos_protocol_native_destroy (PinosProtocolNative *impl)
 | 
						|
{
 | 
						|
  PinosProtocolNativeObject *object, *tmp;
 | 
						|
 | 
						|
  pinos_log_debug ("protocol-native %p: destroy", impl);
 | 
						|
 | 
						|
  pinos_signal_remove (&impl->before_iterate);
 | 
						|
 | 
						|
  pinos_global_destroy (impl->global);
 | 
						|
 | 
						|
  spa_list_for_each_safe (object, tmp, &impl->object_list, link)
 | 
						|
    object_destroy (object);
 | 
						|
 | 
						|
  free (impl);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
bool
 | 
						|
pinos__module_init (PinosModule * module, const char * args)
 | 
						|
{
 | 
						|
  pinos_protocol_native_new (module->core, NULL);
 | 
						|
  return true;
 | 
						|
}
 |