Exemple #1
0
bool
server_process_request(twopence_transaction_t *trans, twopence_buf_t *payload)
{
	twopence_file_xfer_t xfer;
	twopence_command_t cmd;

	switch (trans->type) {
	case TWOPENCE_PROTO_TYPE_INJECT:
		twopence_file_xfer_init(&xfer);
		if (!twopence_protocol_dissect_inject_packet(payload, &xfer))
			goto bad_packet;

		server_inject_file(trans, &xfer);
		twopence_file_xfer_destroy(&xfer);
		break;

	case TWOPENCE_PROTO_TYPE_EXTRACT:
		twopence_file_xfer_init(&xfer);
		if (!twopence_protocol_dissect_extract_packet(payload, &xfer))
			goto bad_packet;

		server_extract_file(trans, &xfer);
		twopence_file_xfer_destroy(&xfer);
		break;

	case TWOPENCE_PROTO_TYPE_COMMAND:
		memset(&cmd, 0, sizeof(cmd));
		if (!twopence_protocol_dissect_command_packet(payload, &cmd)
		 || cmd.command[0] == '\0')
			goto bad_packet;

		server_run_command(trans, &cmd);
		twopence_command_destroy(&cmd);
		break;

	case TWOPENCE_PROTO_TYPE_QUIT:
		server_request_quit();
		/* we should not get here */
		trans->done = true;
		break;

	default:
		twopence_log_error("Unknown command code '%c' in global context\n", trans->type);
		return false;
	}

	return true;

bad_packet:
	twopence_log_error("unable to parse %s packet", twopence_protocol_packet_type_to_string(trans->type));
	return false;
}
Exemple #2
0
long
server_file_size(const char *filename, int fd, int *status)
{
	struct stat stb;

	if (fstat(fd, &stb) < 0) {
		*status = errno;
		twopence_log_error("%s: unable to stat: %m\n", filename);
		return -1;
	}
	if (!S_ISREG(stb.st_mode)) {
		twopence_log_error("%s: not a regular file\n", filename);
		*status = EISDIR;
		return -1;
	}
	return stb.st_size;
}
Exemple #3
0
static void
server_restore_privileges(struct saved_ids *saved_ids)
{
	if (saved_ids->uid == -1)
		return;

	seteuid(saved_ids->uid);
	if (geteuid() != saved_ids->uid) {
		twopence_log_error("Unable to restore previous uid %u: abort\n", saved_ids->uid);
		abort();
	}

	setegid(saved_ids->gid);
	if (getegid() != saved_ids->gid) {
		twopence_log_error("Unable to restore previous gid %u: abort\n", saved_ids->gid);
		abort();
	}
}
Exemple #4
0
static int
__socket_queue_xmit(twopence_sock_t *sock, twopence_buf_t *bp, int flags)
{
	int n = 0, f;

	f = fcntl(sock->fd, F_GETFL);
	if (flags & TWOPENCE_SOCK_XMIT_SYNCHRONOUS)
		fcntl(sock->fd, F_SETFL, f & ~O_NONBLOCK);

	if (sock->write_eof) {
		twopence_log_error("%s: attempt to queue data after write shutdown", __func__);
		goto out_drop_buffer;
	}

	if (flags & TWOPENCE_SOCK_XMIT_SYNCHRONOUS) {
		/* Flush out all queued packets first */
		while (twopence_queue_head(&sock->xmit_queue) != NULL) {
			n = twopence_sock_send_queued(sock);
			if (n < 0)
				goto out_drop_buffer;
		}
	}

	/* If nothing is queued to the socket, we might as well try to
	 * send this data directly. */
	if (twopence_queue_empty(&sock->xmit_queue)) {
		if (flags & TWOPENCE_SOCK_XMIT_SYNCHRONOUS) {
			/* fully synchronous */
			while (twopence_buf_count(bp) != 0) {
				n = twopence_sock_send_buffer(sock, bp);
				if (n < 0)
					goto out_drop_buffer;
			}
		} else
		if (flags & TWOPENCE_SOCK_XMIT_TRYTOWRITE) {
			/* opportunistic - write some */
			(void) twopence_sock_send_buffer(sock, bp);
		}
	}

	/* If there's data left in this buffer, queue it to the socket */
	if (twopence_buf_count(bp) != 0) {
		if (flags & TWOPENCE_SOCK_XMIT_CLONEBUF)
			bp = twopence_buf_clone(bp);
		twopence_queue_append(&sock->xmit_queue, twopence_packet_new(bp));
		goto out;
	}

out_drop_buffer:
	if (!(flags & TWOPENCE_SOCK_XMIT_CLONEBUF))
		twopence_buf_free(bp);

out:
	fcntl(sock->fd, F_SETFL, f);
	return n;
}
Exemple #5
0
static bool
server_change_hats_permanently(const struct passwd *user, int *status)
{
	/* Do nothing for the root user */
	if (!strcmp(user->pw_name, "root"))
		return true;

	if (initgroups(user->pw_name, user->pw_gid) < 0
	 || setgid(user->pw_gid) < 0
	 || setuid(user->pw_uid) < 0) {
		*status = errno;
		twopence_log_error("Unable to drop privileges to become user %s: %m", user->pw_name);
		return false;
	}

	return true;
}
Exemple #6
0
static bool
server_change_to_home(const struct passwd *user)
{
	const char *homedir;

	if ((homedir = user->pw_dir) == NULL || homedir[0] != '/') {
		twopence_debug("user %s has a home directory of \"%s\", substituting \"/\"",
				user->pw_name, user->pw_dir);
		homedir = "/";
	}

	if (chdir(homedir) < 0) {
		twopence_log_error("Cannot change to user %s's home directory: chdir(%s) failed: %m",
				user->pw_name, user->pw_dir);
		return false;
	}

	return true;
}
Exemple #7
0
twopence_sock_t *
twopence_sock_accept(twopence_sock_t *sock)
{
	int sock_fd;

	if (sock->fd < 0)
		return NULL;

	if (sock->poll_data == NULL || !(sock->poll_data->events & POLLIN))
		return NULL;

	sock_fd = accept(sock->fd, NULL, NULL);
	if (sock_fd < 0) {
		if (errno != EAGAIN)
			twopence_log_error("failed to accept incoming connection on socket: %m");
		return NULL;
	}

	return twopence_sock_new(sock_fd);
}
Exemple #8
0
static twopence_sock_t *
__twopence_socket_new(int fd, int oflags)
{
	twopence_sock_t *sock;
	int f;

	sock = twopence_calloc(1, sizeof(*sock));
	sock->fd = fd;
	sock->closeit = true;

	/* Set flags (usually for nonblocking IO) */
	if ((f = fcntl(fd, F_GETFL)) < 0
	 || fcntl(fd, F_SETFL, f | oflags) < 0) {
		twopence_log_error("socket_new: trouble setting socket to nonblocking I/O: %m\n");
		/* Continue anyway */
	}

	twopence_queue_init(&sock->xmit_queue);
	return sock;
}
Exemple #9
0
static bool
server_change_hats_temporarily(const struct passwd *user, struct saved_ids *saved_ids, int *status)
{
	/* Do nothing for the root user */
	if (!strcmp(user->pw_name, "root")) {
		saved_ids->uid = -1;
		return true;
	}

	saved_ids->uid = getuid();
	saved_ids->gid = getgid();

	if (initgroups(user->pw_name, user->pw_gid) < 0
	 || setegid(user->pw_gid) < 0
	 || seteuid(user->pw_uid) < 0) {
		*status = errno;
		twopence_log_error("Unable to drop privileges to become user %s: %m", user->pw_name);
		server_restore_privileges(saved_ids);
		return false;
	}

	return true;
}
Exemple #10
0
bool
server_run_command_recv(twopence_transaction_t *trans, const twopence_hdr_t *hdr, twopence_buf_t *payload)
{
	switch (hdr->type) {
	case TWOPENCE_PROTO_TYPE_INTR:
		/* Send signal to process, and shut down all I/O.
		 * When we send a signal, we're not really interested in what
		 * it has to say, not even "aargh".
		 */
		if (trans->pid && !trans->done) {
			/* Send the KILL signal to all processes in the process group */
			kill(-trans->pid, SIGKILL);
			twopence_transaction_close_sink(trans, 0);
			twopence_transaction_close_source(trans, 0); /* ID zero means all */
		}
		break;

	default:
		twopence_log_error("Unknown command code '%c' in transaction context\n", hdr->type);
		break;
	}

	return true;
}
Exemple #11
0
/*
 * Open the TCP socket
 *
 * Returns the file descriptor if successful, or -1 if failed
 */
