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