예제 #1
0
파일: user.c 프로젝트: Gibstick/seashell
/**
 * int main()
 *
 * Invokes the backend, waits until it has successfully detached from the I/O ports,
 * and then quits. 
 */
int main() {
  /** Block SIGUSR1, SIGCHLD until we can handle them. */
  sigset_t waitset;
  sigemptyset(&waitset);
  sigaddset(&waitset, SIGUSR1);
  sigaddset(&waitset, SIGCHLD);
  sigprocmask(SIG_BLOCK, &waitset, NULL);
  
  /** Install signal handlers for those signals. */
  struct sigaction actions;
  sigemptyset(&actions.sa_mask);
  actions.sa_flags = SA_SIGINFO;
  actions.sa_sigaction = dummy_signal_action;
  sigaction(SIGUSR1, &actions, NULL);
  sigaction(SIGCHLD, &actions, NULL);
  
  /** Fork the child */
  pid_t childpid = fork();
  if(childpid < 0) {
    perror("Could not fork() child:");
    return 1;
  }

  if(childpid == 0) {
    /* Detach session, setup file descriptors, spawn Racket process. */
    if(setsid() < 0) {
      perror("Could not detach session:");
      return 1;
    }

#ifndef NDEBUG
    putenv("PLTSTDERR=debug");
#endif

    // Prefer build directory if debug build.
    if(!IS_INSTALLED() && access(SEASHELL_DEBUG_MAIN, F_OK) != -1) {
      char * argv2[] = {SEASHELL_DEBUG_MAIN, "-s", NULL};
      execv(SEASHELL_DEBUG_MAIN, argv2);
      perror("Could not execv() the Seashell backend:");
      return 1;
    }
    // Main case if not in debug mode (fall through if)
    char * argv[] = {SEASHELL_MAIN, "-s", NULL};
    execv(SEASHELL_MAIN, argv);

    perror("Could not execv() the Seashell backend:");
    return 1;
  } else {
    /* Setup file descriptors, pass key and port, exit. */
    while (true) {
      siginfo_t info;
      int result = sigwaitinfo(&waitset, &info);

      if (result < 0) {
        if (errno == EINTR) {
          continue;
        } else {
          perror("sigwaitinfo failed with:");
          return 1;
        }
      } else {
        /** Wait for the process to quit (successfully) or detach. */
        if (info.si_signo == SIGCHLD) {
          int status = 0;
          result = waitpid(childpid, &status, WCONTINUED | WUNTRACED);

          if (result < 0) {
            perror("wait failed with:");
            return 1;
          } else if (WIFEXITED(status)) {
            return WEXITSTATUS(status);
          }
        } else if (info.si_signo == SIGUSR1) {
          return 0;
        }
      }
    }
  }
}
예제 #2
0
파일: tunnel.c 프로젝트: Tiedye/seashell
/**
 * seashell_tunnel_connect_password (const char* host, const char* user, const char* password, int* error)
 * Connects to the host via SSH on port 22, and launches a Seashell backend instance for that user
 * on the host.
 *
 * Consults /etc/seashell_hosts for host's SSH public keys.  If this file does not exist,
 * this function will fail for security reasons.  /etc/seashell_hosts is a standard
 * OpenSSH known_hosts file.
 *
 * Arguments:
 *  host - Host to connect to.
 *  user - User to run as.
 *  password - User's password.
 *  error - [optional] denotes error on failure.
 *  remote_addr - Address to which the remote IP address will
 *   be written. Reserve 128 bytes.
 *  family - Address family.
 *  target - Target to execute.
 *
 * Returns:
 *  Handle to connection object on success, NULL otherwise.
 *  If error is NOT null, error will hold more detailed error information.
 */
