1 /* client.c file */ 2 /* 3 Copyright 2003 Aris Adamantiadis 4 5 This file is part of the SSH Library 6 7 The SSH Library is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published by 9 the Free Software Foundation; either version 2.1 of the License, or (at your 10 option) any later version. 11 12 The SSH Library is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with the SSH Library; see the file COPYING. If not, write to 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 20 MA 02111-1307, USA. */ 21 22 #include "libssh/priv.h" 23 #include <stdio.h> 24 #include <unistd.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #ifdef HAVE_NETDB_H 28 #include <netdb.h> 29 #endif 30 #ifdef HAVE_WINDOWS_H 31 #include <windows.h> 32 #endif 33 #ifdef HAVE_SYS_TYPES_H 34 #include <sys/types.h> 35 #endif 36 #ifdef HAVE_SYS_SOCKET_H 37 #include <sys/socket.h> 38 #endif 39 #include "libssh/ssh2.h" 40 static void ssh_cleanup(SSH_SESSION *session); 41 #define set_status(opt,status) do {\ 42 if (opt->connect_status_function) \ 43 opt->connect_status_function(opt->connect_status_arg, status); \ 44 } while (0) 45 /* simply gets a banner from a socket */ 46 47 char *ssh_get_banner(SSH_SESSION *session){ 48 char buffer[128]; 49 int i = 0; 50 ssize_t read_ret; 51 52 while (i < 127) { 53 if((read_ret = recv(session->fd, &buffer[i], 1, 0))<=0){ 54 ssh_set_error(session,SSH_CONNECTION_LOST,"Remote host closed connection"); 55 return NULL; 56 } 57 if (buffer[i] == '\015') 58 buffer[i] = 0; 59 if (buffer[i] == '\012') { 60 buffer[i] = 0; 61 return strdup(buffer); 62 } 63 i++; 64 } 65 ssh_set_error(NULL,SSH_FATAL,"Too large banner"); 66 return NULL; 67 } 68 69 /* ssh_send_banner sends a SSH banner to the server */ 70 /* TODO select a banner compatible with server version */ 71 /* switch SSH1/1.5/2 */ 72 /* and quit when the server is SSH1 only */ 73 74 void ssh_send_banner(SSH_SESSION *session){ 75 char *banner=CLIENTBANNER ; 76 char buffer[128]; 77 if(session->options->clientbanner) 78 banner=session->options->clientbanner; 79 session->clientbanner=strdup(banner); 80 snprintf(buffer,128,"%s\r\n",session->clientbanner); 81 send(session->fd,buffer,strlen(buffer),0); 82 } 83 84 85 int dh_handshake(SSH_SESSION *session){ 86 STRING *e,*f,*pubkey,*signature; 87 packet_clear_out(session); 88 buffer_add_u8(session->out_buffer,SSH2_MSG_KEXDH_INIT); 89 dh_generate_x(session); 90 dh_generate_e(session); 91 e=dh_get_e(session); 92 buffer_add_ssh_string(session->out_buffer,e); 93 packet_send(session); 94 free(e); 95 if(packet_wait(session,SSH2_MSG_KEXDH_REPLY,1)) 96 return -1; 97 pubkey=buffer_get_ssh_string(session->in_buffer); 98 if(!pubkey){ 99 ssh_set_error(NULL,SSH_FATAL,"No public key in packet"); 100 return -1; 101 } 102 dh_import_pubkey(session,pubkey); 103 f=buffer_get_ssh_string(session->in_buffer); 104 if(!f){ 105 ssh_set_error(NULL,SSH_FATAL,"No F number in packet"); 106 return -1; 107 } 108 dh_import_f(session,f); 109 free(f); 110 if(!(signature=buffer_get_ssh_string(session->in_buffer))){ 111 ssh_set_error(NULL,SSH_FATAL,"No signature in packet"); 112 return -1; 113 } 114 115 dh_build_k(session); 116 packet_wait(session,SSH2_MSG_NEWKEYS,1); 117 ssh_say(2,"Got SSH_MSG_NEWKEYS\n"); 118 packet_clear_out(session); 119 buffer_add_u8(session->out_buffer,SSH2_MSG_NEWKEYS); 120 packet_send(session); 121 ssh_say(2,"SSH_MSG_NEWKEYS sent\n"); 122 make_sessionid(session); 123 /* set the cryptographic functions for the next crypto (it is needed for generate_session_keys for key lenghts) */ 124 if(crypt_set_algorithms(session)) 125 return -1; 126 generate_session_keys(session); 127 /* verify the host's signature. XXX do it sooner */ 128 if(signature_verify(session,signature)){ 129 free(signature); 130 return -1; 131 } 132 free(signature); /* forget it for now ... */ 133 /* once we got SSH2_MSG_NEWKEYS we can switch next_crypto and current_crypto */ 134 if(session->current_crypto) 135 crypto_free(session->current_crypto); 136 /* XXX later, include a function to change keys */ 137 session->current_crypto=session->next_crypto; 138 session->next_crypto=crypto_new(); 139 return 0; 140 } 141 142 int ssh_service_request(SSH_SESSION *session,char *service){ 143 STRING *service_s; 144 packet_clear_out(session); 145 buffer_add_u8(session->out_buffer,SSH2_MSG_SERVICE_REQUEST); 146 service_s=string_from_char(service); 147 buffer_add_ssh_string(session->out_buffer,service_s); 148 free(service_s); 149 packet_send(session); 150 ssh_say(3,"Sent SSH_MSG_SERVICE_REQUEST (service %s)\n",service); 151 if(packet_wait(session,SSH2_MSG_SERVICE_ACCEPT,1)){ 152 ssh_set_error(session,SSH_INVALID_DATA,"did not receive SERVICE_ACCEPT"); 153 return -1; 154 } 155 ssh_say(3,"Received SSH_MSG_SERVICE_ACCEPT (service %s)\n",service); 156 return 0; 157 } 158 159 SSH_SESSION *ssh_connect(SSH_OPTIONS *options){ 160 SSH_SESSION *session; 161 int fd; 162 if(!options){ 163 ssh_set_error(NULL,SSH_FATAL,"Null argument given to ssh_connect !"); 164 return NULL; 165 } 166 ssh_crypto_init(); 167 if(options->fd==-1 && !options->host){ 168 ssh_set_error(NULL,SSH_FATAL,"Hostname required"); 169 return NULL; 170 } 171 if(options->fd != -1) 172 fd=options->fd; 173 else 174 fd=ssh_connect_host(options->host,options->bindaddr,options->port, 175 options->timeout,options->timeout_usec); 176 if(fd<0) { 177 ssh_set_error(NULL,SSH_FATAL,"ssh_connect_host failed"); 178 return NULL; 179 } 180 set_status(options,0.2); 181 session=ssh_session_new(); 182 session->fd=fd; 183 session->alive=1; 184 session->options=options; 185 if(!(session->serverbanner=ssh_get_banner(session))){ 186 ssh_cleanup(session); 187 ssh_set_error(NULL,SSH_FATAL,"ssh_get_banner failed"); 188 return NULL; 189 } 190 set_status(options,0.4); 191 ssh_say(2,"banner : %s\n",session->serverbanner); 192 ssh_send_banner(session); 193 set_status(options,0.5); 194 if(ssh_get_kex(session,0)){ 195 ssh_disconnect(session); 196 ssh_set_error(NULL,SSH_FATAL,"ssh_get_kex failed"); 197 return NULL; 198 } 199 set_status(options,0.6); 200 list_kex(&session->server_kex); 201 if(set_kex(session)){ 202 ssh_disconnect(session); 203 ssh_set_error(NULL,SSH_FATAL,"set_kex failed"); 204 return NULL; 205 } 206 send_kex(session,0); 207 set_status(options,0.8); 208 if(dh_handshake(session)){ 209 ssh_disconnect(session); 210 ssh_set_error(NULL,SSH_FATAL,"dh_handshake failed"); 211 return NULL; 212 } 213 set_status(options,1.0); 214 session->connected=1; 215 return session; 216 } 217 218 static void ssh_cleanup(SSH_SESSION *session){ 219 int i; 220 if(session->serverbanner) 221 free(session->serverbanner); 222 if(session->clientbanner) 223 free(session->clientbanner); 224 if(session->in_buffer) 225 buffer_free(session->in_buffer); 226 if(session->out_buffer) 227 buffer_free(session->out_buffer); 228 if(session->banner) 229 free(session->banner); 230 if(session->options) 231 options_free(session->options); 232 if(session->current_crypto) 233 crypto_free(session->current_crypto); 234 if(session->next_crypto) 235 crypto_free(session->next_crypto); 236 237 // delete all channels 238 while(session->channels) 239 channel_free(session->channels); 240 if(session->client_kex.methods) 241 for(i=0;i<10;i++) 242 if(session->client_kex.methods[i]) 243 free(session->client_kex.methods[i]); 244 if(session->server_kex.methods) 245 for(i=0;i<10;++i) 246 if(session->server_kex.methods[i]) 247 free(session->server_kex.methods[i]); 248 free(session->client_kex.methods); 249 free(session->server_kex.methods); 250 memset(session,'X',sizeof(SSH_SESSION)); /* burn connection, it could hangs sensitive datas */ 251 free(session); 252 } 253 254 char *ssh_get_issue_banner(SSH_SESSION *session){ 255 if(!session->banner) 256 return NULL; 257 return string_to_char(session->banner); 258 } 259 260 void ssh_disconnect(SSH_SESSION *session){ 261 STRING *str; 262 if(session->fd!= -1) { 263 packet_clear_out(session); 264 buffer_add_u8(session->out_buffer,SSH2_MSG_DISCONNECT); 265 buffer_add_u32(session->out_buffer,htonl(SSH2_DISCONNECT_BY_APPLICATION)); 266 str=string_from_char("Bye Bye"); 267 buffer_add_ssh_string(session->out_buffer,str); 268 free(str); 269 packet_send(session); 270 close(session->fd); 271 session->fd=-1; 272 } 273 session->alive=0; 274 ssh_cleanup(session); 275 } 276 277 const char *ssh_copyright(){ 278 return LIBSSH_VERSION " (c) 2003-2004 Aris Adamantiadis (aris@0xbadc0de.be)" 279 " Distributed under the LGPL, please refer to COPYING file for informations" 280 " about your rights" ; 281 } |