Using ssh command from C-program with STDIN problem



Hello,
now my problem is a little bit hard. I need to transfer the public key of an
user to another machine. But the user has entered the password with my
software and I want to call ssh (or ssh-copy-id script).

Using pipes and execvp it is possible to call other programs, but not ssh,
if I want to use a STDIN pipe. I look to the source of ssh, but it is hard
to understand. I know, it is for security reasons, but...

Below is my source for test purpose. The source comes from rsync project - I
change it for this test. I wrote a small program readstdin.c, which
demonstrate, that all works. The source of readstdin.c is below.

The question is: how can I configure STDIN handle for using SSH? Or what is
the best way to do such a call from C program?

Thank you in advance
Manfred


====================== BEGIN execssh.c ====================================
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* stderror */
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h> /* for pid_t, off_t */
#include <sys/time.h>
#include <sys/wait.h> /* for waitpid() + wait() */
#include <time.h>
#include <sys/stat.h> /* umask */
#include <sys/socket.h>

#define NONBLOCK_FLAG O_NONBLOCK
typedef unsigned int uint;

pid_t piped_child(char **command, int *f_in, int *f_out);
int fd_pair(int fd[2]);
static int wait_child(pid_t pid);
static void set_nonblocking(int fd);
static void set_blocking(int fd);

int main(int argc, char** argv)
{
/* activate the first line to show, that all works: */
char* cmd[] =
{ "./readstdin", "root@xxxxxxxxxxxxx", "ls -al /usr/", NULL, NULL };

/* activate this line, to show the problem with ssh.
* Set your own user@host and the passowrd below! */
//char* cmd[] =
{ "/usr/bin/ssh", "-v", "thomas@xxxxxxxxxxxxx", "ls -al /usr/", NULL,
NULL };
int fin = 0;
int fout = 0;
pid_t pid = 0;
int result = 0;

pid = piped_child(cmd, &fin, &fout);
if(pid > 0) {
int n_chars;
char buffer[256];

result = fcntl(fin, F_GETFL); /*Read the file descriptor's flags.*/
printf("fd-F_GETFL von stdin (%d): %X\n", fin, result);

/* set your password for your host here: */
write(fout, "password\n", 9);
result = 0;
result = wait_child(pid);
do {
n_chars = read(fin, buffer, 256);
} while (n_chars < 0 && errno == EINTR);
printf("GELESEN: [%s] result: %d, errno: %d / %s\n", buffer, result,
errno, strerror(errno));
}
return 0;
}

pid_t piped_child(char **command, int *f_in, int *f_out)
{
pid_t pid;
int to_child_pipe[2];
int from_child_pipe[2];
int blocking_io = 0;
int orig_umask = 022;

if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
printf( "fd_pair failed, %d, %s", errno, "pipe");
exit(1);
}

pid = fork();
if (pid == -1) {
printf( "do_fork failed,%d, %s", errno, "fork");
exit(2);
}

if (pid == 0) {
if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
close(to_child_pipe[1]) < 0 ||
close(from_child_pipe[0]) < 0 ||
dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
printf( "dup2 failed %d, %s", errno, "Failed to
dup/close");
exit(3);
}
if (to_child_pipe[0] != STDIN_FILENO)
close(to_child_pipe[0]);
if (from_child_pipe[1] != STDOUT_FILENO)
close(from_child_pipe[1]);
umask(orig_umask);
set_blocking(STDIN_FILENO);
if (blocking_io > 0)
set_blocking(STDOUT_FILENO);
execvp(command[0], command);
printf("execvp failed %d, %s", errno, "Failed to exec ");
exit(4);
}

if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
printf("error %d, %s", errno, "Failed to close");
exit(6);
}

printf("set %d for f_in, and %d for f_out\n", from_child_pipe[0],
to_child_pipe[1]);
*f_in = from_child_pipe[0];
*f_out = to_child_pipe[1];

return pid;
}


int fd_pair(int fd[2])
{
int ret;
ret = pipe(fd);
if (ret == 0) {
set_nonblocking(fd[0]);
set_nonblocking(fd[1]);
}

return ret;
}

/**
* Set a fd into nonblocking mode
**/
static void set_nonblocking(int fd)
{
int val;

if ((val = fcntl(fd, F_GETFL, 0)) == -1)
return;
if (!(val & NONBLOCK_FLAG)) {
val |= NONBLOCK_FLAG;
fcntl(fd, F_SETFL, val);
}
}

/**
* Set a fd into blocking mode
**/
static void set_blocking(int fd)
{
int val;

if ((val = fcntl(fd, F_GETFL, 0)) == -1)
return;
if (val & NONBLOCK_FLAG) {
val &= ~NONBLOCK_FLAG;
fcntl(fd, F_SETFL, val);
}
}

static int wait_child(pid_t pid)
{
/* This function change errno - we need it in caller function */
struct timespec* ts;
struct timespec tsd;
uint maxwaittime = 0;
uint maxwaitmsecs = 0;/* microseconds (1000 is 1 second) */
uint timeout = 0;
pid_t wpid = 0;
int result = 0;
extern int errno;
ts = &tsd;
ts->tv_sec = 1; /* want to wait 1 Second in this test */
ts->tv_nsec = 0;
timeout = (ts->tv_sec * 1000) + (ts->tv_nsec / 1000);
ts->tv_sec = 0;
ts->tv_nsec = 10000000u;
/* wait for the end of child process */
while(!errno && maxwaitmsecs < timeout)
{
nanosleep(ts, 0);
maxwaittime += ts->tv_nsec;
while(maxwaittime >= 1000000u) {
maxwaitmsecs++;
maxwaittime -= 1000000u;
}
ts->tv_nsec = 10000000u;
result = 0;
do {
wpid = waitpid( pid, &result, WNOHANG );
} while( wpid < 0 && errno == EINTR );
if( wpid == pid ) {
break;
}
}
printf("wait_child: errno: %d, wpid: %u, pid: %u, result: %d\n",
errno, wpid, pid, result);
return result;
}
====================== EOF execssh.c ====================================

====================== BEGIN OF readstdin.c =============================
#include <stdio.h>
#include <errno.h>

int main(int ac, char** av)
{
int c, i;
char buf[200];
/* Read the first character of the line. */
c = fgetc (stdin);
i = 0;
if(c != '\n' && c != EOF) {
buf[i++] = (char) c;
}
while(c != '\n' && c != EOF) {
c = fgetc (stdin);
buf[i++] = (char) c;
if(i >198) {
break;
}
}
buf[i] = '\0';
fprintf(stderr, "Error? errno: %d\n", errno);
fprintf(stdout, "Readin: [%s]\n", buf);
return 0;
}
====================== END OF readstdin.c ===============================


.