1 /* options.c */ 2 /* handle pre-connection options */ 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 #include "libssh/priv.h" 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <unistd.h> 26 #include <string.h> 27 #ifdef HAVE_PWD_H 28 #include <pwd.h> 29 #endif 30 #include <sys/types.h> 31 32 SSH_OPTIONS *options_new(){ 33 SSH_OPTIONS *option=malloc(sizeof(SSH_OPTIONS)); 34 memset(option,0,sizeof(SSH_OPTIONS)); 35 option->port=22; /* set the default port */ 36 option->fd=-1; 37 return option; 38 } 39 40 void options_set_port(SSH_OPTIONS *opt, unsigned int port){ 41 opt->port=port&0xffff; 42 } 43 SSH_OPTIONS *options_copy(SSH_OPTIONS *opt){ 44 SSH_OPTIONS *ret=options_new(); 45 int i; 46 ret->fd=opt->fd; 47 ret->port=opt->port; 48 if(opt->username) 49 ret->username=strdup(opt->username); 50 if(opt->host) 51 ret->host=strdup(opt->host); 52 if(opt->bindaddr) 53 ret->host=strdup(opt->bindaddr); 54 if(opt->identity) 55 ret->identity=strdup(opt->identity); 56 if(opt->ssh_dir) 57 ret->ssh_dir=strdup(opt->ssh_dir); 58 if(opt->known_hosts_file) 59 ret->known_hosts_file=strdup(opt->known_hosts_file); 60 for(i=0;i<10;++i) 61 if(opt->wanted_methods[i]) 62 ret->wanted_methods[i]=strdup(opt->wanted_methods[i]); 63 ret->passphrase_function=opt->passphrase_function; 64 ret->connect_status_function=opt->connect_status_function; 65 ret->connect_status_arg=opt->connect_status_arg; 66 ret->timeout=opt->timeout; 67 ret->timeout_usec=opt->timeout_usec; 68 return ret; 69 } 70 71 void options_free(SSH_OPTIONS *opt){ 72 int i; 73 if(opt->username) 74 free(opt->username); 75 if(opt->identity) 76 free(opt->identity); 77 /* we don't touch the banner. if the implementation did use it, they have to free it */ 78 if(opt->host) 79 free(opt->host); 80 if(opt->bindaddr) 81 free(opt->bindaddr); 82 if(opt->ssh_dir) 83 free(opt->ssh_dir); 84 for(i=0;i<10;i++) 85 if(opt->wanted_methods[i]) 86 free(opt->wanted_methods[i]); 87 memset(opt,0,sizeof(SSH_OPTIONS)); 88 free(opt); 89 } 90 91 92 void options_set_host(SSH_OPTIONS *opt, const char *hostname){ 93 char *ptr=strdup(hostname); 94 char *ptr2=strchr(ptr,'@'); 95 if(opt->host) // don't leak memory 96 free(opt->host); 97 if(ptr2){ 98 *ptr2=0; 99 opt->host=strdup(ptr2+1); 100 if(opt->username) 101 free(opt->username); 102 opt->username=strdup(ptr); 103 free(ptr); 104 } else 105 opt->host=ptr; 106 } 107 108 void options_set_fd(SSH_OPTIONS *opt, int fd){ 109 opt->fd=fd; 110 } 111 112 void options_set_bindaddr(SSH_OPTIONS *opt, char *bindaddr){ 113 opt->bindaddr=strdup(bindaddr); 114 } 115 116 void options_set_username(SSH_OPTIONS *opt,char *username){ 117 opt->username=strdup(username); 118 } 119 120 void options_set_ssh_dir(SSH_OPTIONS *opt, char *dir){ 121 char buffer[1024]; 122 snprintf(buffer,1024,dir,ssh_get_user_home_dir()); 123 opt->ssh_dir=strdup(buffer); 124 } 125 void options_set_known_hosts_file(SSH_OPTIONS *opt, char *dir){ 126 char buffer[1024]; 127 snprintf(buffer,1024,dir,ssh_get_user_home_dir()); 128 opt->known_hosts_file=strdup(buffer); 129 } 130 131 void options_set_identity(SSH_OPTIONS *opt, char *identity){ 132 char buffer[1024]; 133 snprintf(buffer,1024,identity,ssh_get_user_home_dir()); 134 opt->identity=strdup(buffer); 135 } 136 137 /* what's the deal here ? some options MUST be set before authentication or key exchange, 138 * otherwise default values are going to be used. what must be configurable : 139 * Public key certification method * 140 * key exchange method (dh-sha1 for instance)* 141 * c->s, s->c ciphers * 142 * c->s s->c macs * 143 * c->s s->c compression */ 144 145 /* they all return 0 if all went well, 1 or !=0 if not. the most common error is unmatched algo (unimplemented) */ 146 /* don't forget other errors can happen if no matching algo is found in sshd answer */ 147 148 int options_set_wanted_method(SSH_OPTIONS *opt,int method, char *list){ 149 if(method > 9 || method < 0){ 150 ssh_set_error(NULL,SSH_FATAL,"method %d out of range",method); 151 return -1; 152 } 153 if( (!opt->use_nonexisting_algo) && !verify_existing_algo(method,list)){ 154 ssh_set_error(NULL,SSH_FATAL,"Setting method : no algorithm for method \"%s\" (%s)\n",ssh_kex_nums[method],list); 155 return -1; 156 } 157 if(opt->wanted_methods[method]) 158 free(opt->wanted_methods[method]); 159 opt->wanted_methods[method]=strdup(list); 160 return 0; 161 } 162 163 static char *get_username_from_uid(int uid){ 164 #ifdef HAVE_GETPWENT 165 struct passwd *pwd; 166 char *user; 167 while((pwd=getpwent())){ 168 if(pwd->pw_uid == uid){ 169 user=strdup(pwd->pw_name); 170 endpwent(); 171 return user; 172 } 173 } 174 endpwent(); 175 #endif 176 ssh_set_error(NULL,SSH_FATAL,"uid %d doesn't exist !",uid); 177 return NULL; 178 } 179 180 /* this function must be called when no specific username has been asked. it has to guess it */ 181 int options_default_username(SSH_OPTIONS *opt){ 182 char *user; 183 if(opt->username) 184 return 0; 185 user=getenv("USER"); 186 if(user){ 187 opt->username=strdup(user); 188 return 0; 189 } 190 191 #ifdef HAVE_GETUID 192 user=get_username_from_uid(getuid()); 193 if(user){ 194 opt->username=user; 195 return 0; 196 } 197 #endif 198 return -1; 199 } 200 201 int options_default_ssh_dir(SSH_OPTIONS *opt){ 202 char buffer[256]; 203 if(opt->ssh_dir) 204 return 0; 205 snprintf(buffer,256,"%s/.ssh/",ssh_get_user_home_dir()); 206 opt->ssh_dir=strdup(buffer); 207 return 0; 208 } 209 210 int options_default_known_hosts_file(SSH_OPTIONS *opt){ 211 char buffer[1024]; 212 if(opt->known_hosts_file) 213 return 0; 214 options_default_ssh_dir(opt); 215 snprintf(buffer,1024,"%s/known_hosts",opt->ssh_dir); 216 opt->known_hosts_file=strdup(buffer); 217 return 0; 218 } 219 220 void options_set_status_callback(SSH_OPTIONS *opt, void (*callback)(void *arg, float status), void *arg ){ 221 opt->connect_status_function=callback; 222 opt->connect_status_arg=arg; 223 } 224 225 void options_set_timeout(SSH_OPTIONS *opt, long seconds,long usec){ 226 opt->timeout=seconds; 227 opt->timeout_usec=usec; 228 } 229 230 SSH_OPTIONS *ssh_getopt(int *argcptr, char **argv){ 231 int i; 232 int argc=*argcptr; 233 char *user=NULL; 234 int port=22; 235 int debuglevel=0; 236 int usersa=0; 237 int usedss=0; 238 int compress=0; 239 int cont=1; 240 char *cipher=NULL; 241 char *localaddr=NULL; 242 char *identity=NULL; 243 char **save=malloc(argc * sizeof(char *)); 244 int current=0; 245 246 int saveoptind=optind; /* need to save 'em */ 247 int saveopterr=opterr; 248 SSH_OPTIONS *options; 249 opterr=0; /* shut up getopt */ 250 while(cont && ((i=getopt(argc,argv,"c:i:Cl:p:vb:rd12"))!=-1)){ 251 252 switch(i){ 253 case 'l': 254 user=optarg; 255 break; 256 case 'p': 257 port=atoi(optarg)&0xffff; 258 break; 259 case 'v': 260 debuglevel++; 261 break; 262 case 'r': 263 usersa++; 264 break; 265 case 'd': 266 usedss++; 267 break; 268 case 'c': 269 cipher=optarg; 270 break; 271 case 'i': 272 identity=optarg; 273 break; 274 case 'b': 275 localaddr=optarg; 276 break; 277 case 'C': 278 compress++; 279 break; 280 case '2': 281 break; /* only ssh2 support till now */ 282 case '1': 283 ssh_set_error(NULL,SSH_FATAL,"libssh does not support SSH1 protocol"); 284 cont =0; 285 break; 286 default: 287 { 288 char opt[3]="- "; 289 opt[1]=optopt; 290 save[current++]=strdup(opt); 291 if(optarg) 292 save[current++]=argv[optind+1]; 293 } 294 } 295 } 296 opterr=saveopterr; 297 while(optind < argc) 298 save[current++]=argv[optind++]; 299 300 if(usersa && usedss){ 301 ssh_set_error(NULL,SSH_FATAL,"either RSA or DSS must be chosen"); 302 cont=0; 303 } 304 ssh_set_verbosity(debuglevel); 305 optind=saveoptind; 306 if(!cont){ 307 free(save); 308 return NULL; 309 } 310 /* first recopy the save vector into original's */ 311 for(i=0;i<current;i++) 312 argv[i+1]=save[i]; // don't erase argv[0] 313 argv[current+1]=NULL; 314 *argcptr=current+1; 315 free(save); 316 /* set a new option struct */ 317 options=options_new(); 318 if(compress){ 319 if(options_set_wanted_method(options,KEX_COMP_C_S,"zlib")) 320 cont=0; 321 if(options_set_wanted_method(options,KEX_COMP_S_C,"zlib")) 322 cont=0; 323 } 324 if(cont &&cipher){ 325 if(options_set_wanted_method(options,KEX_CRYPT_C_S,cipher)) 326 cont=0; 327 if(cont && options_set_wanted_method(options,KEX_CRYPT_S_C,cipher)) 328 cont=0; 329 } 330 if(cont && usersa) 331 if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-rsa")) 332 cont=0; 333 if(cont && usedss) 334 if(options_set_wanted_method(options,KEX_HOSTKEY,"ssh-dss")) 335 cont=0; 336 if(cont && user) 337 options_set_username(options,user); 338 if(cont && identity) 339 options_set_identity(options,identity); 340 if(cont && localaddr) 341 options_set_bindaddr(options,localaddr); 342 options_set_port(options,port); 343 if(!cont){ 344 options_free(options); 345 return NULL; 346 } else 347 return options; 348 } |