struct seashell_connection* seashell_tunnel_connect_password (const char* host,
    const char* user,
    const char* password,
    int* error,
    char * remote_addr,
    int* family,
    char* target) {
  struct addrinfo hints;
  struct addrinfo *results, *rp;
  int sockfd;
  int i, e;
  struct seashell_connection* result = NULL;

  /* Resolve the host's address.
   * See getaddrinfo(3) for how this works.
   */
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = 0;
  hints.ai_protocol = 0;

  e = getaddrinfo(host, "22", &hints, &results);
  if (e != 0) {
    SET_ERROR(TUNNEL_ERROR_RESOLV);
    return NULL;
  }

  for (rp = results; rp != NULL; rp = rp->ai_next) {
    sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    if (sockfd == -1)
      continue;

    if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) != -1)
      break;

    close(sockfd);
  }

  /* Write address that we're connecting to into
   * remote_addr.
   */

  if(rp != NULL) {
    *family = rp->ai_family;

    switch(rp->ai_family) {
      case AF_INET:
        if(inet_ntop(rp->ai_family, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, remote_addr, 128) == NULL) {
          SET_ERROR(TUNNEL_ERROR_RESOLV);
          return NULL;
        }
        break;
      case AF_INET6:
        if(inet_ntop(rp->ai_family, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, remote_addr, 128) == NULL) {
          SET_ERROR(TUNNEL_ERROR_RESOLV);
          return NULL;
        }
        break;
      default:
        SET_ERROR(TUNNEL_ERROR_RESOLV);
        return NULL;
    }
  }

  freeaddrinfo(results);

  /* Either rp == NULL, in which case we failed at connecting,
   * or sockfd holds our socket.
   */
  if (rp == NULL) {
    SET_ERROR(TUNNEL_ERROR_CONNECT);
    return NULL;
  }

  /** Set up the session */
  LIBSSH2_SESSION* session;
  LIBSSH2_CHANNEL* channel;
  LIBSSH2_KNOWNHOSTS* hosts;
  size_t len;
  int type;

  session = libssh2_session_init();
  if (!session) {
    SET_ERROR(TUNNEL_ERROR_SESSION_START);
    goto session_teardown;
  }

  e = libssh2_session_handshake(session, sockfd);
  if (e) {
    SET_ERROR(TUNNEL_ERROR_SESSION_HANDSHAKE);
    goto session_teardown;
  }

  hosts = libssh2_knownhost_init(session);
  if (!hosts) {
    SET_ERROR(TUNNEL_ERROR_HOSTS_FILE);
    goto session_teardown;
  }

  if (!IS_INSTALLED() && access(DEBUG_HOSTS_FILE, F_OK) != -1) {
    libssh2_knownhost_readfile(hosts, DEBUG_HOSTS_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
  } else {
    libssh2_knownhost_readfile(hosts, HOSTS_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
  }

  const char* fingerprint = libssh2_session_hostkey(session, &len, &type);
  if (!fingerprint || type == LIBSSH2_HOSTKEY_TYPE_UNKNOWN) {
    libssh2_knownhost_free(hosts);

    SET_ERROR(TUNNEL_ERROR_HOST);
    goto session_teardown;
  }

  struct libssh2_knownhost *hostkey;
  /** NOTE: Documentation is buggy.  hostkey MUST be passed. */
  int check = libssh2_knownhost_check(hosts, host, fingerprint, len,
      LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, &hostkey);

  if (check != LIBSSH2_KNOWNHOST_CHECK_MATCH) {
    int keytype = 0;

    switch (type) {
      case LIBSSH2_HOSTKEY_TYPE_RSA:
        keytype = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
        break;
      case LIBSSH2_HOSTKEY_TYPE_DSS:
        keytype = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
        break;
    }

    if (keytype) {
      libssh2_knownhost_addc(hosts, host, NULL, fingerprint, len,
          "Generated from Seashell Tunnel", strlen("Generated from Seashell Tunnel"),
            LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW
          | (type == LIBSSH2_HOSTKEY_TYPE_RSA ? LIBSSH2_KNOWNHOST_KEY_SSHRSA : LIBSSH2_KNOWNHOST_KEY_SSHDSS),
          NULL);

      libssh2_knownhost_writefile(hosts, DUMP_FILE, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
      fprintf(stderr, "%s: Check SSH key for %s! Keys written to %s\n", user, host, DUMP_FILE);
    } else {
      fprintf(stderr, "%s: Check SSH key for %s!\n", user, host, DUMP_FILE);
      fprintf(stderr, "%s: Keys not written to file - contact Seashell Maintainers to add support for the LibSSH2 key format %d\n", user, type);
    }

    libssh2_knownhost_free(hosts);

    SET_ERROR(TUNNEL_ERROR_HOST);
    goto session_teardown;
  }
  libssh2_knownhost_free(hosts);

  FPRINTF_IF_DEBUG(stderr, "%s: Host check passed for %s (fingerprint type %d) - ", user, host, type);
  for(i = 0; i < 20; i++) {
    FPRINTF_IF_DEBUG(stderr, "%02X ", (unsigned char)fingerprint[i]);
  }
  FPRINTF_IF_DEBUG(stderr, "\n");

  e = libssh2_userauth_password(session, user, password);
  if (e) {
    FPRINTF_IF_DEBUG(stderr, "%s: Error authenticating: %d\n", user, e);
    SET_ERROR(TUNNEL_ERROR_CREDS);
    goto session_teardown;
  }

  channel = libssh2_channel_open_session(session);
  if (!channel) {
    SET_ERROR(TUNNEL_ERROR_CHANNEL_OPEN);
    goto session_teardown;
  }

  /**
   * Ideally we'd have a subsystem configured,
   * as I don't see a good way of pulling out of ssh2
   * if the target does not exist.
   */
  e = libssh2_channel_exec(channel, target);
  if (e) {
    SET_ERROR(TUNNEL_ERROR_LAUNCH_SEASHELL);
    goto channel_teardown;
  }

  result = malloc(sizeof(struct seashell_connection));
  if (!result) {
    SET_ERROR(TUNNEL_ERROR_SESSION_START);
    goto channel_teardown;
  }

  result->sockfd = sockfd;
  result->session = session;
  result->channel = channel;

  goto end;
channel_teardown:
  libssh2_channel_free(channel);
session_teardown:
  libssh2_session_free(session);
  close(sockfd);
end:
  return result;
}