static ssh_channel channel_open_request_cb(ssh_session session, void *userdata) { struct tmate_ssh_client *client = userdata; if (!client->username) { /* The authentication did not go through yet */ return NULL; } if (client->channel) { /* * We already have a channel, and we don't support multi * channels yet. Returning NULL means the channel request will * be denied. */ return NULL; } client->channel = ssh_channel_new(session); if (!client->channel) tmate_fatal("Error getting channel"); memset(&client->channel_cb, 0, sizeof(client->channel_cb)); ssh_callbacks_init(&client->channel_cb); client->channel_cb.userdata = client; client->channel_cb.channel_pty_request_function = pty_request; client->channel_cb.channel_shell_request_function = shell_request; client->channel_cb.channel_subsystem_request_function = subsystem_request; client->channel_cb.channel_exec_request_function = exec_request; ssh_set_channel_callbacks(client->channel, &client->channel_cb); return client->channel; }
static ssh_channel pkd_channel_openreq_cb(ssh_session s, void *userdata) { ssh_channel c = NULL; ssh_channel *out = (ssh_channel *) userdata; /* assumes pubkey authentication has already succeeded */ pkdout("pkd_channel_openreq_cb\n"); c = ssh_channel_new(s); if (c == NULL) { pkderr("ssh_channel_new: %s\n", ssh_get_error(s)); return NULL; } ssh_callbacks_init(&pkd_channel_cb); pkd_channel_cb.userdata = userdata; if (ssh_set_channel_callbacks(c, &pkd_channel_cb) != SSH_OK) { pkderr("ssh_set_channel_callbacks: %s\n", ssh_get_error(s)); ssh_channel_free(c); c = NULL; } *out = c; return c; }
static int message_callback(ssh_session session, ssh_message message, void *userdata) { ssh_channel channel; int socket_fd, *pFd; struct ssh_channel_callbacks_struct *cb_chan; struct event_fd_data_struct *event_fd_data; (void)session; (void)message; (void)userdata; _ssh_log(SSH_LOG_PACKET, "=== message_callback", "Message type: %d", ssh_message_type(message)); _ssh_log(SSH_LOG_PACKET, "=== message_callback", "Message Subtype: %d", ssh_message_subtype(message)); if (ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN) { _ssh_log(SSH_LOG_PROTOCOL, "=== message_callback", "channel_request_open"); if (ssh_message_subtype(message) == SSH_CHANNEL_DIRECT_TCPIP) { channel = ssh_message_channel_request_open_reply_accept(message); if (channel == NULL) { _ssh_log(SSH_LOG_WARNING, "=== message_callback", "Accepting direct-tcpip channel failed!"); return 1; } else { _ssh_log(SSH_LOG_PROTOCOL, "=== message_callback", "Connected to channel!"); socket_fd = open_tcp_socket(message); if (-1 == socket_fd) { return 1; } pFd = malloc(sizeof *pFd); cb_chan = malloc(sizeof *cb_chan); event_fd_data = malloc(sizeof *event_fd_data); (*pFd) = socket_fd; event_fd_data->channel = channel; event_fd_data->p_fd = pFd; event_fd_data->stacked = 0; event_fd_data->cb_chan = cb_chan; cb_chan->userdata = event_fd_data; cb_chan->channel_eof_function = my_channel_eof_function; cb_chan->channel_close_function = my_channel_close_function; cb_chan->channel_data_function = my_channel_data_function; cb_chan->channel_exit_status_function = my_channel_exit_status_function; ssh_callbacks_init(cb_chan); ssh_set_channel_callbacks(channel, cb_chan); ssh_event_add_fd(mainloop, (socket_t)*pFd, POLLIN, my_fd_data_function, event_fd_data); return 0; } } } return 1; }
static ssh_channel new_session_channel(ssh_session session, void *userdata){ (void) session; (void) userdata; if(chan != NULL) return NULL; printf("Allocated session channel\n"); chan = ssh_channel_new(session); ssh_callbacks_init(&channel_cb); ssh_set_channel_callbacks(chan, &channel_cb); return chan; }
static ssh_channel channel_open(ssh_session session, void *userdata) { ssh_channel channel = NULL; ssh_channel_callbacks channel_cb = userdata; /* unused */ (void)userdata; channel = ssh_channel_new(session); if (channel == NULL) { goto out; } ssh_set_channel_callbacks(channel, channel_cb); out: return channel; }
static int main_loop(ssh_channel chan) { ssh_session session = ssh_channel_get_session(chan); socket_t fd; struct termios *term = NULL; struct winsize *win = NULL; pid_t childpid; ssh_event event; short events; childpid = forkpty(&fd, NULL, term, win); if(childpid == 0) { execl("/bin/bash", "/bin/bash", (char *)NULL); abort(); } cb.userdata = &fd; ssh_callbacks_init(&cb); ssh_set_channel_callbacks(chan, &cb); events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; event = ssh_event_new(); if(event == NULL) { printf("Couldn't get a event\n"); return -1; } if(ssh_event_add_fd(event, fd, events, copy_fd_to_chan, chan) != SSH_OK) { printf("Couldn't add an fd to the event\n"); return -1; } if(ssh_event_add_session(event, session) != SSH_OK) { printf("Couldn't add the session to the event\n"); return -1; } do { ssh_event_dopoll(event, 1000); } while(!ssh_channel_is_closed(chan)); ssh_event_remove_fd(event, fd); ssh_event_remove_session(event, session); ssh_event_free(event); return 0; }
static ssh_channel new_session_channel( ssh_session session, void *userdata ) { SSHSession *s = (SSHSession *)userdata; (void) session; if( s->sshs_Chan != NULL) { ERROR("New session channel\n"); return NULL; } printf("Allocated session channel\n"); s->sshs_Chan = ssh_channel_new( session ); ssh_callbacks_init( &channel_cb ); ssh_set_channel_callbacks( s->sshs_Chan, &channel_cb ); return s->sshs_Chan; }
static int do_shell (ssh_event event, ssh_channel chan) { socket_t fd; struct termios *term = NULL; struct winsize *win = NULL; short events; int fd_status; state.childpid = forkpty (&fd, NULL, term, win); if (state.childpid == 0) { close (state.bind_fd); close (state.session_fd); execl ("/bin/bash", "/bin/bash", NULL); _exit (127); } else if (state.childpid < 0) { g_critical ("forkpty failed: %s", g_strerror (errno)); return -1; } fd_status = fcntl (fd, F_GETFL, 0); if (fcntl (fd, F_SETFL, fd_status | O_NONBLOCK) < 0) { g_critical ("couldn't set non-blocking mode"); return -1; } cb.userdata = (gpointer)(long)fd; ssh_callbacks_init(&cb); ssh_set_channel_callbacks (chan, &cb); events = POLLIN | POLLOUT | POLLPRI | POLLERR | POLLHUP | POLLNVAL; if (ssh_event_add_fd (event, fd, events, fd_data, chan) != SSH_OK) g_return_val_if_reached(-1); return 0; }
static int do_exec (ssh_event event, ssh_channel chan, const gchar *cmd) { socket_t fd; short events; fd = fork_exec (cmd); if (fd < 0) return -1; cb.userdata = GINT_TO_POINTER (fd); ssh_callbacks_init(&cb); ssh_set_channel_callbacks (chan, &cb); events = POLLIN | POLLOUT | POLLPRI | POLLERR | POLLHUP | POLLNVAL; if (ssh_event_add_fd (event, fd, events, fd_data, chan) != SSH_OK) g_return_val_if_reached(-1); return 0; }
void tmate_client_pty_init(struct tmate_session *session) { struct tmate_ssh_client *client = &session->ssh_client; ioctl(session->pty, TIOCSWINSZ, &session->ssh_client.winsize_pty); memset(&client->channel_cb, 0, sizeof(client->channel_cb)); ssh_callbacks_init(&client->channel_cb); client->channel_cb.userdata = session; client->channel_cb.channel_data_function = on_ssh_channel_read, ssh_set_channel_callbacks(client->channel, &client->channel_cb); ssh_set_message_callback(session->ssh_client.session, on_ssh_message_callback, session); setblocking(session->pty, 0); event_set(&session->ev_pty, session->pty, EV_READ | EV_PERSIST, __on_pty_event, session); event_add(&session->ev_pty, NULL); tmate_add_ssh_latency_callback(client, on_latency_callback, session); }
static gboolean cockpit_ssh_source_prepare (GSource *source, gint *timeout) { CockpitSshSource *cs = (CockpitSshSource *)source; CockpitSshTransport *self = cs->transport; GThread *thread; gint status; *timeout = 1; /* Connecting, check if done */ if (G_UNLIKELY (!self->data)) { if (g_atomic_int_get (&self->connecting)) return FALSE; g_object_ref (self); /* Get the result from connecting thread */ thread = self->connect_thread; self->connect_fd = -1; self->connect_thread = NULL; self->data = g_thread_join (thread); g_assert (self->data != NULL); if (!self->result_emitted) { self->result_emitted = TRUE; g_signal_emit_by_name (self, "result", self->data->problem); } if (self->data->problem) { close_immediately (self, self->data->problem); g_object_unref (self); return FALSE; } g_object_unref (self); ssh_event_add_session (self->event, self->data->session); ssh_set_channel_callbacks (self->data->channel, &self->channel_cbs); /* Start watching the fd */ ssh_set_blocking (self->data->session, 0); cs->pfd.fd = ssh_get_fd (self->data->session); g_source_add_poll (source, &cs->pfd); g_debug ("%s: starting io", self->logname); } status = ssh_get_status (self->data->session); /* Short cut this ... we're ready now */ if (self->drain_buffer) return TRUE; /* * Channel completely closed, and output buffers * are empty. We're in a good place to close the * SSH session and thus the transport. */ if (close_maybe (self, status)) return FALSE; cs->pfd.revents = 0; cs->pfd.events = G_IO_IN | G_IO_ERR | G_IO_NVAL | G_IO_HUP; /* libssh has something in its buffer: want to write */ if (status & SSH_WRITE_PENDING) cs->pfd.events |= G_IO_OUT; /* We have something in our queue: want to write */ else if (!g_queue_is_empty (self->queue)) cs->pfd.events |= G_IO_OUT; /* We are closing and need to send eof: want to write */ else if (self->closing && !self->sent_eof) cs->pfd.events |= G_IO_OUT; /* Need to reply to an EOF or close */ if ((self->received_eof && self->sent_eof && !self->sent_close) || (self->received_close && !self->sent_close)) cs->pfd.events |= G_IO_OUT; return cockpit_ssh_source_check (source); }
static void handle_session(ssh_event event, ssh_session session) { int n; int rc = 0; /* Structure for storing the pty size. */ struct winsize wsize = { .ws_row = 0, .ws_col = 0, .ws_xpixel = 0, .ws_ypixel = 0 }; /* Our struct holding information about the channel. */ struct channel_data_struct cdata = { .pid = 0, .pty_master = -1, .pty_slave = -1, .child_stdin = -1, .child_stdout = -1, .child_stderr = -1, .event = NULL, .winsize = &wsize }; /* Our struct holding information about the session. */ struct session_data_struct sdata = { .channel = NULL, .auth_attempts = 0, .authenticated = 0 }; struct ssh_channel_callbacks_struct channel_cb = { .userdata = &cdata, .channel_pty_request_function = pty_request, .channel_pty_window_change_function = pty_resize, .channel_shell_request_function = shell_request, .channel_exec_request_function = exec_request, .channel_data_function = data_function, .channel_subsystem_request_function = subsystem_request }; struct ssh_server_callbacks_struct server_cb = { .userdata = &sdata, .auth_password_function = auth_password, .channel_open_request_session_function = channel_open, }; if (authorizedkeys[0]) { server_cb.auth_pubkey_function = auth_publickey; ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY); } else ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD); ssh_callbacks_init(&server_cb); ssh_callbacks_init(&channel_cb); ssh_set_server_callbacks(session, &server_cb); if (ssh_handle_key_exchange(session) != SSH_OK) { fprintf(stderr, "%s\n", ssh_get_error(session)); return; } ssh_event_add_session(event, session); n = 0; while (sdata.authenticated == 0 || sdata.channel == NULL) { /* If the user has used up all attempts, or if he hasn't been able to * authenticate in 10 seconds (n * 100ms), disconnect. */ if (sdata.auth_attempts >= 3 || n >= 100) { return; } if (ssh_event_dopoll(event, 100) == SSH_ERROR) { fprintf(stderr, "%s\n", ssh_get_error(session)); return; } n++; } ssh_set_channel_callbacks(sdata.channel, &channel_cb); do { /* Poll the main event which takes care of the session, the channel and * even our child process's stdout/stderr (once it's started). */ if (ssh_event_dopoll(event, -1) == SSH_ERROR) { ssh_channel_close(sdata.channel); } /* If child process's stdout/stderr has been registered with the event, * or the child process hasn't started yet, continue. */ if (cdata.event != NULL || cdata.pid == 0) { continue; } /* Executed only once, once the child process starts. */ cdata.event = event; /* If stdout valid, add stdout to be monitored by the poll event. */ if (cdata.child_stdout != -1) { if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout, sdata.channel) != SSH_OK) { fprintf(stderr, "Failed to register stdout to poll context\n"); ssh_channel_close(sdata.channel); } } /* If stderr valid, add stderr to be monitored by the poll event. */ if (cdata.child_stderr != -1){ if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr, sdata.channel) != SSH_OK) { fprintf(stderr, "Failed to register stderr to poll context\n"); ssh_channel_close(sdata.channel); } } } while(ssh_channel_is_open(sdata.channel) && (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); close(cdata.pty_master); close(cdata.child_stdin); close(cdata.child_stdout); close(cdata.child_stderr); /* Remove the descriptors from the polling context, since they are now * closed, they will always trigger during the poll calls. */ ssh_event_remove_fd(event, cdata.child_stdout); ssh_event_remove_fd(event, cdata.child_stderr); /* If the child process exited. */ if (kill(cdata.pid, 0) < 0 && WIFEXITED(rc)) { rc = WEXITSTATUS(rc); ssh_channel_request_send_exit_status(sdata.channel, rc); /* If client terminated the channel or the process did not exit nicely, * but only if something has been forked. */ } else if (cdata.pid > 0) { kill(cdata.pid, SIGKILL); } ssh_channel_send_eof(sdata.channel); ssh_channel_close(sdata.channel); /* Wait up to 5 seconds for the client to terminate the session. */ for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { ssh_event_dopoll(event, 100); } } /* SIGCHLD handler for cleaning up dead children. */ static void sigchld_handler(int signo) { (void) signo; while (waitpid(-1, NULL, WNOHANG) > 0); } int main(int argc, char **argv) { ssh_bind sshbind; ssh_session session; ssh_event event; struct sigaction sa; int rc; /* Set up SIGCHLD handler. */ sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, NULL) != 0) { fprintf(stderr, "Failed to register SIGCHLD handler\n"); return 1; } rc = ssh_init(); if (rc < 0) { fprintf(stderr, "ssh_init failed\n"); return 1; } sshbind = ssh_bind_new(); if (sshbind == NULL) { fprintf(stderr, "ssh_bind_new failed\n"); return 1; } #ifdef HAVE_ARGP_H argp_parse(&argp, argc, argv, 0, 0, sshbind); #else (void) argc; (void) argv; set_default_keys(sshbind, 0, 0, 0); #endif /* HAVE_ARGP_H */ if(ssh_bind_listen(sshbind) < 0) { fprintf(stderr, "%s\n", ssh_get_error(sshbind)); return 1; } while (1) { session = ssh_new(); if (session == NULL) { fprintf(stderr, "Failed to allocate session\n"); continue; } /* Blocks until there is a new incoming connection. */ if(ssh_bind_accept(sshbind, session) != SSH_ERROR) { switch(fork()) { case 0: /* Remove the SIGCHLD handler inherited from parent. */ sa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sa, NULL); /* Remove socket binding, which allows us to restart the * parent process, without terminating existing sessions. */ ssh_bind_free(sshbind); event = ssh_event_new(); if (event != NULL) { /* Blocks until the SSH session ends by either * child process exiting, or client disconnecting. */ handle_session(event, session); ssh_event_free(event); } else { fprintf(stderr, "Could not create polling context\n"); } ssh_disconnect(session); ssh_free(session); exit(0); case -1: fprintf(stderr, "Failed to fork\n"); } } else { fprintf(stderr, "%s\n", ssh_get_error(sshbind)); } /* Since the session has been passed to a child fork, do some cleaning * up at the parent process. */ ssh_disconnect(session); ssh_free(session); } ssh_bind_free(sshbind); ssh_finalize(); return 0; }
int set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks callbacks) { ssh_callbacks_init(callbacks); return ssh_set_channel_callbacks(channel, callbacks); }