diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2005-10-04 22:11:19 +0000 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2005-10-04 22:11:19 +0000 |
commit | 5f7c84f900b81e3bbff55378f8170ddf150daf9c (patch) | |
tree | 2076a6416e087eb5870c2217873ae76e85451f6b /libssh/keyfiles.c | |
parent | c0525750fd090bca0f1ab1884cc26ecd227addb7 (diff) | |
download | libssh-5f7c84f900b81e3bbff55378f8170ddf150daf9c.tar.gz libssh-5f7c84f900b81e3bbff55378f8170ddf150daf9c.tar.xz libssh-5f7c84f900b81e3bbff55378f8170ddf150daf9c.zip |
added the gcrypt patch (without gcrypt as default library).
still needs tests.
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@38 7dcaeef0-15fb-0310-b436-a5af3365683c
Diffstat (limited to 'libssh/keyfiles.c')
-rw-r--r-- | libssh/keyfiles.c | 484 |
1 files changed, 481 insertions, 3 deletions
diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c index 9322353..e299295 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; |