5773051 [rkeene@sledge /home/rkeene/projects/libssh-win32/v0.11/src/libssh-0.11-win32/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 #ifdef HAVE_GETPASS
 44     pass=getpass(buffer);
 45     snprintf(buf,size,"%s",buffer);
 46     memset(pass,0,strlen(pass));
 47 #else
 48     fgets(buf, size, stdin);
 49     if (strlen(buf) > 0) {
 50         if (buf[strlen(buf) - 1] == '\n') {
 51              buf[strlen(buf) - 1] = '\0';
 52         }
 53     }
 54 #endif
 55     len=strlen(buf);
 56     return len;
 57 }
 58 
 59 /* in case the passphrase has been given in parameter */
 60 static int get_password_specified(char *buf,int size, int rwflag, char *password){
 61     snprintf(buf,size,"%s",password);
 62     return strlen(buf);
 63 }
 64 
 65 /* TODO : implement it to read both DSA and RSA at once */
 66 PRIVATE_KEY  *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){
 67     FILE *file=fopen(filename,"r");
 68     PRIVATE_KEY *privkey;
 69     DSA *dsa=NULL;
 70     RSA *rsa=NULL;
 71     if(!file){
 72         ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
 73         return NULL;
 74     }
 75     if(type==TYPE_DSS){
 76         if(!passphrase){
 77             if(session && session->options->passphrase_function)
 78                 dsa=PEM_read_DSAPrivateKey(file,NULL, session->options->passphrase_function,"DSA private key");
 79             else
 80                 dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)default_get_password, "DSA private key");
 81         }
 82         else
 83             dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
 84         fclose(file);
 85         if(!dsa){
 86             ssh_set_error(session,SSH_FATAL,"parsing private key %s :
	%s",filename,ERR_error_string(ERR_get_error(),NULL));
 87         return NULL;
 88         }
 89     }
 90     else if (type==TYPE_RSA){
 91         if(!passphrase){
 92             if(session && session->options->passphrase_function)
 93                 rsa=PEM_read_RSAPrivateKey(file,NULL, session->options->passphrase_function,"RSA private key");
 94             else
 95                 rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)default_get_password, "RSA private key");
 96         }
 97         else
 98             rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase);
 99         fclose(file);
