diff options
Diffstat (limited to 'sample.c')
-rw-r--r-- | sample.c | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/sample.c b/sample.c new file mode 100644 index 00000000..42ae09d1 --- /dev/null +++ b/sample.c @@ -0,0 +1,434 @@ +/* client.c */ +/* +Copyright 2003 Aris Adamantiadis + +This file is part of the SSH Library + +You are free to copy this file, modify it in any way, consider it being public +domain. This does not apply to the rest of the library though, but it is +allowed to cut-and-paste working code from this file to any license of +program. +The goal is to show the API in action. It's not a reference on how terminal +clients must be made or how a client should react. +*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> + +#include <sys/select.h> +#include <sys/time.h> +#include <pty.h> +#include <signal.h> +#include <errno.h> +#include <libssh/libssh.h> +#include <libssh/sftp.h> + +#include <fcntl.h> + +#define MAXCMD 10 +char *host; +char *user; +int sftp; +char *cmds[MAXCMD]; +struct termios terminal; +void do_sftp(SSH_SESSION *session); + +void add_cmd(char *cmd){ + int n; + for(n=0;cmds[n] && (n<MAXCMD);n++); + if(n==MAXCMD) + return; + cmds[n]=strdup(cmd); +} + +void usage(){ + fprintf(stderr,"Usage : ssh [options] [login@]hostname\n" + "Options :\n" + " -l user : log in as user\n" + " -p port : connect to port\n" + " -d : use DSS to verify host public key\n" + " -r : use RSA to verify host public key\n"); + exit(0); +} + +int opts(int argc, char **argv){ + int i; + if(strstr(argv[0],"sftp")) + sftp=1; +// for(i=0;i<argc;i++) +// printf("%d : %s\n",i,argv[i]); + /* insert your own arguments here */ + while((i=getopt(argc,argv,""))!=-1){ + switch(i){ + default: + fprintf(stderr,"unknown option %c\n",optopt); + usage(); + } + } + if(optind < argc) + host=argv[optind++]; + while(optind < argc) + add_cmd(argv[optind++]); + if(host==NULL) + usage(); + return 0; +} + +void do_cleanup(){ + tcsetattr(0,TCSANOW,&terminal); +} +void do_exit(){ + do_cleanup(); + exit(0); +} +CHANNEL *chan; +int signal_delayed=0; +void setsignal(); +void sigwindowchanged(){ + signal_delayed=1; +} +void sizechanged(){ + struct winsize win = { 0, 0, 0, 0 }; + ioctl(1, TIOCGWINSZ, &win); + channel_change_pty_size(chan,win.ws_col, win.ws_row); +// printf("Changed pty size\n"); + setsignal(); +} +void setsignal(){ + signal(SIGWINCH,sigwindowchanged); + signal_delayed=0; +} + +void select_loop(SSH_SESSION *session,CHANNEL *channel){ + fd_set fds; + struct timeval timeout; + char buffer[10]; + BUFFER *readbuf=buffer_new(); + CHANNEL *channels[]={channel,NULL}; + CHANNEL *outchannel[2]; + int lus; + int eof=0; + int ret; + while(channel){ + /* when a signal is caught, ssh_select will return + * with SSH_EINTR, which means it should be started + * again. It lets you handle the signal the faster you + * can, like in this window changed example. Of course, if + * your signal handler doesn't call libssh at all, you're + * free to handle signals directly in sighandler. + */ + do{ + FD_ZERO(&fds); + if(!eof) + FD_SET(0,&fds); + timeout.tv_sec=30; + timeout.tv_usec=0; + ret=ssh_select(channels,outchannel,0+1,&fds,&timeout); + if(signal_delayed) + sizechanged(); + } while (ret==SSH_EINTR); + if(FD_ISSET(0,&fds)){ + lus=read(0,buffer,10); + if(lus){ + channel_write(channel,buffer,lus); + } + else{ + eof=1; + channel_send_eof(channel); + } + } + if(outchannel[0]){ + while(channel_poll(outchannel[0],0)){ + lus=channel_read(outchannel[0],readbuf,0,0); + if(lus==-1){ + ssh_say(0,"error reading channel : %s\n",ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_say(1,"EOF received\n"); + } else + write(1,buffer_get(readbuf),lus); + } + while(channel_poll(outchannel[0],1)){ /* stderr */ + lus=channel_read(outchannel[0],readbuf,0,1); + if(lus==-1){ + ssh_say(0,"error reading channel : %s\n",ssh_get_error(session)); + return; + } + if(lus==0){ + ssh_say(1,"EOF received\n"); + } else + write(2,buffer_get(readbuf),lus); + } + } + if(!channel_is_open(channel)){ + channel_free(channel); + channel=NULL; + } + } + buffer_free(readbuf); +} + +void shell(SSH_SESSION *session){ + CHANNEL *channel; + struct termios terminal_local; + int interactive=isatty(0); + if(interactive){ + tcgetattr(0,&terminal_local); + memcpy(&terminal,&terminal_local,sizeof(struct termios)); + cfmakeraw(&terminal_local); + tcsetattr(0,TCSANOW,&terminal_local); + setsignal(); + } + signal(SIGTERM,do_cleanup); + channel = channel_new(session); + channel_open_session(channel); + chan=channel; + if(interactive){ + channel_request_pty(channel); + sizechanged(); + } + channel_request_shell(channel); + select_loop(session,channel); +} + +void batch_shell(SSH_SESSION *session){ + CHANNEL *channel; + char buffer[1024]; + int i,s=0; + for(i=0;i<MAXCMD && cmds[i];++i) + s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]); + channel=channel_new(session); + channel_open_session(channel); + if(channel_request_exec(channel,buffer)){ + printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session)); + return; + } + select_loop(session,channel); +} + +/* it's just a proof of concept code for sftp, till i write a real documentation about it */ +void do_sftp(SSH_SESSION *session){ + SFTP_SESSION *sftp=sftp_new(session); + SFTP_DIR *dir; + SFTP_ATTRIBUTES *file; + SFTP_FILE *fichier; + SFTP_FILE *to; + int len=1; + int i; + char data[8000]; + if(!sftp){ + ssh_say(0,"sftp error initialising channel : %s\n",ssh_get_error(session)); + return; + } + if(sftp_init(sftp)){ + ssh_say(0,"error initialising sftp : %s\n",ssh_get_error(session)); + return; + } + /* the connection is made */ + /* opening a directory */ + dir=sftp_opendir(sftp,"./"); + if(!dir) { + ssh_say(0,"Directory not opened(%s)\n",ssh_get_error(session)); + return ; + } + /* reading the whole directory, file by file */ + while((file=sftp_readdir(sftp,dir))){ + ssh_say(0,"%30s(%.8lo) : %.5d.%.5d : %.10lld bytes\n",file->name,file->permissions,file->uid,file->gid,file->size); + sftp_attributes_free(file); + } + /* when file=NULL, an error has occured OR the directory listing is end of file */ + if(!sftp_dir_eof(dir)){ + ssh_say(0,"error : %s\n",ssh_get_error(session)); + return; + } + if(sftp_dir_close(dir)){ + ssh_say(0,"Error : %s\n",ssh_get_error(session)); + return; + } + /* this will open a file and copy it into your /home directory */ + /* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */ + + fichier=sftp_open(sftp,"/usr/bin/ssh",O_RDONLY,NULL); + if(!fichier){ + ssh_say(0,"Error opening /usr/bin/ssh : %s\n",ssh_get_error(session)); + return; + } + /* open a file for writing... */ + to=sftp_open(sftp,"ssh-copy",O_WRONLY | O_CREAT,NULL); + if(!to){ + ssh_say(0,"Error opening ssh-copy for writing : %s\n",ssh_get_error(session)); + return; + } + while((len=sftp_read(fichier,data,4096)) > 0){ + if(sftp_write(to,data,len)!=len){ + ssh_say(0,"error writing %d bytes : %s\n",len,ssh_get_error(session)); + return; + } + } + printf("finished\n"); + if(len<0) + ssh_say(0,"Error reading file : %s\n",ssh_get_error(session)); + sftp_file_close(fichier); + sftp_file_close(to); + printf("fichiers fermés\n"); + to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT,NULL); + for(i=0;i<1000;++i){ + len=sftp_write(to,data,8000); + printf("wrote %d bytes\n",len); + if(len != 8000){ + printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session)); + } + } + sftp_file_close(to); + /* close the sftp session */ + sftp_free(sftp); + printf("session sftp terminée\n"); +} + +int auth_kbdint(SSH_SESSION *session){ + int err=ssh_userauth_kbdint(session,NULL,NULL); + char *name,*instruction,*prompt,*ptr; + char buffer[128]; + int i,n; + char echo; + while (err==SSH_AUTH_INFO){ + name=ssh_userauth_kbdint_getname(session); + instruction=ssh_userauth_kbdint_getinstruction(session); + n=ssh_userauth_kbdint_getnprompts(session); + if(strlen(name)>0) + printf("%s\n",name); + if(strlen(instruction)>0) + printf("%s\n",instruction); + for(i=0;i<n;++i){ + prompt=ssh_userauth_kbdint_getprompt(session,i,&echo); + if(echo){ + printf("%s",prompt); + fgets(buffer,sizeof(buffer),stdin); + buffer[sizeof(buffer)-1]=0; + if((ptr=strchr(buffer,'\n'))) + *ptr=0; + ssh_userauth_kbdint_setanswer(session,i,buffer); + memset(buffer,0,strlen(buffer)); + } else { + ptr=getpass(prompt); + ssh_userauth_kbdint_setanswer(session,i,ptr); + } + } + err=ssh_userauth_kbdint(session,NULL,NULL); + } + return err; +} + +int main(int argc, char **argv){ + SSH_SESSION *session; + SSH_OPTIONS *options; + int auth=0; + char *password; + char *banner; + int state; + char buf[10]; + char hash[MD5_DIGEST_LEN]; + + options=ssh_options_new(); + if(ssh_options_getopt(options,&argc, argv)) + usage(); + opts(argc,argv); + signal(SIGTERM,do_exit); + if(user) + ssh_options_set_username(options,user); + ssh_options_set_host(options,host); + session=ssh_new(); + ssh_set_options(session,options); + if(ssh_connect(session)){ + fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session)); + ssh_disconnect(session); + return 1; + } + state=ssh_is_server_known(session); + switch(state){ + case SSH_SERVER_KNOWN_OK: + break; /* ok */ + case SSH_SERVER_KNOWN_CHANGED: + fprintf(stderr,"Host key for server changed : server's one is now :\n"); + ssh_get_pubkey_hash(session,hash); + ssh_print_hexa("Public key hash",hash,MD5_DIGEST_LEN); + fprintf(stderr,"For security reason, connection will be stopped\n"); + ssh_disconnect(session); + exit(-1); + case SSH_SERVER_FOUND_OTHER: + fprintf(stderr,"The host key for this server was not found but an other type of key exists.\n"); + fprintf(stderr,"An attacker might change the default server key to confuse your client" + "into thinking the key does not exist\n" + "We advise you to rerun the client with -d or -r for more safety.\n"); + ssh_disconnect(session); + exit(-1); + case SSH_SERVER_NOT_KNOWN: + fprintf(stderr,"The server is unknown. Do you trust the host key ?\n"); + ssh_get_pubkey_hash(session,hash); + ssh_print_hexa("Public key hash",hash,MD5_DIGEST_LEN); + fgets(buf,sizeof(buf),stdin); + if(strncasecmp(buf,"yes",3)!=0){ + ssh_disconnect(session); + exit(-1); + } + fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n"); + fgets(buf,sizeof(buf),stdin); + if(strncasecmp(buf,"yes",3)==0){ + if(ssh_write_knownhost(session)) + fprintf(stderr,"error %s\n",ssh_get_error(session)); + } + + break; + case SSH_SERVER_ERROR: + fprintf(stderr,"%s",ssh_get_error(session)); + ssh_disconnect(session); + exit(-1); + } + + /* no ? you should :) */ + auth=ssh_userauth_autopubkey(session); + if(auth==SSH_AUTH_ERROR){ + fprintf(stderr,"Authenticating with pubkey: %s\n",ssh_get_error(session)); + return -1; + } + banner=ssh_get_issue_banner(session); + if(banner){ + printf("%s\n",banner); + free(banner); + } + if(auth!=SSH_AUTH_SUCCESS){ + auth=auth_kbdint(session); + if(auth==SSH_AUTH_ERROR){ + fprintf(stderr,"authenticating with keyb-interactive: %s\n", + ssh_get_error(session)); + return -1; + } + } + if(auth!=SSH_AUTH_SUCCESS){ + password=getpass("Password : "); + if(ssh_userauth_password(session,NULL,password) != SSH_AUTH_SUCCESS){ + fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(session)); + ssh_disconnect(session); + return -1; + } + memset(password,0,strlen(password)); + } + ssh_say(1,"Authentication success\n"); + if(!sftp){ + if(!cmds[0]) + shell(session); + else + batch_shell(session); + } + else + do_sftp(session); + if(!sftp && !cmds[0]) + do_cleanup(); + ssh_disconnect(session); + + return 0; +} |