5773053 [rkeene@sledge /home/rkeene/projects/libssh-win32/v0.11/src/libssh-0.11/libssh]$ cat -n keyfiles.c
  1 /* keyfiles.c */
  2 /* This part of the library handles private and public key files needed for publickey authentication,*/
  3 /* as well as servers public hashes verifications and certifications. Lot of code here handles openssh */
  4 /* implementations (key files aren't standardized yet). */
  5 
  6 /*
  7 Copyright 2003,04 Aris Adamantiadis
  8 
  9 This file is part of the SSH Library
 10 
 11 The SSH Library is free software; you can redistribute it and/or modify
 12 it under the terms of the GNU Lesser General Public License as published by
 13 the Free Software Foundation; either version 2.1 of the License, or (at your
 14 option) any later version.
 15 
 16 The SSH Library is distributed in the hope that it will be useful, but
 17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 19 License for more details.
 20 
 21 You should have received a copy of the GNU Lesser General Public License
 22 along with the SSH Library; see the file COPYING.  If not, write to
 23 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 24 MA 02111-1307, USA. */
 25 #include <stdio.h>
 26 #include <string.h>
 27 #include <errno.h>
 28 #include <unistd.h>
 29 #include <stdlib.h>
 30 #include <fcntl.h>
 31 #include <openssl/pem.h>
 32 #include <openssl/dsa.h>
 33 #include <openssl/err.h>
 34 #include <openssl/rsa.h>
 35 #include "libssh/priv.h"
 36 #define MAXLINESIZE 80
 37 
 38 static int default_get_password(char *buf, int size,int rwflag, char *descr){
 39     char *pass;
 40     char buffer[256];
 41     int len;
 42     snprintf(buffer,256,"Please enter passphrase for %s",descr);
 43     pass=getpass(buffer);
 44     snprintf(buf,size,"%s",buffer);
 45     len=strlen(buf);
 46     memset(pass,0,strlen(pass));
 47     return len;
 48 }
 49 
 50 /* in case the passphrase has been given in parameter */
 51 static int get_password_specified(char *buf,int size, int rwflag, char *password){
 52     snprintf(buf,size,"%s",password);
 53     return strlen(buf);
 54 }
 55 
 56 /* TODO : implement it to read both DSA and RSA at once */
 57 PRIVATE_KEY  *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){
 58     FILE *file=fopen(filename,"r");
 59     PRIVATE_KEY *privkey;
 60     DSA *dsa=NULL;
 61     RSA *rsa=NULL;
 62     if(!file){
 63         ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
 64         return NULL;
 65     }
 66     if(type==TYPE_DSS){
 67         if(!passphrase){
 68             if(session && session->options->passphrase_function)
 69                 dsa=PEM_read_DSAPrivateKey(file,NULL, session->options->passphrase_function,"DSA private key");
 70             else
 71                 dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)default_get_password, "DSA private key");
 72         }
 73         else
 74             dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
 75         fclose(file);
 76         if(!dsa){
 77             ssh_set_error(session,SSH_FATAL,"parsing private key %s :
	%s",filename,ERR_error_string(ERR_get_error(),NULL));
 78         return NULL;
 79         }
 80     }
 81     else if (type==TYPE_RSA){
 82         if(!passphrase){
 83             if(session && session->options->passphrase_function)
 84                 rsa=PEM_read_RSAPrivateKey(file,NULL, session->options->passphrase_function,"RSA private key");
 85             else
 86                 rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)default_get_password, "RSA private key");
 87         }
 88         else
 89             rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
 90         fclose(file);
 91         if(!rsa){
 92             ssh_set_error(session,SSH_FATAL,"parsing private key %s :
	%s",filename,ERR_error_string(ERR_get_error(),NULL));
 93         return NULL;
 94         }
 95     } else {
 96         ssh_set_error(session,SSH_FATAL,"Invalid private key type %d",type);
 97         return NULL;
 98     }    
 99     
