1 /* packet.c */ 2 /* packet building functions */ 3 /* 4 Copyright 2003 Aris Adamantiadis 5 6 This file is part of the SSH Library 7 8 The SSH Library is free software; you can redistribute it and/or modify 9 it under the terms of the GNU Lesser General Public License as published by 10 the Free Software Foundation; either version 2.1 of the License, or (at your 11 option) any later version. 12 13 The SSH Library is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 16 License for more details. 17 18 You should have received a copy of the GNU Lesser General Public License 19 along with the SSH Library; see the file COPYING. If not, write to 20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 21 MA 02111-1307, USA. */ 22 23 #include "libssh/priv.h" 24 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <unistd.h> 28 #include <string.h> 29 #include "libssh/ssh2.h" 30 #ifdef HAVE_NETDB_H 31 #include <netdb.h> 32 #endif 33 #ifdef HAVE_WINDOWS_H 34 #include <windows.h> 35 #endif 36 #ifdef HAVE_SYS_TYPES_H 37 #include <sys/types.h> 38 #endif 39 #ifdef HAVE_SYS_SOCKET_H 40 #include <sys/socket.h> 41 #endif 42 #include <errno.h> 43 #include "libssh/crypto.h" 44 45 /* XXX include selected mac size */ 46 static int macsize=SHA_DIGEST_LENGTH; 47 48 /* completeread will read blocking until len bytes have been read */ 49 static int completeread(int fd, void *buffer, int len){ 50 int r; 51 int total=0; 52 int toread=len; 53 while((r=recv(fd,buffer+total,toread,0))){ 54 if(r==-1) 55 return -1; 56 total += r; 57 toread-=r; 58 if(total==len) 59 return len; 60 if(r==0) 61 return 0; 62 } 63 return total ; /* connection closed */ 64 } 65 66 int packet_read(SSH_SESSION *session){ 67 u32 len; 68 void *packet=NULL; 69 char mac[30]; 70 char buffer[16]; 71 int be_read,i; 72 int to_be_read; 73 u8 padding; 74 unsigned int blocksize=(session->current_crypto?session->current_crypto->in_cipher->blocksize:8); 75 session->datatoread=0; /* clear the dataavailable flag */ 76 memset(&session->in_packet,0,sizeof(PACKET)); 77 if(session->in_buffer) 78 buffer_free(session->in_buffer); 79 session->in_buffer=buffer_new(); 80 81 be_read=completeread(session->fd,buffer,blocksize); 82 if(be_read!=blocksize){ 83 if(be_read<=0){ 84 session->alive=0; 85 close(session->fd); 86 session->fd=-1; 87 ssh_set_error((session->connected?session:NULL),SSH_FATAL, 88 (be_read==0)?"Connection closed by remote host" : "Error reading socket"); 89 return -1; 90 } 91 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): asked %d bytes, received %d",blocksize,be_read); 92 return -1; 93 } 94 len=packet_decrypt_len(session,buffer); 95 buffer_add_data(session->in_buffer,buffer,blocksize); 96 97 if(len> MAX_PACKET_LEN){ 98 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): Packet len too high(%uld %.8lx)",len,len); 99 return -1; 100 } 101 to_be_read=len-be_read+sizeof(u32); 102 if(to_be_read<0){ 103 /* remote sshd is trying to get me ?*/ 104 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"given numbers of bytes left to be read <0 (%d)!",to_be_read); 105 return -1; 106 } 107 /* handle the case in which the whole packet size = blocksize */ 108 if(to_be_read !=0){ 109 packet=malloc(to_be_read); 110 i=completeread(session->fd,packet,to_be_read); 111 if(i<=0){ 112 session->alive=0; 113 close(session->fd); 114 session->fd=-1; 115 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Server closed connection"); 116 return -1; 117 } 118 if(i!=to_be_read){ 119 free(packet); 120 packet=NULL; 121 ssh_say(3,"Read only %d, wanted %d\n",i,to_be_read); 122 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): read only %d, wanted %d",i,to_be_read); 123 return -1; 124 } 125 ssh_say(3,"Read a %d bytes packet\n",len); 126 buffer_add_data(session->in_buffer,packet,to_be_read); 127 free(packet); 128 } 129 if(session->current_crypto){ 130 packet_decrypt(session,buffer_get(session->in_buffer)+blocksize,buffer_get_len(session->in_buffer)-blocksize); 131 if((i=completeread(session->fd,mac,macsize))!=macsize){ 132 if(i<=0){ 133 session->alive=0; 134 close(session->fd); 135 session->fd=-1; 136 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Server closed connection"); 137 return -1; 138 } 139 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"read_packet(): wanted %d, had %d",i,macsize); 140 return -1; 141 } 142 if(packet_hmac_verify(session,session->in_buffer,mac)){ 143 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"HMAC error"); 144 return -1; 145 } 146 } 147 buffer_pass_bytes(session->in_buffer,sizeof(u32)); /*pass the size which has been processed before*/ 148 if(!buffer_get_u8(session->in_buffer,&padding)){ 149 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Packet too short to read padding"); 150 return -1; 151 } 152 ssh_say(3,"%hhd bytes padding\n",padding); 153 if(padding > buffer_get_rest_len(session->in_buffer)){ 154 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"invalid padding: %d (%d resting)",padding,buffer_get_rest_len(session->in_buffer)); 155 ssh_print_hexa("incrimined packet",buffer_get(session->in_buffer),buffer_get_len(session->in_buffer)); 156 return -1; 157 } 158 buffer_pass_bytes_end(session->in_buffer,padding); 159 #ifdef HAVE_LIBZ 160 if(session->current_crypto && session->current_crypto->do_compress_in){ 161 decompress_buffer(session,session->in_buffer); 162 } 163 #endif 164 session->recv_seq++; 165 return 0; 166 } 167 168 int packet_translate(SSH_SESSION *session){ 169 memset(&session->in_packet,0,sizeof(PACKET)); 170 if(!session->in_buffer) 171 return -1; 172 ssh_say(3,"Final size %d\n",buffer_get_rest_len(session->in_buffer)); 173 if(!buffer_get_u8(session->in_buffer,&session->in_packet.type)){ 174 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Packet too short to read type"); 175 return -1; 176 } 177 ssh_say(3,"type %hhd\n",session->in_packet.type); 178 session->in_packet.valid=1; 179 return 0; 180 } 181 182 static int atomic_write(int fd, void *buffer, int len){ 183 int written; 184 int total=0; 185 do { 186 written=send(fd,buffer,len,0); 187 if(written==0) 188 return 0; 189 if(written==-1) 190 return total; 191 total+=written; 192 len-=written; 193 buffer+=written; 194 } while (len > 0); 195 return total; 196 } 197 198 int packet_send(SSH_SESSION *session){ 199 char padstring[32]; 200 u32 finallen; 201 u8 padding; 202 u32 currentlen=buffer_get_len(session->out_buffer); 203 char *hmac; 204 int ret=0; 205 unsigned int blocksize=(session->current_crypto?session->current_crypto->out_cipher->blocksize:8); 206 ssh_say(3,"Writing on the wire a packet having %ld bytes before",currentlen); 207 #ifdef HAVE_LIBZ 208 if(session->current_crypto && session->current_crypto->do_compress_out){ 209 compress_buffer(session,session->out_buffer); 210 currentlen=buffer_get_len(session->out_buffer); 211 } 212 #endif 213 padding=(blocksize- ((currentlen+5) % blocksize)); 214 if(padding<4) 215 padding+=blocksize; 216 if(session->current_crypto) 217 ssh_get_random(padstring,padding); 218 else 219 memset(padstring,0,padding); 220 finallen=htonl(currentlen+padding+1); 221 ssh_say(3,",%d bytes after comp + %d padding bytes = %d bytes packet\n",currentlen,padding,(ntohl(finallen))); 222 buffer_add_data_begin(session->out_buffer,&padding,sizeof(u8)); 223 buffer_add_data_begin(session->out_buffer,&finallen,sizeof(u32)); 224 buffer_add_data(session->out_buffer,padstring,padding); 225 hmac=packet_encrypt(session,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer)); 226 if(hmac) 227 buffer_add_data(session->out_buffer,hmac,20); 228 if(atomic_write(session->fd,buffer_get(session->out_buffer),buffer_get_len(session->out_buffer))!=buffer_get_len(sessio n->out_buffer)){ 229 session->alive=0; 230 close(session->fd); 231 session->fd=-1; 232 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Writing packet : error on socket (or connection closed): %s", 233 strerror(errno)); 234 ret=-1; 235 } 236 session->send_seq++; 237 buffer_reinit(session->out_buffer); 238 return ret; 239 } 240 241 void packet_parse(SSH_SESSION *session){ 242 int type=session->in_packet.type; 243 u32 foo; 244 STRING *error_s; 245 char *error=NULL; 246 247 switch(type){ 248 case SSH2_MSG_DISCONNECT: 249 buffer_get_u32(session->in_buffer,&foo); 250 error_s=buffer_get_ssh_string(session->in_buffer); 251 if(error_s) 252 error=string_to_char(error_s); 253 ssh_say(2,"Received SSH_MSG_DISCONNECT\n"); 254 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"Received SSH_MSG_DISCONNECT : %s",error); 255 if(error_s){ 256 free(error_s); 257 free(error); 258 } 259 close(session->fd); 260 session->fd=-1; 261 session->alive=0; 262 return; 263 case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 264 case SSH2_MSG_CHANNEL_DATA: 265 case SSH2_MSG_CHANNEL_EXTENDED_DATA: 266 case SSH2_MSG_CHANNEL_REQUEST: 267 case SSH2_MSG_CHANNEL_EOF: 268 case SSH2_MSG_CHANNEL_CLOSE: 269 270 channel_handle(session,type); 271 case SSH2_MSG_IGNORE: 272 return; 273 default: 274 ssh_say(0,"Received unhandled msg %d\n",type); 275 } 276 } 277 int packet_wait(SSH_SESSION *session,int type,int blocking){ 278 while(1){ 279 if(packet_read(session)) 280 return -1; 281 if(packet_translate(session)) 282 return -1; 283 switch(session->in_packet.type){ 284 case SSH2_MSG_DISCONNECT: 285 packet_parse(session); 286 return -1; 287 case SSH2_MSG_CHANNEL_WINDOW_ADJUST: 288 case SSH2_MSG_CHANNEL_DATA: 289 case SSH2_MSG_CHANNEL_EXTENDED_DATA: 290 case SSH2_MSG_CHANNEL_REQUEST: 291 case SSH2_MSG_CHANNEL_EOF: 292 case SSH2_MSG_CHANNEL_CLOSE: 293 packet_parse(session); 294 break;; 295 case SSH2_MSG_IGNORE: 296 break; 297 default: 298 if(type && (type != session->in_packet.type)){ 299 ssh_set_error((session->connected?session:NULL),SSH_FATAL,"waitpacket(): Received a %d type packet, was waiting for a %d\n",session->in_packet.type,type); 300 return -1; 301 } 302 return 0; 303 } 304 if(blocking==0) 305 return 0; 306 } 307 return 0; 308 } 309 310 void packet_clear_out(SSH_SESSION *session){ 311 if(session->out_buffer) 312 buffer_reinit(session->out_buffer); 313 else 314 session->out_buffer=buffer_new(); 315 } |