1 /* client.c */ 2 /* 3 Copyright 2003 Aris Adamantiadis 4 5 This file is part of the SSH Library 6 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 10 program. 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. 13 */ 14 15 #include "libssh/priv.h" 16 17 #include <stdio.h> 18 #include <unistd.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #ifdef HAVE_TERMIOS_H 22 #include <termios.h> 23 #endif 24 25 #ifdef HAVE_SYS_SELECT_H 26 #include <sys/select.h> 27 #endif 28 #include <sys/time.h> 29 #ifdef HAVE_PTY_H 30 #include <pty.h> 31 #endif 32 #ifdef HAVE_SIGNAL_H 33 #include <signal.h> 34 #endif 35 #include <errno.h> 36 #include <libssh/libssh.h> 37 #include <libssh/sftp.h> 38 39 #include <fcntl.h> 40 41 #define MAXCMD 10 42 char *host; 43 char *user; 44 int sftp; 45 char *cmds[MAXCMD]; 46 #ifdef HAVE_TERMIOS_H 47 struct termios terminal; 48 #endif 49 void do_sftp(SSH_SESSION *session); 50 51 void add_cmd(char *cmd){ 52 int n; 53 for(n=0;cmds[n] && (n<MAXCMD);n++); 54 if(n==MAXCMD) 55 return; 56 cmds[n]=strdup(cmd); 57 } 58 59 void usage(){ 60 fprintf(stderr,"Usage : ssh [options] [login@]hostname\n" 61 "Options :\n" 62 " -l user : log in as user\n" 63 " -p port : connect to port\n" 64 " -d : use DSS to verify host public key\n" 65 " -r : use RSA to verify host public key\n"); 66 exit(0); 67 } 68 69 int opts(int argc, char **argv){ 70 int i; 71 if(strstr(argv[0],"sftp")) 72 sftp=1; 73 // for(i=0;i<argc;i++) 74 // printf("%d : %s\n",i,argv[i]); 75 /* insert your own arguments here */ 76 while((i=getopt(argc,argv,""))!=-1){ 77 switch(i){ 78 default: 79 fprintf(stderr,"unknown option %c\n",optopt); 80 usage(); 81 } 82 } 83 if(optind < argc) 84 host=argv[optind++]; 85 while(optind < argc) 86 add_cmd(argv[optind++]); 87 if(host==NULL) 88 usage(); 89 return 0; 90 } 91 92 void do_cleanup(){ 93 #ifdef HAVE_TCSETATTR 94 tcsetattr(0,TCSANOW,&terminal); 95 #endif 96 } 97 void do_exit(){ 98 do_cleanup(); 99 exit(0); 100 } 101 CHANNEL *chan; 102 int signal_delayed=0; 103 void setsignal(); 104 void sigwindowchanged(){ 105 signal_delayed=1; 106 } 107 void sizechanged(){ 108 #ifdef HAVE_PTY_H 109 struct winsize win = { 0, 0, 0, 0 }; 110 ioctl(1, TIOCGWINSZ, &win); 111 channel_change_pty_size(chan,win.ws_col, win.ws_row); 112 // printf("Changed pty size\n"); 113 setsignal(); 114 #endif 115 } 116 void setsignal(){ 117 #ifdef HAVE_SIGNAL 118 signal(SIGWINCH,sigwindowchanged); 119 signal_delayed=0; 120 #endif 121 } 122 123 void select_loop(SSH_SESSION *session, CHANNEL *channel){ 124 fd_set fds; 125 struct timeval timeout; 126 char buffer[10]; 127 BUFFER *readbuf = buffer_new(); 128 CHANNEL *channels[2] = {channel, NULL}; 129 CHANNEL *outchannel[2] = {NULL, NULL}; 130 int lus; 131 int eof = 0; 132 int ret; 133 while (channel) { 134 /* when a signal is caught, ssh_select will return 135 * with SSH_EINTR, which means it should be started 136 * again. It lets you handle the signal the faster you 137 * can, like in this window changed example. Of course, if 138 * your signal handler doesn't call libssh at all, you're 139 * free to handle signals directly in sighandler. 140 */ 141 do { 142 FD_ZERO(&fds); 143 if(!eof) { 144 FD_SET(fileno(stdin), &fds); 145 } 146 timeout.tv_sec = 30; 147 timeout.tv_usec = 0; 148 ret = ssh_select(channels,outchannel,fileno(stdin)+1,&fds,&timeout); 149 if (signal_delayed) { 150 sizechanged(); 151 } 152 } while (ret==SSH_EINTR); 153 if(FD_ISSET(fileno(stdin),&fds)){ 154 lus=fread(buffer,1,10,stdin); 155 if(lus){ 156 channel_write(channel,buffer,lus); 157 } 158 else{ 159 eof=1; 160 channel_send_eof(channel); 161 } 162 } 163 if(outchannel[0]){ 164 while(channel_poll(outchannel[0],0)){ 165 lus=channel_read(outchannel[0],readbuf,0,0); 166 if(lus==-1){ 167 ssh_say(0,"error reading channel : %s\n",ssh_get_error(session)); 168 return; 169 } 170 if(lus==0){ 171 ssh_say(1,"EOF received\n"); 172 } else 173 write(1,buffer_get(readbuf),lus); 174 } 175 while(channel_poll(outchannel[0],1)){ /* stderr */ 176 lus=channel_read(outchannel[0],readbuf,0,1); 177 if(lus==-1){ 178 ssh_say(0,"error reading channel : %s\n",ssh_get_error(session)); 179 return; 180 } 181 if(lus==0){ 182 ssh_say(1,"EOF received\n"); 183 } else 184 write(2,buffer_get(readbuf),lus); 185 } 186 } 187 if(!channel_is_open(channel)){ 188 channel_free(channel); 189 channel=NULL; 190 } 191 } 192 buffer_free(readbuf); 193 } 194 195 void shell(SSH_SESSION *session){ 196 CHANNEL *channel; 197 #ifdef HAVE_TERMIOS_H 198 struct termios terminal_local; 199 #endif 200 int interactive=isatty(0); 201 if(interactive){ 202 #ifdef HAVE_TCGETATTR 203 tcgetattr(0,&terminal_local); 204 memcpy(&terminal,&terminal_local,sizeof(struct termios)); 205 cfmakeraw(&terminal_local); 206 tcsetattr(0,TCSANOW,&terminal_local); 207 #endif 208 setsignal(); 209 } 210 #ifdef HAVE_SIGNAL 211 signal(SIGTERM,do_cleanup); 212 #endif 213 channel = channel_open_session(session); 214 chan=channel; 215 if(interactive){ 216 channel_request_pty(channel); 217 sizechanged(); 218 } 219 channel_request_shell(channel); 220 select_loop(session,channel); 221 } 222 void batch_shell(SSH_SESSION *session){ 223 CHANNEL *channel; 224 char buffer[1024]; 225 int i,s=0; 226 for(i=0;i<MAXCMD && cmds[i];++i) 227 s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]); 228 channel=channel_open_session(session); 229 if(channel_request_exec(channel,buffer)){ 230 printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session)); 231 return; 232 } 233 select_loop(session,channel); 234 } 235 236 /* it's just a proof of concept code for sftp, till i write a real documentation about it */ 237 void do_sftp(SSH_SESSION *session){ 238 SFTP_SESSION *sftp=sftp_new(session); 239 SFTP_DIR *dir; 240 SFTP_ATTRIBUTES *file; 241 SFTP_FILE *fichier; 242 SFTP_FILE *to; 243 int len=1; 244 int i; 245 char data[8000]; 246 if(!sftp){ 247 ssh_say(0,"sftp error initialising channel : %s\n",ssh_get_error(session)); 248 return; 249 } 250 if(sftp_init(sftp)){ 251 ssh_say(0,"error initialising sftp : %s\n",ssh_get_error(session)); 252 return; 253 } 254 /* the connection is made */ 255 /* opening a directory */ 256 dir=sftp_opendir(sftp,"./"); 257 if(!dir) { 258 ssh_say(0,"Directory not opened(%s)\n",ssh_get_error(session)); 259 return ; 260 } 261 /* reading the whole directory, file by file */ 262 while((file=sftp_readdir(sftp,dir))){ 263 ssh_say(0,"%30s(%.8lo) : %.5d.%.5d : %.10lld bytes\n",file->name,file->permissions,file->uid,file->gid,file->size); 264 sftp_attributes_free(file); 265 } 266 /* when file=NULL, an error has occured OR the directory listing is end of file */ 267 if(!sftp_dir_eof(dir)){ 268 ssh_say(0,"error : %s\n",ssh_get_error(session)); 269 return; 270 } 271 if(sftp_dir_close(dir)){ 272 ssh_say(0,"Error : %s\n",ssh_get_error(session)); 273 return; 274 } 275 /* this will open a file and copy it into your /home directory */ 276 /* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */ 277 278 fichier=sftp_open(sftp,"/usr/bin/ssh",O_RDONLY,NULL); 279 if(!fichier){ 280 ssh_say(0,"Error opening /usr/bin/ssh : %s\n",ssh_get_error(session)); 281 return; 282 } 283 /* open a file for writing... */ 284 to=sftp_open(sftp,"ssh-copy",O_WRONLY | O_CREAT,NULL); 285 if(!to){ 286 ssh_say(0,"Error opening ssh-copy for writing : %s\n",ssh_get_error(session)); 287 return; 288 } 289 while((len=sftp_read(fichier,data,4096)) > 0){ 290 if(sftp_write(to,data,len)!=len){ 291 ssh_say(0,"error writing %d bytes : %s\n",len,ssh_get_error(session)); 292 return; 293 } 294 } 295 printf("finished\n"); 296 if(len<0) 297 ssh_say(0,"Error reading file : %s\n",ssh_get_error(session)); 298 sftp_file_close(fichier); 299 sftp_file_close(to); 300 printf("fichiers fermés\n"); 301 to=sftp_open(sftp,"/tmp/grosfichier",O_WRONLY|O_CREAT,NULL); 302 for(i=0;i<1000;++i){ 303 len=sftp_write(to,data,8000); 304 printf("wrote %d bytes\n",len); 305 if(len != 8000){ 306 printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session)); 307 } 308 } 309 sftp_file_close(to); 310 /* close the sftp session */ 311 sftp_free(sftp); 312 printf("session sftp terminée\n"); 313 } 314 int auth_kbdint(SSH_SESSION *session){ 315 int err=ssh_userauth_kbdint(session,NULL,NULL); 316 char *name,*instruction,*prompt,*ptr; 317 char buffer[128]; 318 int i,n; 319 char echo; 320 while (err==SSH_AUTH_INFO){ 321 name=ssh_userauth_kbdint_getname(session); 322 instruction=ssh_userauth_kbdint_getinstruction(session); 323 n=ssh_userauth_kbdint_getnprompts(session); 324 if(strlen(name)>0) 325 printf("%s\n",name); 326 if(strlen(instruction)>0) 327 printf("%s\n",instruction); 328 for(i=0;i<n;++i){ 329 prompt=ssh_userauth_kbdint_getprompt(session,i,&echo); 330 if(echo){ 331 printf("%s",prompt); 332 fgets(buffer,sizeof(buffer),stdin); 333 buffer[sizeof(buffer)-1]=0; 334 if((ptr=strchr(buffer,'\n'))) 335 *ptr=0; 336 ssh_userauth_kbdint_setanswer(session,i,buffer); 337 memset(buffer,0,strlen(buffer)); 338 } else { 339 #ifdef HAVE_GETPASS 340 ptr=getpass(prompt); 341 #else 342 fprintf(stderr, "%s", prompt); 343 ptr = malloc(1024); 344 fgets(ptr, 1024, stdin); 345 if (strlen(ptr) > 0) { 346 if (ptr[strlen(ptr) - 1] == '\n') { 347 ptr[strlen(ptr) - 1] = '\0'; 348 } 349 } 350 #endif 351 ssh_userauth_kbdint_setanswer(session,i,ptr); 352 } 353 } 354 err=ssh_userauth_kbdint(session,NULL,NULL); 355 } 356 return err; 357 } 358 359 int main(int argc, char **argv){ 360 SSH_SESSION *session; 361 SSH_OPTIONS *options; 362 int auth=0; 363 char *password; 364 char *banner; 365 int state; 366 char buf[10]; 367 char hash[MD5_DIGEST_LEN]; 368 369 options=ssh_getopt(&argc, argv); 370 if(!options){ 371 fprintf(stderr,"Error : %s\n",ssh_get_error(NULL)); 372 usage(); 373 return -1; 374 } 375 opts(argc,argv); 376 #ifdef HAVE_SIGNAL 377 signal(SIGTERM,do_exit); 378 #endif 379 if(user) 380 options_set_username(options,user); 381 options_set_host(options,host); 382 session=ssh_connect(options); 383 if(!session){ 384 fprintf(stderr,"[4891] Connection failed : %s\n",ssh_get_error(NULL)); 385 return -1; 386 } 387 388 state=ssh_is_server_known(session); 389 switch(state){ 390 case SSH_SERVER_KNOWN_OK: 391 break; /* ok */ 392 case SSH_SERVER_KNOWN_CHANGED: 393 fprintf(stderr,"Host key for server changed : server's one is now :\n"); 394 ssh_get_pubkey_hash(session,hash); 395 ssh_print_hexa("Public key hash",hash,MD5_DIGEST_LEN); 396 fprintf(stderr,"For security reason, connection will be stopped\n"); 397 ssh_disconnect(session); 398 exit(-1); 399 case SSH_SERVER_FOUND_OTHER: 400 fprintf(stderr,"The host key for this server was not found but an other type of key exists.\n"); 401 fprintf(stderr,"An attacker might change the default server key to confuse your client" 402 "into thinking the key does not exist\n" 403 "We advise you to rerun the client with -d or -r for more safety.\n"); 404 ssh_disconnect(session); 405 exit(-1); 406 case SSH_SERVER_NOT_KNOWN: 407 fprintf(stderr,"The server is unknown. Do you trust the host key ?\n"); 408 ssh_get_pubkey_hash(session,hash); 409 ssh_print_hexa("Public key hash",hash,MD5_DIGEST_LEN); 410 fgets(buf,sizeof(buf),stdin); 411 if(strncasecmp(buf,"yes",3)!=0){ 412 ssh_disconnect(session); 413 exit(-1); 414 } 415 fprintf(stderr,"This new key will be written on disk for further usage. do you agree ?\n"); 416 fgets(buf,sizeof(buf),stdin); 417 if(strncasecmp(buf,"yes",3)==0){ 418 if(ssh_write_knownhost(session)) 419 fprintf(stderr,"error %s\n",ssh_get_error(session)); 420 } 421 422 break; 423 case SSH_SERVER_ERROR: 424 fprintf(stderr,"%s",ssh_get_error(session)); 425 ssh_disconnect(session); 426 exit(-1); 427 } 428 429 /* no ? you should :) */ 430 auth=ssh_userauth_autopubkey(session); 431 if(auth==SSH_AUTH_ERROR){ 432 fprintf(stderr,"Authenticating with pubkey: %s\n",ssh_get_error(session)); 433 return -1; 434 } 435 banner=ssh_get_issue_banner(session); 436 if(banner){ 437 printf("%s\n",banner); 438 free(banner); 439 } 440 if(auth!=SSH_AUTH_SUCCESS){ 441 auth=auth_kbdint(session); 442 if(auth==SSH_AUTH_ERROR){ 443 fprintf(stderr,"authenticating with keyb-interactive: %s\n", 444 ssh_get_error(session)); 445 return -1; 446 } 447 } 448 if(auth!=SSH_AUTH_SUCCESS){ 449 #ifdef HAVE_GETPASS 450 password=getpass("Password : "); 451 #else 452 fprintf(stderr, "Password : "); 453 password = malloc(1024); 454 fgets(password, 1024, stdin); 455 if (strlen(password) > 0) { 456 if (password[strlen(password) - 1] == '\n') { 457 password[strlen(password) - 1] = '\0'; 458 } 459 } 460 #endif 461 if(ssh_userauth_password(session,NULL,password) != SSH_AUTH_SUCCESS){ 462 fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(session)); 463 ssh_disconnect(session); 464 return -1; 465 } 466 memset(password,0,strlen(password)); 467 } 468 ssh_say(1,"Authentication success\n"); 469 if(!sftp){ 470 if(!cmds[0]) 471 shell(session); 472 else 473 batch_shell(session); 474 } 475 else 476 do_sftp(session); 477 if(!sftp && !cmds[0]) 478 do_cleanup(); 479 ssh_disconnect(session); 480 return 0; 481 } sample.c is a sample program using libssh (v0.11 for Win32) |