3
Copyright 2003 Aris Adamantiadis
5
This file is part of the SSH Library
7
You are free to copy this file, modify it in any way, consider it being public
8
domain. This does not apply to the rest of the library though, but it is
9
allowed to cut-and-paste working code from this file to any license of
11
The goal is to show the API in action. It's not a reference on how terminal
12
clients must be made or how a client should react.
22
#include <sys/select.h>
27
#include <sys/ioctl.h>
30
#include <libssh/libssh.h>
31
#include <libssh/sftp.h>
40
struct termios terminal;
41
void do_sftp(SSH_SESSION *session);
43
static void add_cmd(char *cmd){
45
for(n=0;cmds[n] && (n<MAXCMD);n++);
52
fprintf(stderr,"Usage : ssh [options] [login@]hostname\n"
53
"sample client - libssh-%s\n"
55
" -l user : log in as user\n"
56
" -p port : connect to port\n"
57
" -d : use DSS to verify host public key\n"
58
" -r : use RSA to verify host public key\n",
63
static int opts(int argc, char **argv){
65
if(strstr(argv[0],"sftp"))
67
// for(i=0;i<argc;i++)
68
// printf("%d : %s\n",i,argv[i]);
69
/* insert your own arguments here */
70
while((i=getopt(argc,argv,""))!=-1){
73
fprintf(stderr,"unknown option %c\n",optopt);
80
add_cmd(argv[optind++]);
86
#ifndef HAVE_CFMAKERAW
87
static void cfmakeraw(struct termios *termios_p){
88
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
89
termios_p->c_oflag &= ~OPOST;
90
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
91
termios_p->c_cflag &= ~(CSIZE|PARENB);
92
termios_p->c_cflag |= CS8;
97
static void do_cleanup(int i) {
101
tcsetattr(0,TCSANOW,&terminal);
104
static void do_exit(int i) {
105
/* unused variable */
113
int signal_delayed=0;
115
static void sigwindowchanged(int i){
120
static void setsignal(void){
121
signal(SIGWINCH, sigwindowchanged);
125
static void sizechanged(void){
126
struct winsize win = { 0, 0, 0, 0 };
127
ioctl(1, TIOCGWINSZ, &win);
128
channel_change_pty_size(chan,win.ws_col, win.ws_row);
129
// printf("Changed pty size\n");
132
static void select_loop(SSH_SESSION *session,CHANNEL *channel){
134
struct timeval timeout;
136
BUFFER *readbuf=buffer_new();
137
CHANNEL *channels[2];
143
/* when a signal is caught, ssh_select will return
144
* with SSH_EINTR, which means it should be started
145
* again. It lets you handle the signal the faster you
146
* can, like in this window changed example. Of course, if
147
* your signal handler doesn't call libssh at all, you're
148
* free to handle signals directly in sighandler.
156
FD_SET(ssh_get_fd(session),&fds);
157
maxfd=ssh_get_fd(session)+1;
158
ret=select(maxfd,&fds,NULL,NULL,&timeout);
161
if(FD_ISSET(0,&fds)){
162
lus=read(0,buffer,10);
164
channel_write(channel,buffer,lus);
167
channel_send_eof(channel);
170
if(FD_ISSET(ssh_get_fd(session),&fds)){
171
ssh_set_fd_toread(session);
173
channels[0]=channel; // set the first channel we want to read from
175
ret=channel_select(channels,NULL,NULL,NULL); // no specific timeout - just poll
178
} while (ret==EINTR || ret==SSH_EINTR);
180
// we already looked for input from stdin. Now, we are looking for input from the channel
182
if(channel && channel_is_closed(channel)){
183
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
185
channel_free(channel);
190
while(channel && channel_is_open(channel) && channel_poll(channel,0)){
191
lus=channel_read_buffer(channel,readbuf,0,0);
193
fprintf(stderr, "Error reading channel: %s\n",
194
ssh_get_error(session));
198
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
199
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
201
channel_free(channel);
202
channel=channels[0]=NULL;
204
write(1,buffer_get(readbuf),lus);
206
while(channel && channel_is_open(channel) && channel_poll(channel,1)){ /* stderr */
207
lus=channel_read_buffer(channel,readbuf,0,1);
209
fprintf(stderr, "Error reading channel: %s\n",
210
ssh_get_error(session));
214
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
215
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
216
channel_free(channel);
217
channel=channels[0]=NULL;
219
write(2,buffer_get(readbuf),lus);
222
if(channel && channel_is_closed(channel)){
223
channel_free(channel);
227
buffer_free(readbuf);
231
static void shell(SSH_SESSION *session){
233
struct termios terminal_local;
234
int interactive=isatty(0);
235
channel = channel_new(session);
237
tcgetattr(0,&terminal_local);
238
memcpy(&terminal,&terminal_local,sizeof(struct termios));
240
if(channel_open_session(channel)){
241
printf("error opening channel : %s\n",ssh_get_error(session));
246
channel_request_pty(channel);
249
if(channel_request_shell(channel)){
250
printf("Requesting shell : %s\n",ssh_get_error(session));
254
cfmakeraw(&terminal_local);
255
tcsetattr(0,TCSANOW,&terminal_local);
258
signal(SIGTERM,do_cleanup);
259
select_loop(session,channel);
262
static void batch_shell(SSH_SESSION *session){
266
for(i=0;i<MAXCMD && cmds[i];++i)
267
s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]);
268
channel=channel_new(session);
269
channel_open_session(channel);
270
if(channel_request_exec(channel,buffer)){
271
printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session));
274
select_loop(session,channel);
278
/* it's just a proof of concept code for sftp, till i write a real documentation about it */
279
void do_sftp(SSH_SESSION *session){
280
SFTP_SESSION *sftp_session=sftp_new(session);
282
SFTP_ATTRIBUTES *file;
289
fprintf(stderr, "sftp error initialising channel: %s\n",
290
ssh_get_error(session));
293
if(sftp_init(sftp_session)){
294
fprintf(stderr, "error initialising sftp: %s\n",
295
ssh_get_error(session));
298
/* the connection is made */
299
/* opening a directory */
300
dir=sftp_opendir(sftp_session,"./");
302
fprintf(stderr, "Directory not opened(%s)\n", ssh_get_error(session));
305
/* reading the whole directory, file by file */
306
while((file=sftp_readdir(sftp_session,dir))){
307
fprintf(stderr, "%30s(%.8o) : %.5d.%.5d : %.10llu bytes\n",
312
(long long unsigned int) file->size);
313
sftp_attributes_free(file);
315
/* when file=NULL, an error has occured OR the directory listing is end of file */
316
if(!sftp_dir_eof(dir)){
317
fprintf(stderr, "Error: %s\n", ssh_get_error(session));
320
if(sftp_closedir(dir)){
321
fprintf(stderr, "Error: %s\n", ssh_get_error(session));
324
/* this will open a file and copy it into your /home directory */
325
/* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */
327
fichier=sftp_open(sftp_session,"/usr/bin/ssh",O_RDONLY, 0);
329
fprintf(stderr, "Error opening /usr/bin/ssh: %s\n",
330
ssh_get_error(session));
333
/* open a file for writing... */
334
to=sftp_open(sftp_session,"ssh-copy",O_WRONLY | O_CREAT, 0);
336
fprintf(stderr, "Error opening ssh-copy for writing: %s\n",
337
ssh_get_error(session));
340
while((len=sftp_read(fichier,data,4096)) > 0){
341
if(sftp_write(to,data,len)!=len){
342
fprintf(stderr, "Error writing %d bytes: %s\n",
343
len, ssh_get_error(session));
347
printf("finished\n");
349
fprintf(stderr, "Error reading file: %s\n", ssh_get_error(session));
352
printf("fichiers ferm\n");
353
to=sftp_open(sftp_session,"/tmp/grosfichier",O_WRONLY|O_CREAT, 0644);
355
len=sftp_write(to,data,8000);
356
printf("wrote %d bytes\n",len);
358
printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session));
362
/* close the sftp session */
363
sftp_free(sftp_session);
364
printf("session sftp termin�\n");
368
static int auth_kbdint(SSH_SESSION *session){
369
int err=ssh_userauth_kbdint(session,NULL,NULL);
370
const char *name, *instruction, *prompt;
375
while (err==SSH_AUTH_INFO){
376
name=ssh_userauth_kbdint_getname(session);
377
instruction=ssh_userauth_kbdint_getinstruction(session);
378
n=ssh_userauth_kbdint_getnprompts(session);
381
if(strlen(instruction)>0)
382
printf("%s\n",instruction);
384
prompt=ssh_userauth_kbdint_getprompt(session,i,&echo);
387
fgets(buffer,sizeof(buffer),stdin);
388
buffer[sizeof(buffer)-1]=0;
389
if((ptr=strchr(buffer,'\n')))
391
if (ssh_userauth_kbdint_setanswer(session,i,buffer) < 0) {
392
return SSH_AUTH_ERROR;
394
memset(buffer,0,strlen(buffer));
397
if (ssh_userauth_kbdint_setanswer(session,i,ptr) < 0) {
398
return SSH_AUTH_ERROR;
402
err=ssh_userauth_kbdint(session,NULL,NULL);
407
int main(int argc, char **argv){
408
SSH_SESSION *session;
409
SSH_OPTIONS *options;
416
unsigned char *hash = NULL;
419
options=ssh_options_new();
420
if(ssh_options_getopt(options,&argc, argv)){
421
fprintf(stderr,"error parsing command line :%s\n",ssh_get_error(options));
425
signal(SIGTERM, do_exit);
428
if (ssh_options_set_username(options,user) < 0) {
429
ssh_options_free(options);
434
if (ssh_options_set_host(options,host) < 0) {
435
ssh_options_free(options);
439
ssh_set_options(session,options);
440
if(ssh_connect(session)){
441
fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
442
ssh_disconnect(session);
446
state=ssh_is_server_known(session);
448
hlen = ssh_get_pubkey_hash(session, &hash);
450
ssh_disconnect(session);
455
case SSH_SERVER_KNOWN_OK:
457
case SSH_SERVER_KNOWN_CHANGED:
458
fprintf(stderr,"Host key for server changed : server's one is now :\n");
459
ssh_print_hexa("Public key hash",hash, hlen);
461
fprintf(stderr,"For security reason, connection will be stopped\n");
462
ssh_disconnect(session);
465
case SSH_SERVER_FOUND_OTHER:
466
fprintf(stderr,"The host key for this server was not found but an other type of key exists.\n");
467
fprintf(stderr,"An attacker might change the default server key to confuse your client"
468
"into thinking the key does not exist\n"
469
"We advise you to rerun the client with -d or -r for more safety.\n");
470
ssh_disconnect(session);
473
case SSH_SERVER_FILE_NOT_FOUND:
474
fprintf(stderr,"Could not find known host file. If you accept the host key here,\n");
475
fprintf(stderr,"the file will be automatically created.\n");
476
/* fallback to SSH_SERVER_NOT_KNOWN behaviour */
477
case SSH_SERVER_NOT_KNOWN:
478
hexa = ssh_get_hexa(hash, hlen);
479
fprintf(stderr,"The server is unknown. Do you trust the host key ?\n");
480
fprintf(stderr, "Public key hash: %s\n", hexa);
482
fgets(buf,sizeof(buf),stdin);
483
if(strncasecmp(buf,"yes",3)!=0){
484
ssh_disconnect(session);
487
fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n");
488
fgets(buf,sizeof(buf),stdin);
489
if(strncasecmp(buf,"yes",3)==0){
490
if (ssh_write_knownhost(session) < 0) {
492
fprintf(stderr, "error %s\n", strerror(errno));
498
case SSH_SERVER_ERROR:
500
fprintf(stderr,"%s",ssh_get_error(session));
501
ssh_disconnect(session);
507
ssh_userauth_none(session, NULL);
509
auth = ssh_auth_list(session);
510
printf("auth: 0x%04x\n", auth);
511
printf("supported auth methods: ");
512
if (auth & SSH_AUTH_METHOD_PUBLICKEY) {
515
if (auth & SSH_AUTH_METHOD_INTERACTIVE) {
516
printf(", keyboard-interactive");
520
/* no ? you should :) */
521
auth=ssh_userauth_autopubkey(session, NULL);
522
if(auth==SSH_AUTH_ERROR){
523
fprintf(stderr,"Authenticating with pubkey: %s\n",ssh_get_error(session));
527
banner=ssh_get_issue_banner(session);
529
printf("%s\n",banner);
532
if(auth!=SSH_AUTH_SUCCESS){
533
auth=auth_kbdint(session);
534
if(auth==SSH_AUTH_ERROR){
535
fprintf(stderr,"authenticating with keyb-interactive: %s\n",
536
ssh_get_error(session));
541
if(auth!=SSH_AUTH_SUCCESS){
542
password=getpass("Password: ");
543
if(ssh_userauth_password(session,NULL,password) != SSH_AUTH_SUCCESS){
544
fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(session));
545
ssh_disconnect(session);
549
memset(password,0,strlen(password));
551
ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success");
552
if(strstr(argv[0],"sftp")){
554
ssh_log(session, SSH_LOG_FUNCTIONS, "Doing sftp instead");
560
batch_shell(session);
566
if(!sftp && !cmds[0])
568
ssh_disconnect(session);