100         if(!rsa){
101             ssh_set_error(session,SSH_FATAL,"parsing private key %s :
	%s",filename,ERR_error_string(ERR_get_error(),NULL));
102         return NULL;
103         }
104     } else {
105         ssh_set_error(session,SSH_FATAL,"Invalid private key type %d",type);
106         return NULL;
107     }    
108     
109     privkey=malloc(sizeof(PRIVATE_KEY));
110     privkey->type=type;
111     privkey->dsa_priv=dsa;
112     privkey->rsa_priv=rsa;
113     return privkey;
114 }
115 
116 void private_key_free(PRIVATE_KEY *prv){
117     if(prv->dsa_priv)
118         DSA_free(prv->dsa_priv);
119     if(prv->rsa_priv)
120         RSA_free(prv->rsa_priv);
121     memset(prv,0,sizeof(PRIVATE_KEY));
122     free(prv);
123 }
124 
125 STRING *publickey_from_file(char *filename,int *_type){
126     BUFFER *buffer;
127     int type;
128     STRING *str;
129     char buf[4096]; /* noone will have bigger keys that that */
130                         /* where have i head that again ? */
131     int fd=open(filename,O_RDONLY);
132     int r;
133     char *ptr;
134     if(fd<0){
135         ssh_set_error(NULL,SSH_INVALID_REQUEST,"nonexistent public key file");
136         return NULL;
137     }
138     if(read(fd,buf,8)!=8){
139         close(fd);
140         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
141         return NULL;
142     }
143     buf[7]=0;
144     if(!strcmp(buf,"ssh-dss"))
145         type=TYPE_DSS;
146     else if (!strcmp(buf,"ssh-rsa"))
147         type=TYPE_RSA;
148     else {
149         close(fd);
150         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
151         return NULL;
152     }
153     r=read(fd,buf,sizeof(buf)-1);
154     close(fd);
155     if(r<=0){
156         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
157         return NULL;
158     }
159     buf[r]=0;
160     ptr=strchr(buf,' ');
161     if(ptr)
162         *ptr=0; /* eliminates the garbage at end of file */
163     buffer=base64_to_bin(buf);
164     if(buffer){
165         str=string_new(buffer_get_len(buffer));
166         string_fill(str,buffer_get(buffer),buffer_get_len(buffer));
167         buffer_free(buffer);
168         if(_type)
169             *_type=type;
170         return str;
171     } else {
172         ssh_set_error(NULL,SSH_INVALID_REQUEST,"Invalid public key file");
173         return NULL; /* invalid file */
174     }
175 }
176 
177 
178 /* why recursing ? i'll explain. on top, publickey_from_next_file will be executed until NULL returned */
179 /* we can't return null if one of the possible keys is wrong. we must test them before getting over */
180 STRING *publickey_from_next_file(SSH_SESSION *session,char **pub_keys_path,char **keys_path,
181                             char **privkeyfile,int *type,int *count){
182     static char *home=NULL;
183     char public[256];
184     char private[256];
185     char *priv;
186     char *pub;
187     STRING *pubkey;
188     if(!home)
189         home=ssh_get_user_home_dir();
190     if(home==NULL) {
191         ssh_set_error(session,SSH_FATAL,"User home dir impossible to guess");
192         return NULL;
193     }
194     ssh_set_error(session,SSH_NO_ERROR,"no public key matched");
195     if((pub=pub_keys_path[*count])==NULL)
196         return NULL;
197     if((priv=keys_path[*count])==NULL)
198         return NULL;
199     ++*count;
200     /* are them readable ? */
201     snprintf(public,256,pub,home);
202     ssh_say(2,"Trying to open %s\n",public);
203     if(!ssh_file_readaccess_ok(public)){
204         ssh_say(2,"Failed\n");
205         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
206     } 
207     snprintf(private,256,priv,home);
208     ssh_say(2,"Trying to open %s\n",private);
209     if(!ssh_file_readaccess_ok(private)){
210         ssh_say(2,"Failed\n");
211         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
212     }
213     ssh_say(2,"Okay both files ok\n");
214     /* ok, we are sure both the priv8 and public key files are readable : we return the public one as a string,
215         and the private filename in arguments */
216     pubkey=publickey_from_file(public,type);
217     if(!pubkey){
218         ssh_say(2,"Wasn't able to open public key file %s : %s\n",public,ssh_get_error(session));
219         return publickey_from_next_file(session,pub_keys_path,keys_path,privkeyfile,type,count);
220     }
221     *privkeyfile=realloc(*privkeyfile,strlen(private)+1);
222     strcpy(*privkeyfile,private);
223     return pubkey;
224 }
225 
226 #define FOUND_OTHER ( (void *)-1)
227 #define FILE_NOT_FOUND ((void *)-2)
228 /* will return a token array containing [host,]ip keytype key */
229 /* NULL if no match was found, FOUND_OTHER if the match is on an other */
230 /* type of key (ie dsa if type was rsa) */
231 static char **ssh_parse_knownhost(char *filename, char *hostname, char *type){
232     FILE *file=fopen(filename,"r");
233     char buffer[4096];
234     char *ptr;
235     char **tokens;
236     char **ret=NULL;
237     if(!file)
238         return FILE_NOT_FOUND;
239     while(fgets(buffer,sizeof(buffer),file)){
240         ptr=strchr(buffer,'\n');
241         if(ptr) *ptr=0;
242         if((ptr=strchr(buffer,'\r'))) *ptr=0;
243         if(!buffer[0])
244             continue; /* skip empty lines */
245         tokens=space_tokenize(buffer);
246         if(!tokens[0] || !tokens[1] || !tokens[2]){
247             /* it should have exactly 3 tokens */
248             free(tokens[0]);
249             free(tokens);
250             continue;
251         }
252         if(tokens[3]){
253             /* 3 tokens only, not four */
254             free(tokens[0]);
255             free(tokens);
256             continue;
257         }
258         ptr=tokens[0];
259         while(*ptr==' ')
260             ptr++; /* skip the initial spaces */
261         /* we allow spaces or ',' to follow the hostname. It's generaly an IP */
262         /* we don't care about ip, if the host key match there is no problem with ip */
263         if(strncasecmp(ptr,hostname,strlen(hostname))==0){
264             if(ptr[strlen(hostname)]==' ' || ptr[strlen(hostname)]=='\0' 
265                     || ptr[strlen(hostname)]==','){
266                 if(strcasecmp(tokens[1],type)==0){
267                     fclose(file);
268                     return tokens;
269                 } else {
270                     ret=FOUND_OTHER;
271                 }
272             }
273         }
274         /* not the good one */
275         free(tokens[0]);
276         free(tokens);
277     }
278     fclose(file);
279     /* we did not find */
280     return ret;
281 }
282 
283 /* public function to test if the server is known or not */
284 int ssh_is_server_known(SSH_SESSION *session){
285     char *pubkey_64;
286     BUFFER *pubkey_buffer;
287     STRING *pubkey=session->current_crypto->server_pubkey;
288     char **tokens;
289 
290     options_default_known_hosts_file(session->options);
291     if(!session->options->host){
292         ssh_set_error(session,SSH_FATAL,"Can't verify host in known hosts if the hostname isn't known");
293         return SSH_SERVER_ERROR;
294     }
295     tokens=ssh_parse_knownhost(session->options->known_hosts_file,
296         session->options->host,session->current_crypto->server_pubkey_type);
297     if(tokens==NULL)
298         return SSH_SERVER_NOT_KNOWN;
299     if(tokens==FOUND_OTHER)
300         return SSH_SERVER_FOUND_OTHER;
301     if(tokens==FILE_NOT_FOUND){
302 #if 0
303         ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : file %s not
	found",session->options->known_hosts_file);
304         return SSH_SERVER_ERROR;
305 #else
306     return SSH_SERVER_KNOWN_OK;
307 #endif
308     }
309     /* ok we found some public key in known hosts file. now un-base64it */
310     /* Some time, we may verify the IP address did not change. I honestly think */
311     /* it's not an important matter as IP address are known not to be secure */
312     /* and the crypto stuff is enough to prove the server's identity */
313     pubkey_64=tokens[2];
314     pubkey_buffer=base64_to_bin(pubkey_64);
315     /* at this point, we may free the tokens */
316     free(tokens[0]);
317     free(tokens);
318     if(!pubkey_buffer){
319         ssh_set_error(session,SSH_FATAL,"verifying that server is a known host : base 64 error");
320         return SSH_SERVER_ERROR;
321     }
322     if(buffer_get_len(pubkey_buffer)!=string_len(pubkey)){
323         buffer_free(pubkey_buffer);
324         return SSH_SERVER_KNOWN_CHANGED;
325     }
326     /* now test that they are identical */
327     if(memcmp(buffer_get(pubkey_buffer),pubkey->string,buffer_get_len(pubkey_buffer))!=0){
328         buffer_free(pubkey_buffer);
329         return SSH_SERVER_KNOWN_CHANGED;
330     }
331     buffer_free(pubkey_buffer);
332     return SSH_SERVER_KNOWN_OK;
333 }
334 
335 int ssh_write_knownhost(SSH_SESSION *session){
336     char *pubkey_64;
337     STRING *pubkey=session->current_crypto->server_pubkey;
338     char buffer[4096];
339     FILE *file;
340     options_default_known_hosts_file(session->options);
341     if(!session->options->host){
342         ssh_set_error(session,SSH_FATAL,"Cannot write host in known hosts if the hostname is unknown");
343         return -1;
344     }
345     /* a = append only */
346     file=fopen(session->options->known_hosts_file,"a");
347     if(!file){
348         ssh_set_error(session,SSH_FATAL,"Opening known host file %s for appending : %s",
349         session->options->known_hosts_file,strerror(errno));
350         return -1;
351     }
352     pubkey_64=bin_to_base64(pubkey->string,string_len(pubkey));
353     snprintf(buffer,sizeof(buffer),"%s %s
	%s\n",session->options->host,session->current_crypto->server_pubkey_type,pubkey_64);
354     free(pubkey_64);
355     fwrite(buffer,strlen(buffer),1,file);
356     fclose(file);
357     return 0;
358 }
5773052 [rkeene@sledge /home/rkeene/projects/libssh-win32/v0.11/src/libssh-0.11-win32/libssh]$

Click here to go back to the directory listing.
Click here to download this file.
last modified: 2007-02-17 17:46:50