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