100     privkey=malloc(sizeof(PRIVATE_KEY));
101     privkey->type=type;
102     privkey->dsa_priv=dsa;
103     privkey->rsa_priv=rsa;
104     return privkey;
105 }
106 
107 void private_key_free(PRIVATE_KEY *prv){
108     if(prv->dsa_priv)
109         DSA_free(prv->dsa_priv);
110     if(prv->rsa_priv)
111         RSA_free(prv->rsa_priv);
112     memset(prv,0,sizeof(PRIVATE_KEY));
113     free(prv);
114 }
115 
116 STRING *publickey_from_file(char *filename,int *_type){
117     BUFFER *buffer;
118     int type;
119     STRING *str;
120     char buf[4096]; /* noone will have bigger keys that that */
121                         /* where have i head that again ? */
122     int fd=open(filename,O_RDONLY);
123     int r;
124     char *ptr;
125     if(fd<0){
126         ssh_set_error(NULL,SSH_INVALID_REQUEST,"nonexistent public key file");
127         return NULL;
128     }
129     if(read(fd,buf,8)!=8){
130         close(fd);
131         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
132         return NULL;
133     }
134     buf[7]=0;
135     if(!strcmp(buf,"ssh-dss"))
136         type=TYPE_DSS;
137     else if (!strcmp(buf,"ssh-rsa"))
138         type=TYPE_RSA;
139     else {
140         close(fd);
141         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
142         return NULL;
143     }
144     r=read(fd,buf,sizeof(buf)-1);
145     close(fd);
146     if(r<=0){
147         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
148         return NULL;
149     }
150     buf[r]=0;
151     ptr=strchr(buf,' ');
152     if(ptr)
153         *ptr=0; /* eliminates the garbage at end of file */
154     buffer=base64_to_bin(buf);
155     if(buffer){
156         str=string_new(buffer_get_len(buffer));
157         string_fill(str,buffer_get(buffer),buffer_get_len(buffer));
158         buffer_free(buffer);
159         if(_type)
160             *_type=type;
161         return str;
162     } else {
163         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
164         return NULL; /* invalid file */
165     }
166 }
167 
168 
169 /* why recursing ? i'll explain. on top, publickey_from_next_file will be executed until NULL returned */
170 /* we can't return null if one of the possible keys is wrong. we must test them before getting over */
171 STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path,
172                             char **privkeyfile,int *type,int *count){
173     static char *home=NULL;
174     char public[256];
175     char private[256];
176     char *priv;
177     char *pub;
178     STRING *pubkey;
179     if(!home)
180         home=ssh_get_user_home_dir();
181     if(home==NULL) {
182         ssh_set_error(session,SSH_FATAL,"User home dir impossible to guess");
183         return NULL;
184     }
185     ssh_set_error(session,SSH_NO_ERROR,"no public key matched");
186     if((pub=pub_keys_path[*count])==NULL)
187         return NULL;
188     if((priv=keys_path[*count])==NULL)
189         return NULL;
190     ++*count;
191     /* are them readable ? */
192     snprintf(public,256,pub,home);
193     ssh_say(2,"Trying to open %s\n",public);
194     if(!ssh_file_readaccess_ok(public)){
195         ssh_say(2,"Failed\n");
196         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
197     } 
198     snprintf(private,256,priv,home);
199     ssh_say(2,"Trying to open %s\n",private);
200     if(!ssh_file_readaccess_ok(private)){
201         ssh_say(2,"Failed\n");
202         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
203     }
204     ssh_say(2,"Okay both files ok\n");
205     /* ok, we are sure both the priv8 and public key files are readable : we return the public one as a string,
206         and the private filename in arguments */
207     pubkey=publickey_from_file(public,type);
208     if(!pubkey){
209         ssh_say(2,"Wasn't able to open public key file %s : %s\n",public,ssh_get_error(session));
210         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
211     }
212     *privkeyfile=realloc(*privkeyfile,strlen(private)+1);
213     strcpy(*privkeyfile,private);
214     return pubkey;
215 }
216 
217 #define FOUND_OTHER ( (void *)-1)
218 #define FILE_NOT_FOUND ((void *)-2)
219 /* will return a token array containing [host,]ip keytype key */
220 /* NULL if no match was found, FOUND_OTHER if the match is on an other */
221 /* type of key (ie dsa if type was rsa) */
222 static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){
223     FILE *file=fopen(filename,"r");
224     char buffer[4096];
225     char *ptr;
226     char **tokens;
227     char **ret=NULL;
228     if(!file)
229         return FILE_NOT_FOUND;
230     while(fgets(buffer,sizeof(buffer),file)){
231         ptr=strchr(buffer,'\n');
232         if(ptr) *ptr=0;
233         if((ptr=strchr(buffer,'\r'))) *ptr=0;
234         if(!buffer[0])
235             continue; /* skip empty lines */
236         tokens=space_tokenize(buffer);
237         if(!tokens[0] || !tokens[1] || !tokens[2]){
238             /* it should have exactly 3 tokens */
239             free(tokens[0]);
240             free(tokens);
241             continue;
242         }
243         if(tokens[3]){
244             /* 3 tokens only, not four */
245             free(tokens[0]);
246             free(tokens);
247             continue;
248         }
249         ptr=tokens[0];
250         while(*ptr==' ')
251             ptr++; /* skip the initial spaces */
252         /* we allow spaces or ',' to follow the hostname. It's generaly an IP */
253         /* we don't care about ip, if the host key match there is no problem with ip */
254         if(strncasecmp(ptr,hostname,strlen(hostname))==0){
255             if(ptr[strlen(hostname)]==' ' || ptr[strlen(hostname)]=='\0' 
256                     || ptr[strlen(hostname)]==','){
257                 if(strcasecmp(tokens[1],type)==0){
258                     fclose(file);
259                     return tokens;
260                 } else {
261                     ret=FOUND_OTHER;
262                 }
263             }
264         }
265         /* not the good one */
266         free(tokens[0]);
267         free(tokens);
268     }
269     fclose(file);
270     /* we did not find */
271     return ret;
272 }
273 
274 /* public function to test if the server is known or not */
275 int ssh_is_server_known(SSH_SESSION *session){
276     char *pubkey_64;
277     BUFFER *pubkey_buffer;
278     STRING *pubkey=session->current_crypto->server_pubkey;
279     char **tokens;
280     options_default_known_hosts_file(session->options);
281     if(!session->options->host){
282         ssh_set_error(session,SSH_FATAL,"Can't verify host in known hosts if the hostname isn't known");
283         return SSH_SERVER_ERROR;
284     }
285     tokens=ssh_parse_knownhost(session->options->known_hosts_file,
286         session->options->host,session->current_crypto->server_pubkey_type);
287     if(tokens==NULL)
288         return SSH_SERVER_NOT_KNOWN;
289     if(tokens==FOUND_OTHER)
290         return SSH_SERVER_FOUND_OTHER;
291     if(tokens==FILE_NOT_FOUND){
292         ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : file %s not
	found",session->options->known_hosts_file);
293         return SSH_SERVER_ERROR;
294     }
295     /* ok we found some public key in known hosts file. now un-base64it */
296     /* Some time, we may verify the IP address did not change. I honestly think */
297     /* it's not an important matter as IP address are known not to be secure */
298     /* and the crypto stuff is enough to prove the server's identity */
299     pubkey_64=tokens[2];
300     pubkey_buffer=base64_to_bin(pubkey_64);
301     /* at this point, we may free the tokens */
302     free(tokens[0]);
303     free(tokens);
304     if(!pubkey_buffer){
305         ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : base 64 error");
306         return SSH_SERVER_ERROR;
307     }
308     if(buffer_get_len(pubkey_buffer)!=string_len(pubkey)){
309         buffer_free(pubkey_buffer);
310         return SSH_SERVER_KNOWN_CHANGED;
311     }
312     /* now test that they are identical */
313     if(memcmp(buffer_get(pubkey_buffer),pubkey->string,buffer_get_len(pubkey_buffer))!=0){
314         buffer_free(pubkey_buffer);
315         return SSH_SERVER_KNOWN_CHANGED;
316     }
317     buffer_free(pubkey_buffer);
318     return SSH_SERVER_KNOWN_OK;
319 }
320 
321 int ssh_write_knownhost(SSH_SESSION *session){
322     char *pubkey_64;
323     STRING *pubkey=session->current_crypto->server_pubkey;
324     char buffer[4096];
325     FILE *file;
326     options_default_known_hosts_file(session->options);
327     if(!session->options->host){
328         ssh_set_error(session,SSH_FATAL,"Cannot write host in known hosts if the hostname is unknown");
329         return -1;
330     }
331     /* a = append only */
332     file=fopen(session->options->known_hosts_file,"a");
333     if(!file){
334         ssh_set_error(session,SSH_FATAL,"Opening known host file %s for appending : %s",
335         session->options->known_hosts_file,strerror(errno));
336         return -1;
337     }
338     pubkey_64=bin_to_base64(pubkey->string,string_len(pubkey));
339     snprintf(buffer,sizeof(buffer),"%s %s
	%s\n",session->options->host,session->current_crypto->server_pubkey_type,pubkey_64);
340     free(pubkey_64);
341     fwrite(buffer,strlen(buffer),1,file);
342     fclose(file);
343     return 0;
344 }
5773054 [rkeene@sledge /home/rkeene/projects/libssh-win32/v0.11/src/libssh-0.11/libssh]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2005-03-04 19:54:59