aboutsummaryrefslogtreecommitdiff
path: root/libssh/keyfiles.c
diff options
context:
space:
mode:
Diffstat (limited to 'libssh/keyfiles.c')
-rw-r--r--libssh/keyfiles.c484
1 files changed, 481 insertions, 3 deletions
diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c
index 93223538..e2992952 100644
--- a/libssh/keyfiles.c
+++ b/libssh/keyfiles.c
@@ -28,12 +28,431 @@ MA 02111-1307, USA. */
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
+#include "libssh/priv.h"
+#ifdef HAVE_LIBCRYPTO
#include <openssl/pem.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
-#include "libssh/priv.h"
+#endif
+#include <netinet/in.h>
#define MAXLINESIZE 80
+#ifdef HAVE_LIBGCRYPT
+#define MAX_KEY_SIZE 32
+#define MAX_PASSPHRASE_SIZE 1024
+#define RSA_HEADER_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
+#define RSA_HEADER_END "-----END RSA PRIVATE KEY-----"
+#define DSA_HEADER_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
+#define DSA_HEADER_END "-----END DSA PRIVATE KEY-----"
+#define ASN1_INTEGER 2
+#define ASN1_SEQUENCE 48
+#define PKCS5_SALT_LEN 8
+
+int load_iv(char *header, unsigned char *iv, int iv_len)
+{
+ int i;
+ int j;
+ int k;
+
+ memset(iv, 0, iv_len);
+ for (i=0; i < iv_len; i++)
+ {
+ if ((header[2*i] >= '0') && (header[2*i] <= '9'))
+ j = header[2*i] - '0';
+ else if ((header[2*i] >= 'A') && (header[2*i] <= 'F'))
+ j = header[2*i] - 'A' + 10;
+ else if ((header[2*i] >= 'a') && (header[2*i] <= 'f'))
+ j = header[2*i] - 'a' + 10;
+ else
+ return 0;
+ if ((header[2*i+1] >= '0') && (header[2*i+1] <= '9'))
+ k = header[2*i+1] - '0';
+ else if ((header[2*i+1] >= 'A') && (header[2*i+1] <= 'F'))
+ k = header[2*i+1] - 'A' + 10;
+ else if ((header[2*i+1] >= 'a') && (header[2*i+1] <= 'f'))
+ k = header[2*i+1] - 'a' + 10;
+ else
+ return 0;
+ iv[i] = (j << 4) + k;
+ }
+ return 1;
+}
+
+u32 char_to_u32(unsigned char *data, u32 size)
+{
+ u32 ret;
+ u32 i;
+
+ for (i=0,ret=0;i<size;ret=ret<<8,ret+=data[i++])
+ ;
+ return ret;
+}
+
+u32 asn1_get_len(BUFFER *buffer)
+{
+ u32 len;
+ unsigned char tmp[4];
+
+ if (!buffer_get_data(buffer,tmp,1))
+ return 0;
+ if (tmp[0] > 127)
+ {
+ len=tmp[0] & 127;
+ if (len>4)
+ return 0; /* Length doesn't fit in u32. Can this really happen? */
+ if (!buffer_get_data(buffer,tmp,len))
+ return 0;
+ len=char_to_u32(tmp,len);
+ }
+ else
+ len=char_to_u32(tmp,1);
+ return len;
+}
+
+STRING *asn1_get_int(BUFFER *buffer)
+{
+ STRING *ret;
+ unsigned char type;
+ u32 size;
+
+ if (!buffer_get_data(buffer,&type,1) || type != ASN1_INTEGER)
+ return NULL;
+ size=asn1_get_len(buffer);
+ if (!size)
+ return NULL;
+ ret=string_new(size);
+ if (!buffer_get_data(buffer,ret->string,size))
+ return NULL;
+ return ret;
+}
+
+int asn1_check_sequence(BUFFER *buffer)
+{
+ unsigned char tmp;
+ unsigned char *j;
+ int i;
+ u32 size;
+
+ if (!buffer_get_data(buffer,&tmp,1) || tmp != ASN1_SEQUENCE)
+ return 0;
+ size=asn1_get_len(buffer);
+ if (size != buffer_get_len(buffer) - buffer->pos)
+ for (i = buffer_get_len(buffer) - buffer->pos - size,
+ j = buffer_get(buffer) + size + buffer->pos; i; i--, j++)
+ {
+ if (*j != 1) /* padding is allowed */
+ return 0; /* but nothing else */
+ }
+ return 1;
+}
+
+int read_line(char *data, unsigned int len, FILE *fp)
+{
+ char tmp;
+ int i;
+
+ for (i=0; fread(&tmp, 1, 1, fp) && tmp!='\n' && i<len; data[i++]=tmp)
+ ;
+ if (tmp=='\n')
+ return i;
+ if (i>=len)
+ return -1;
+ return 0;
+}
+
+int passphrase_to_key(char *data, unsigned int datalen, unsigned char *salt, unsigned char *key,unsigned int keylen)
+{
+ MD5CTX md;
+ unsigned char digest[MD5_DIGEST_LEN];
+ unsigned int i;
+ unsigned int j;
+ unsigned int md_not_empty;
+
+ for (j=0,md_not_empty=0;j<keylen;)
+ {
+ md = md5_init();
+ if (!md)
+ return 0;
+ if (md_not_empty)
+ md5_update(md,digest,MD5_DIGEST_LEN);
+ else
+ md_not_empty=1;
+ md5_update(md,data,datalen);
+ if (salt)
+ md5_update(md, salt, PKCS5_SALT_LEN);
+ md5_final(digest,md);
+ for (i = 0; j < keylen && i < MD5_DIGEST_LEN; j++, i++)
+ if (key)
+ key[j] = digest[i];
+ }
+ return 1;
+}
+
+int privatekey_decrypt(int algo, int mode, unsigned int key_len,
+ unsigned char *iv, unsigned int iv_len,
+ BUFFER *data, int cb(char *,int , int , char *),
+ char *desc)
+{
+ gcry_cipher_hd_t cipher;
+ unsigned int passphrase_len;
+ char passphrase[MAX_PASSPHRASE_SIZE];
+ unsigned char key[MAX_KEY_SIZE];
+ unsigned char *tmp;
+ gcry_error_t err;
+
+ if (!algo)
+ return 1;
+ passphrase_len=cb(passphrase, MAX_PASSPHRASE_SIZE, 0, desc);
+ if (passphrase_len <= 0)
+ return 0;
+ passphrase_to_key(passphrase, passphrase_len, iv, key, key_len);
+ if (gcry_cipher_open(&cipher, algo, mode, GCRY_CIPHER_SECURE)
+ || gcry_cipher_setkey(cipher, key, key_len)
+ || gcry_cipher_setiv(cipher, iv, iv_len)
+ || !(tmp = malloc(buffer_get_len(data) * sizeof (char)))
+ || (err = gcry_cipher_decrypt(cipher, tmp, buffer_get_len(data),
+ buffer_get(data), buffer_get_len(data))))
+ {
+ gcry_cipher_close(cipher);
+ return 0;
+ }
+ memcpy(buffer_get(data), tmp, buffer_get_len(data));
+ gcry_cipher_close(cipher);
+ return 1;
+}
+
+int privatekey_dek_header(char *header, unsigned int header_len, int *algo, int *mode, unsigned int *key_len, unsigned char **iv, unsigned int *iv_len)
+{
+ unsigned int iv_pos;
+
+ if (header_len > 13 && !strncmp("DES-EDE3-CBC", header, 12))
+ {
+ *algo = GCRY_CIPHER_3DES;
+ iv_pos = 13;
+ *mode = GCRY_CIPHER_MODE_CBC;
+ *key_len = 24;
+ *iv_len = 8;
+ }
+ else if (header_len > 8 && !strncmp("DES-CBC", header, 7))
+ {
+ *algo = GCRY_CIPHER_DES;
+ iv_pos = 8;
+ *mode = GCRY_CIPHER_MODE_CBC;
+ *key_len = 8;
+ *iv_len = 8;
+ }
+ else if (header_len > 12 && !strncmp("AES-128-CBC", header, 11))
+ {
+ *algo = GCRY_CIPHER_AES128;
+ iv_pos = 12;
+ *mode = GCRY_CIPHER_MODE_CBC;
+ *key_len = 16;
+ *iv_len = 16;
+ }
+ else if (header_len > 12 && !strncmp("AES-192-CBC", header, 11))
+ {
+ *algo = GCRY_CIPHER_AES192;
+ iv_pos = 12;
+ *mode = GCRY_CIPHER_MODE_CBC;
+ *key_len = 24;
+ *iv_len = 16;
+ }
+ else if (header_len > 12 && !strncmp("AES-256-CBC", header, 11))
+ {
+ *algo = GCRY_CIPHER_AES256;
+ iv_pos = 12;
+ *mode = GCRY_CIPHER_MODE_CBC;
+ *key_len = 32;
+ *iv_len = 16;
+ }
+ else
+ return 0;
+ *iv = malloc(*iv_len);
+ load_iv(header + iv_pos, *iv, *iv_len);
+ return 1;
+}
+
+BUFFER *privatekey_file_to_buffer(FILE *fp, int type, int cb(char *, int , int , char *), char *desc)
+{
+ char buf[MAXLINESIZE];
+ char *header_begin;
+ unsigned int header_begin_size;
+ char *header_end;
+ unsigned int header_end_size;
+ BUFFER *buffer=buffer_new();
+ BUFFER *ret;
+ int len;
+ int algo = 0;
+ int mode = 0;
+ unsigned int key_len = 0;
+ unsigned char *iv = NULL;
+ unsigned int iv_len = 0;
+
+ switch(type)
+ {
+ case TYPE_DSS:
+ header_begin=DSA_HEADER_BEGIN;
+ header_end=DSA_HEADER_END;
+ break;
+ case TYPE_RSA:
+ header_begin=RSA_HEADER_BEGIN;
+ header_end=RSA_HEADER_END;
+ break;
+ default:
+ return NULL;
+ }
+ header_begin_size=strlen(header_begin);
+ header_end_size=strlen(header_end);
+ while (read_line(buf,MAXLINESIZE,fp) && strncmp(buf,header_begin,header_begin_size))
+ ;
+ len = read_line(buf, MAXLINESIZE, fp);
+ if (len > 11 && !strncmp("Proc-Type: 4,ENCRYPTED", buf, 11))
+ {
+ len = read_line(buf, MAXLINESIZE, fp);
+ if (len > 10 && !strncmp("DEK-Info: ", buf, 10))
+ {
+ if (!privatekey_dek_header(buf + 10, len - 10, &algo, &mode, &key_len,
+ &iv, &iv_len)
+ || read_line(buf, MAXLINESIZE, fp))
+ {
+ buffer_free(buffer);
+ free(iv);
+ return NULL;
+ }
+ }
+ else
+ {
+ buffer_free(buffer);
+ free(iv);
+ return NULL;
+ }
+ }
+ else
+ buffer_add_data(buffer,buf,len);
+ while ((len = read_line(buf,MAXLINESIZE,fp))
+ && strncmp(buf,header_end,header_end_size))
+ {
+ if (len == -1)
+ {
+ buffer_free(buffer);
+ free(iv);
+ return NULL;
+ }
+ buffer_add_data(buffer,buf,len);
+ }
+ if (strncmp(buf,header_end,header_end_size))
+ {
+ buffer_free(buffer);
+ free(iv);
+ return NULL;
+ }
+ buffer_add_data(buffer,"\0",1);
+ ret=base64_to_bin(buffer_get(buffer));
+ buffer_free(buffer);
+ if (algo)
+ {
+ if (!privatekey_decrypt(algo, mode, key_len, iv, iv_len, ret, cb, desc))
+ {
+ free(iv);
+ return NULL;
+ }
+ }
+ free(iv);
+ return ret;
+}
+
+int read_rsa_privatekey(FILE *fp, gcry_sexp_t *r,
+ int cb(char *, int , int , char *), char *desc)
+{
+ STRING *n;
+ STRING *e;
+ STRING *d;
+ STRING *p;
+ STRING *q;
+ STRING *unused1;
+ STRING *unused2;
+ STRING *u;
+ STRING *v;
+ BUFFER *buffer;
+
+ if (!(buffer=privatekey_file_to_buffer(fp, TYPE_RSA, cb, desc)))
+ return 0;
+ if (!asn1_check_sequence(buffer))
+ {
+ buffer_free(buffer);
+ return 0;
+ }
+ v=asn1_get_int(buffer);
+ if (ntohl(v->size)!=1 || v->string[0]!=0)
+ {
+ buffer_free(buffer);
+ return 0;
+ }
+ n=asn1_get_int(buffer);
+ e=asn1_get_int(buffer);
+ d=asn1_get_int(buffer);
+ q=asn1_get_int(buffer);
+ p=asn1_get_int(buffer);
+ unused1=asn1_get_int(buffer);
+ unused2=asn1_get_int(buffer);
+ u=asn1_get_int(buffer);
+ buffer_free(buffer);
+ if (!n || !e || !d || !p || !q || !unused1 || !unused2 || !u)
+ return 0;
+ gcry_sexp_build(r,NULL,"(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %b)))",ntohl(n->size),n->string,ntohl(e->size),e->string,ntohl(d->size),d->string,ntohl(p->size),p->string,ntohl(q->size),q->string,ntohl(u->size),u->string);
+ free(n);
+ free(e);
+ free(d);
+ free(p);
+ free(q);
+ free(unused1);
+ free(unused2);
+ free(u);
+ free(v);
+ return 1;
+}
+
+int read_dsa_privatekey(FILE *fp, gcry_sexp_t *r, int cb(char *, int , int , char *), char *desc)
+{
+ STRING *p;
+ STRING *q;
+ STRING *g;
+ STRING *y;
+ STRING *x;
+ STRING *v;
+ BUFFER *buffer;
+
+ if (!(buffer=privatekey_file_to_buffer(fp, TYPE_DSS, cb, desc)))
+ return 0;
+ if (!asn1_check_sequence(buffer))
+ {
+ buffer_free(buffer);
+ return 0;
+ }
+ v=asn1_get_int(buffer);
+ if (ntohl(v->size)!=1 || v->string[0]!=0)
+ {
+ buffer_free(buffer);
+ return 0;
+ }
+ p=asn1_get_int(buffer);
+ q=asn1_get_int(buffer);
+ g=asn1_get_int(buffer);
+ y=asn1_get_int(buffer);
+ x=asn1_get_int(buffer);
+ buffer_free(buffer);
+ if (!p || !q || !g || !y || !x)
+ return 0;
+ gcry_sexp_build(r,NULL,"(private-key(dsa(p %b)(q %b)(g %b)(y %b)(x %b)))",ntohl(p->size),p->string,ntohl(q->size),q->string,ntohl(g->size),g->string,ntohl(y->size),y->string,ntohl(x->size),x->string);
+ free(p);
+ free(q);
+ free(g);
+ free(y);
+ free(x);
+ free(v);
+ return 1;
+}
+#endif /* GCRYPT */
static int default_get_password(char *buf, int size,int rwflag, char *descr){
char *pass;
@@ -41,7 +460,7 @@ static int default_get_password(char *buf, int size,int rwflag, char *descr){
int len;
snprintf(buffer,256,"Please enter passphrase for %s",descr);
pass=getpass(buffer);
- snprintf(buf,size,"%s",buffer);
+ snprintf(buf,size,"%s",pass);
len=strlen(buf);
memset(pass,0,strlen(pass));
return len;
@@ -57,8 +476,14 @@ static int get_password_specified(char *buf,int size, int rwflag, char *password
PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){
FILE *file=fopen(filename,"r");
PRIVATE_KEY *privkey;
+#ifdef HAVE_LIBGCRYPT
+ gcry_sexp_t dsa=NULL;
+ gcry_sexp_t rsa=NULL;
+ int valid;
+#elif defined HAVE_LIBCRYPTO
DSA *dsa=NULL;
RSA *rsa=NULL;
+#endif
if(!file){
ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
return NULL;
@@ -66,6 +491,17 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,
if(type==TYPE_DSS){
if(!passphrase){
if(session && session->options->passphrase_function)
+#ifdef HAVE_LIBGCRYPT
+ valid = read_dsa_privatekey(file,&dsa, session->options->passphrase_function,"DSA private key");
+ else
+ valid = read_dsa_privatekey(file,&dsa,(void *)default_get_password, "DSA private key");
+ }
+ else
+ valid = read_dsa_privatekey(file,&dsa,(void *)get_password_specified,passphrase);
+ fclose(file);
+ if(!valid){
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s",filename);
+#elif defined HAVE_LIBCRYPTO
dsa=PEM_read_DSAPrivateKey(file,NULL, session->options->passphrase_function,"DSA private key");
else
dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)default_get_password, "DSA private key");
@@ -76,12 +512,24 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,
if(!dsa){
ssh_set_error(session,SSH_FATAL,"parsing private key %s"
": %s",filename,ERR_error_string(ERR_get_error(),NULL));
+#endif
return NULL;
}
}
else if (type==TYPE_RSA){
if(!passphrase){
if(session && session->options->passphrase_function)
+#ifdef HAVE_LIBGCRYPT
+ valid = read_rsa_privatekey(file,&rsa, session->options->passphrase_function,"RSA private key");
+ else
+ valid = read_rsa_privatekey(file,&rsa,(void *)default_get_password, "RSA private key");
+ }
+ else
+ valid = read_rsa_privatekey(file,&rsa,(void *)get_password_specified,passphrase);
+ fclose(file);
+ if(!valid){
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s",filename);
+#elif defined HAVE_LIBCRYPTO
rsa=PEM_read_RSAPrivateKey(file,NULL, session->options->passphrase_function,"RSA private key");
else
rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)default_get_password, "RSA private key");
@@ -92,6 +540,7 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,
if(!rsa){
ssh_set_error(session,SSH_FATAL,"parsing private key %s"
": %s",filename,ERR_error_string(ERR_get_error(),NULL));
+#endif
return NULL;
}
} else {
@@ -110,27 +559,49 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,
PRIVATE_KEY *_privatekey_from_file(void *session,char *filename,int type){
FILE *file=fopen(filename,"r");
PRIVATE_KEY *privkey;
+#ifdef HAVE_LIBGCRYPT
+ gcry_sexp_t dsa=NULL;
+ gcry_sexp_t rsa=NULL;
+ int valid;
+#elif defined HAVE_LIBCRYPTO
DSA *dsa=NULL;
RSA *rsa=NULL;
+#endif
if(!file){
ssh_set_error(session,SSH_REQUEST_DENIED,"Error opening %s : %s",filename,strerror(errno));
return NULL;
}
if(type==TYPE_DSS){
+#ifdef HAVE_LIBGCRYPT
+ valid=read_dsa_privatekey(file,&dsa,NULL,NULL);
+ fclose(file);
+ if(!valid){
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s"
+ ,filename);
+#elif defined HAVE_LIBCRYPTO
dsa=PEM_read_DSAPrivateKey(file,NULL,NULL,NULL);
fclose(file);
if(!dsa){
ssh_set_error(session,SSH_FATAL,"parsing private key %s"
": %s",filename,ERR_error_string(ERR_get_error(),NULL));
+#endif
return NULL;
}
}
else if (type==TYPE_RSA){
+#ifdef HAVE_LIBGCRYPT
+ valid=read_rsa_privatekey(file,&rsa,NULL,NULL);
+ fclose(file);
+ if(!valid){
+ ssh_set_error(session,SSH_FATAL,"parsing private key %s"
+ ,filename);
+#elif defined HAVE_LIBCRYPTO
rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL);
fclose(file);
if(!rsa){
ssh_set_error(session,SSH_FATAL,"parsing private key %s"
": %s",filename,ERR_error_string(ERR_get_error(),NULL));
+#endif
return NULL;
}
} else {
@@ -145,10 +616,17 @@ PRIVATE_KEY *_privatekey_from_file(void *session,char *filename,int type){
}
void private_key_free(PRIVATE_KEY *prv){
+#ifdef HAVE_LIBGCRYPT
+ if(prv->dsa_priv)
+ gcry_sexp_release(prv->dsa_priv);
+ if(prv->rsa_priv)
+ gcry_sexp_release(prv->rsa_priv);
+#elif defined HAVE_LIBCRYPTO
if(prv->dsa_priv)
DSA_free(prv->dsa_priv);
if(prv->rsa_priv)
RSA_free(prv->rsa_priv);
+#endif
memset(prv,0,sizeof(PRIVATE_KEY));
free(prv);
}
@@ -359,7 +837,7 @@ int ssh_is_server_known(SSH_SESSION *session){
}
int ssh_write_knownhost(SSH_SESSION *session){
- char *pubkey_64;
+ unsigned char *pubkey_64;
STRING *pubkey=session->current_crypto->server_pubkey;
char buffer[4096];
FILE *file;