/* $Id: userkey.c,v 1.8 1999/06/25 05:33:44 levitte Exp $ */ #include "gnu_extras.h" #include #include #include #include #include #include #if defined(__DECC) || defined(__GNUC__) #include #endif #include #include #include #include #ifdef __GNUC__ #define __DECC #endif #include #include #include #ifdef __GNUC__ #undef __DECC #endif #include "fish.h" #include "ssh.h" #include "erf.h" #include "userkey.h" #include "buffer.h" #include "util.h" #include "fishmsg.h" void userkey_init_static(userkey *uk) { memset(uk, 0, sizeof(userkey)); } userkey *userkey_init(void) { userkey *uk = xmalloc(sizeof(userkey)); userkey_init_static(uk); return uk; } void userkey_destroy_static(userkey *uk) { if (uk == 0) return; if (uk->key) RSA_free(uk->key); if (uk->comment) xfree(uk->comment); if (uk->encrypted_data) xfree(uk->encrypted_data); } void userkey_destroy(userkey *uk) { if (uk == 0) return; xfree(uk); return; } int userkey_decode_buffer(userkey *uk, general_buffer *buf) { int status = SS$_NORMAL; char id_buffer[] = AUTHFILE_ID_STRING; unsigned long dummy = strlen(id_buffer); general_buffer_reader gbr; bufread_init_static(&gbr, buf); userkey_init_static(uk); uk->key = RSA_new(); if (!bufread_bytes(&gbr, id_buffer, dummy+1) || id_buffer[dummy] != '\0') goto pubformerr; if (strcmp(id_buffer, AUTHFILE_ID_STRING) != 0) goto pubbadid; if (!bufread_bytes(&gbr, &uk->cipher_type, 1)) goto pubformerr; if (!bufread_int(&gbr, &dummy) || !bufread_int(&gbr, &uk->bits) || !bufread_bn(&gbr, &uk->key->n) || !bufread_bn(&gbr, &uk->key->e) || !bufread_strdup(&gbr, &uk->comment)) goto pubformerr; uk->encrypted_data = xmalloc(uk->encrypted_len = bufread_remaining(&gbr)); if (!bufread_bytes(&gbr, uk->encrypted_data, uk->encrypted_len)) goto pubformerr; return SS$_NORMAL; pubcleanup: userkey_destroy(uk); return status; pubformerr: status = FISH_M_UKFORMERR; goto pubcleanup; pubbadid: status = FISH_M_UKBADID; goto pubcleanup; } int userkey_encode_buffer(userkey *uk, general_buffer **buf) { *buf = buf_init(0); if (uk->encrypted_data == 0) goto privnodata; if (!buf_append_bytes(*buf, AUTHFILE_ID_STRING, strlen(AUTHFILE_ID_STRING)+1) || !buf_append_bytes(*buf, &uk->cipher_type, 1) || !buf_append_int(*buf, 0) || !buf_append_int(*buf, uk->bits) || !buf_append_bn(*buf, uk->key->n) || !buf_append_bn(*buf, uk->key->e) || !buf_append_chars(*buf, uk->comment) || !buf_append_bytes(*buf, uk->encrypted_data, uk->encrypted_len)) goto privmemfull; return SS$_NORMAL; privnodata: buf_destroy(*buf); return FISH_M_UKNODATA; privmemfull: buf_destroy(*buf); return FISH_M_MEMFULL; } int userkey_decrypt_private_part(userkey *uk, general_buffer *passphrase) { int status = SS$_NORMAL; unsigned char *decrypted = 0; general_buffer decryptbuf; general_buffer_reader decryptreader; MD5_CTX md; unsigned char digest[16]; ssh_state internal_state; Erf internal_erf = NULL; void *internal_erfp = NULL; unsigned char check1_1; unsigned char check1_2; unsigned char check2_1; unsigned char check2_2; #if 0 if (uk->cipher_type != SSH_CIPHER_NONE && ((1 << uk->cipher_type) & cipher_mask) == 0) goto privbadcipher; #endif decrypted = xmalloc(uk->encrypted_len); buf_init_static(&decryptbuf, decrypted, uk->encrypted_len); /* Generate a MD5 checksum from the passphrase */ MD5_Init(&md); MD5_Update(&md, buf_chars_noadjust(passphrase), buf_amount(passphrase)); MD5_Final(digest, &md); /* Build a 32 byte string from the digest, with padding */ memset(internal_state.sesskey, 0, sizeof(internal_state.sesskey)); memcpy(internal_state.sesskey, digest, 16); internal_state.info = "RSA key cipher"; ssh_crypto_setup(uk->cipher_type, &internal_state, 16, internal_erf, internal_erfp); internal_state.info = 0; if (internal_state.decrypt) { (internal_state.decrypt)(uk->encrypted_data, decrypted, uk->encrypted_len, internal_state.decryptstate); } buf_adjust_amount_by(&decryptbuf, uk->encrypted_len); bufread_init_static(&decryptreader, &decryptbuf); if (!bufread_bytes(&decryptreader, &check1_1, 1) || !bufread_bytes(&decryptreader, &check1_2, 1) || !bufread_bytes(&decryptreader, &check2_1, 1) || !bufread_bytes(&decryptreader, &check2_2, 1)) goto privformerr; if (check1_1 != check2_1 || check1_2 != check2_2) goto privbadpass; if (!bufread_bn(&decryptreader, &uk->key->d) || !bufread_bn(&decryptreader, &uk->key->iqmp) || !bufread_bn(&decryptreader, &uk->key->p) || !bufread_bn(&decryptreader, &uk->key->q)) goto privformerr; xfree(decrypted); return status; privcleanup: userkey_destroy_static(uk); privcleanup2: if (decrypted) xfree(decrypted); return status; privformerr: status = FISH_M_UKFORMERR; goto privcleanup; #if 0 /* Uhmmm, removed right now... */ privbadcipher: status = FISH_M_UKBADCIPHER; goto privcleanup; #endif privbadpass: status = FISH_M_UKBADPASS; goto privcleanup2; } int userkey_encrypt_private_part(userkey *uk, general_buffer *passphrase) { unsigned char byte1, byte2; ssh_state internal_state; MD5_CTX md; unsigned char digest[16]; general_buffer *encrypted = buf_init(0); if (uk->encrypted_data != 0) goto privnodata; MD5_Init(&md); MD5_Update(&md, buf_bytes(passphrase), buf_amount(passphrase)); MD5_Final(digest, &md); /* Build a 32 byte string from the digest, with padding */ memset(internal_state.sesskey, 0, sizeof(internal_state.sesskey)); memcpy(internal_state.sesskey, digest, 16); internal_state.info = "RSA key cipher"; ssh_crypto_setup(uk->cipher_type, &internal_state, 16, NULL, NULL); internal_state.info = 0; /* Build private part */ RAND_bytes(&byte1, 1); RAND_bytes(&byte2, 1); buf_append_bytes(encrypted, &byte1, 1); buf_append_bytes(encrypted, &byte2, 1); buf_append_bytes(encrypted, &byte1, 1); buf_append_bytes(encrypted, &byte2, 1); buf_append_bn(encrypted, uk->key->d); buf_append_bn(encrypted, uk->key->iqmp); buf_append_bn(encrypted, uk->key->p); buf_append_bn(encrypted, uk->key->q); if ((buf_amount(encrypted) % 8) != 0) buf_append_bytes(encrypted, "\0\0\0\0\0\0\0\0", 8 - (buf_amount(encrypted) % 8)); uk->encrypted_len = buf_amount(encrypted); uk->encrypted_data = xmalloc(uk->encrypted_len); if (internal_state.encrypt) { (internal_state.encrypt)(buf_chars_noadjust(encrypted), uk->encrypted_data, uk->encrypted_len, internal_state.encryptstate); } else { memcpy(uk->encrypted_data, buf_chars_noadjust(encrypted), uk->encrypted_len); } buf_destroy(encrypted); return SS$_NORMAL; privnodata: buf_destroy(encrypted); return FISH_M_UKNODATA; } int userkey_read_keyfile(userkey *uk, char *filename, unsigned long uic) { int status; struct stat statb; int keyfh; unsigned long n; int save_errno; general_buffer *tmpbuf; if (stat(filename, &statb) < 0) { return FISH_M_UKNOFILE; } else if (statb.st_uid != uic) { char errbuf[1024]; ssh_F_uknotowner(filename); return FISH_M_UKNOTOWNER | 0x10000000; } if (!(tmpbuf = buf_init(statb.st_size))) { return FISH_M_MEMFULL; } keyfh = open(filename, O_RDONLY, 0); while((n = read(keyfh, buf_chars_noadjust(tmpbuf) + buf_amount(tmpbuf), statb.st_size - buf_amount(tmpbuf))) > 0) buf_adjust_amount_by(tmpbuf, n); save_errno = errno; close(keyfh); if (buf_amount(tmpbuf) != statb.st_size) { lib$signal(FISH_M_UKSHORTIDFILE, 3, filename, buf_amount(tmpbuf), statb.st_size); return FISH_M_UKSHORTIDFILE | 0x10000000; } status = userkey_decode_buffer(uk, tmpbuf); if (!$VMS_STATUS_SUCCESS(status)) { buf_destroy(tmpbuf); return status; } return SS$_NORMAL; } /* Emacs local variables Local variables: eval: (set-c-style "BSD") end: */