static twopence_sock_t *
__twopence_tcp_open(struct twopence_pipe_target *pipe_handle)
{
  struct twopence_tcp_target *handle = (struct twopence_tcp_target *) pipe_handle;
  char *copy, *hostname, *portname = NULL;
  struct addrinfo hints;
  struct addrinfo *ai_list, *ai;
  int socket_fd = -1;
  int res;

  copy = hostname = twopence_strdup(handle->server_spec);
  if (hostname[0] == '[') {
    /* Something like [::1] */
    char *s;

    for (s = ++hostname; *s != ']'; ++s) {
      if (*s == '\0') {
        twopence_log_error("tcp: cannot parse \"%s\"", handle->server_spec);
	free(copy);
        return NULL;
      }
    }
    *s++ = '\0';
    if (*s == ':')
      portname = ++s;
    /* Any other garbage is silently ignored for now */
  } else
  if ((portname = strchr(hostname, ':')) != NULL)
    *portname++ = '\0';

  if (portname == NULL)
    portname = TWOPENCE_TCP_PORT_DEFAULT_STR;

  memset(&hints, 0, sizeof(hints));
  hints.ai_socktype = SOCK_STREAM;
  res = getaddrinfo(hostname, portname, &hints, &ai_list);

  free(copy);
  copy = hostname = portname = NULL;

  if (res != 0) {
    twopence_log_error("tcp: cannot resolve \"%s\": %s", handle->server_spec, gai_strerror(res));
    return NULL;
  }

  twopence_debug("trying to open connection to %s", handle->server_spec);
  for (ai = ai_list; ai && socket_fd < 0; ai = ai->ai_next) {
    socket_fd = socket(ai->ai_family, SOCK_STREAM, 0);
    if (socket_fd <= 0)
      break;

    // Open the connection
    if (connect(socket_fd, ai->ai_addr, ai->ai_addrlen) < 0) {
      /* Okay, this address didn't work. Try the next one */
      close(socket_fd);
      socket_fd = -1;
    }
  }

  freeaddrinfo(ai_list);
  if (socket_fd <= 0)
    return NULL;

  /* Note, we do not pass O_NONBLOCK here, but we do set O_CLOEXEC */
  return twopence_sock_new_flags(socket_fd, O_RDWR | O_CLOEXEC);
}
Exemple #12
0
int
server_open_file_as(const char *username, const char *filename, unsigned int filemode, int oflags, int *status)
{
	struct stat stb;
	struct saved_ids saved_ids;
	struct passwd *user;
	int fd;

	if (!(user = server_get_user(username, status))) {
		twopence_debug("Unknown user \"%s\"\n", username);
		return -1;
	}

	/* If the path is not absolute, interpret it relatively to the
	 * user's home directory */
	if (filename[0] != '/') {
		filename = server_build_path(user->pw_dir, filename);
		if (filename == NULL) {
			twopence_log_error("Unable to build path from user %s's home \"%s\" and relative name \"%s\"\n",
					username, user->pw_dir, filename);
			*status = ENAMETOOLONG;
			return false;
		}
	}

	twopence_debug("%s(user=%s, file=%s, flags=0%0)\n", __func__, username, filename, oflags);

	/* We may want to have the client specify the file mode as well */
	if (!strcmp(username, "root")) {
		fd = open(filename, oflags, filemode);
		if (fd < 0)
			*status = errno;
	} else {
		if (!server_change_hats_temporarily(user, &saved_ids, status))
			return -1;
		fd = open(filename, oflags, filemode);
		if (fd < 0)
			*status = errno;

		server_restore_privileges(&saved_ids);
	}

	if (fd < 0)
		return -1;

	if (fstat(fd, &stb) < 0) {
		*status = errno;
		twopence_log_error("failed to stat \"%s\": %m", filename);
		close(fd);
		return -1;
	}
	if (!S_ISREG(stb.st_mode)) {
		twopence_log_error("%s: not a regular file\n", filename);
		*status = EISDIR;
		close(fd);
		return -1;
	}
	if (oflags != O_RDONLY && fchmod(fd, filemode) < 0) {
		*status = errno;
		twopence_log_error("failed to change file mode \"%s\" to 0%o: %m", filename, filemode);
		close(fd);
		return -1;
	}

	return fd;
}