wrapper.c | wrapper.c | |||
---|---|---|---|---|
skipping to change at line 34 | skipping to change at line 34 | |||
/* | /* | |||
* Why a wrapper? | * Why a wrapper? | |||
* | * | |||
* Let's say you want to port libssh from libcrypto of openssl to libfoo | * Let's say you want to port libssh from libcrypto of openssl to libfoo | |||
* you are going to spend hours to remove every references to SHA1_Update() | * you are going to spend hours to remove every references to SHA1_Update() | |||
* to libfoo_sha1_update after the work is finished, you're going to have | * to libfoo_sha1_update after the work is finished, you're going to have | |||
* only this file to modify it's not needed to say that your modifications | * only this file to modify it's not needed to say that your modifications | |||
* are welcome. | * are welcome. | |||
*/ | */ | |||
#include "config.h" | ||||
#include <stdlib.h> | #include <stdlib.h> | |||
#include <stdio.h> | #include <stdio.h> | |||
#include <string.h> | #include <string.h> | |||
#include "libssh/priv.h" | #include "libssh/priv.h" | |||
#include "libssh/session.h" | ||||
#include "libssh/crypto.h" | ||||
#include "libssh/wrapper.h" | ||||
#ifdef HAVE_LIBGCRYPT | #ifdef HAVE_LIBGCRYPT | |||
#include <gcrypt.h> | #include <gcrypt.h> | |||
#include "libssh/crypto.h" | ||||
static int alloc_key(struct crypto_struct *cipher) { | static int alloc_key(struct crypto_struct *cipher) { | |||
cipher->key = malloc(cipher->keylen); | cipher->key = malloc(cipher->keylen); | |||
if (cipher->key == NULL) { | if (cipher->key == NULL) { | |||
return -1; | return -1; | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
SHACTX sha1_init(void) { | SHACTX sha1_init(void) { | |||
skipping to change at line 265 | skipping to change at line 267 | |||
if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { | if (gcry_cipher_setiv(cipher->key[0], IV, 8)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES, | if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES, | |||
GCRY_CIPHER_MODE_CBC, 0)) { | GCRY_CIPHER_MODE_CBC, 0)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_setkey(cipher->key[1], key + 8, 8)) { | if (gcry_cipher_setkey(cipher->key[1], (unsigned char *)key + 8, 8)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_setiv(cipher->key[1], IV + 8, 8)) { | if (gcry_cipher_setiv(cipher->key[1], (unsigned char *)IV + 8, 8)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES, | if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES, | |||
GCRY_CIPHER_MODE_CBC, 0)) { | GCRY_CIPHER_MODE_CBC, 0)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_setkey(cipher->key[2], key + 16, 8)) { | if (gcry_cipher_setkey(cipher->key[2], (unsigned char *)key + 16, 8)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
if (gcry_cipher_setiv(cipher->key[2], IV + 16, 8)) { | if (gcry_cipher_setiv(cipher->key[2], (unsigned char *)IV + 16, 8)) { | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
static void des3_1_encrypt(struct crypto_struct *cipher, void *in, | static void des3_1_encrypt(struct crypto_struct *cipher, void *in, | |||
void *out, unsigned long len) { | void *out, unsigned long len) { | |||
skipping to change at line 408 | skipping to change at line 410 | |||
#endif | #endif | |||
#ifdef HAVE_OPENSSL_BLOWFISH_H | #ifdef HAVE_OPENSSL_BLOWFISH_H | |||
#define HAS_BLOWFISH | #define HAS_BLOWFISH | |||
#include <openssl/blowfish.h> | #include <openssl/blowfish.h> | |||
#endif | #endif | |||
#ifdef HAVE_OPENSSL_DES_H | #ifdef HAVE_OPENSSL_DES_H | |||
#define HAS_DES | #define HAS_DES | |||
#include <openssl/des.h> | #include <openssl/des.h> | |||
#endif | #endif | |||
#if (OPENSSL_VERSION_NUMBER<0x009070000) | #if (OPENSSL_VERSION_NUMBER<0x00907000L) | |||
#define OLD_CRYPTO | #define OLD_CRYPTO | |||
#endif | #endif | |||
#include "libssh/crypto.h" | #include "libssh/crypto.h" | |||
static int alloc_key(struct crypto_struct *cipher) { | static int alloc_key(struct crypto_struct *cipher) { | |||
cipher->key = malloc(cipher->keylen); | cipher->key = malloc(cipher->keylen); | |||
if (cipher->key == NULL) { | if (cipher->key == NULL) { | |||
return -1; | return -1; | |||
} | } | |||
skipping to change at line 580 | skipping to change at line 582 | |||
#endif /* HAS_AES */ | #endif /* HAS_AES */ | |||
#ifdef HAS_DES | #ifdef HAS_DES | |||
static int des3_set_key(struct crypto_struct *cipher, void *key) { | static int des3_set_key(struct crypto_struct *cipher, void *key) { | |||
if (cipher->key == NULL) { | if (cipher->key == NULL) { | |||
if (alloc_key(cipher) < 0) { | if (alloc_key(cipher) < 0) { | |||
return -1; | return -1; | |||
} | } | |||
DES_set_odd_parity(key); | DES_set_odd_parity(key); | |||
DES_set_odd_parity(key + 8); | DES_set_odd_parity((void*)((uint8_t*)key + 8)); | |||
DES_set_odd_parity(key + 16); | DES_set_odd_parity((void*)((uint8_t*)key + 16)); | |||
DES_set_key_unchecked(key, cipher->key); | DES_set_key_unchecked(key, cipher->key); | |||
DES_set_key_unchecked(key + 8, cipher->key + sizeof(DES_key_schedule)); | DES_set_key_unchecked((void*)((uint8_t*)key + 8), (void*)((uint8_t*)cip | |||
DES_set_key_unchecked(key + 16, cipher->key + 2 * sizeof(DES_key_schedu | her->key + sizeof(DES_key_schedule))); | |||
le)); | DES_set_key_unchecked((void*)((uint8_t*)key + 16), (void*)((uint8_t*)ci | |||
pher->key + 2 * sizeof(DES_key_schedule))); | ||||
} | } | |||
return 0; | return 0; | |||
} | } | |||
static void des3_encrypt(struct crypto_struct *cipher, void *in, | static void des3_encrypt(struct crypto_struct *cipher, void *in, | |||
void *out, unsigned long len, void *IV) { | void *out, unsigned long len, void *IV) { | |||
DES_ede3_cbc_encrypt(in, out, len, cipher->key, | DES_ede3_cbc_encrypt(in, out, len, cipher->key, | |||
cipher->key + sizeof(DES_key_schedule), | (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), | |||
cipher->key + 2 * sizeof(DES_key_schedule), | (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), | |||
IV, 1); | IV, 1); | |||
} | } | |||
static void des3_decrypt(struct crypto_struct *cipher, void *in, | static void des3_decrypt(struct crypto_struct *cipher, void *in, | |||
void *out, unsigned long len, void *IV) { | void *out, unsigned long len, void *IV) { | |||
DES_ede3_cbc_encrypt(in, out, len, cipher->key, | DES_ede3_cbc_encrypt(in, out, len, cipher->key, | |||
cipher->key + sizeof(DES_key_schedule), | (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)), | |||
cipher->key + 2 * sizeof(DES_key_schedule), | (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)), | |||
IV, 0); | IV, 0); | |||
} | } | |||
static void des3_1_encrypt(struct crypto_struct *cipher, void *in, | static void des3_1_encrypt(struct crypto_struct *cipher, void *in, | |||
void *out, unsigned long len, void *IV) { | void *out, unsigned long len, void *IV) { | |||
#ifdef DEBUG_CRYPTO | #ifdef DEBUG_CRYPTO | |||
ssh_print_hexa("Encrypt IV before", IV, 24); | ssh_print_hexa("Encrypt IV before", IV, 24); | |||
#endif | #endif | |||
DES_ncbc_encrypt(in, out, len, cipher->key, IV, 1); | DES_ncbc_encrypt(in, out, len, cipher->key, IV, 1); | |||
DES_ncbc_encrypt(out, in, len, cipher->key + sizeof(DES_key_schedule), | DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES | |||
IV + 8, 0); | _key_schedule)), | |||
DES_ncbc_encrypt(in, out, len, cipher->key + 2 * sizeof(DES_key_schedule) | (void*)((uint8_t*)IV + 8), 0); | |||
, | DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof | |||
IV + 16, 1); | (DES_key_schedule)), | |||
(void*)((uint8_t*)IV + 16), 1); | ||||
#ifdef DEBUG_CRYPTO | #ifdef DEBUG_CRYPTO | |||
ssh_print_hexa("Encrypt IV after", IV, 24); | ssh_print_hexa("Encrypt IV after", IV, 24); | |||
#endif | #endif | |||
} | } | |||
static void des3_1_decrypt(struct crypto_struct *cipher, void *in, | static void des3_1_decrypt(struct crypto_struct *cipher, void *in, | |||
void *out, unsigned long len, void *IV) { | void *out, unsigned long len, void *IV) { | |||
#ifdef DEBUG_CRYPTO | #ifdef DEBUG_CRYPTO | |||
ssh_print_hexa("Decrypt IV before", IV, 24); | ssh_print_hexa("Decrypt IV before", IV, 24); | |||
#endif | #endif | |||
DES_ncbc_encrypt(in, out, len, cipher->key + 2 * sizeof(DES_key_schedule) , | DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof (DES_key_schedule)), | |||
IV, 0); | IV, 0); | |||
DES_ncbc_encrypt(out, in, len, cipher->key + sizeof(DES_key_schedule), | DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES | |||
IV + 8, 1); | _key_schedule)), | |||
DES_ncbc_encrypt(in, out, len, cipher->key, IV + 16, 0); | (void*)((uint8_t*)IV + 8), 1); | |||
DES_ncbc_encrypt(in, out, len, cipher->key, (void*)((uint8_t*)IV + 16), 0 | ||||
); | ||||
#ifdef DEBUG_CRYPTO | #ifdef DEBUG_CRYPTO | |||
ssh_print_hexa("Decrypt IV after", IV, 24); | ssh_print_hexa("Decrypt IV after", IV, 24); | |||
#endif | #endif | |||
} | } | |||
#endif /* HAS_DES */ | #endif /* HAS_DES */ | |||
/* the table of supported ciphers */ | /* | |||
* The table of supported ciphers | ||||
* | ||||
* WARNING: If you modify crypto_struct, you must make sure the order is | ||||
* correct! | ||||
*/ | ||||
static struct crypto_struct ssh_ciphertab[] = { | static struct crypto_struct ssh_ciphertab[] = { | |||
#ifdef HAS_BLOWFISH | #ifdef HAS_BLOWFISH | |||
{ | { | |||
.name = "blowfish-cbc", | "blowfish-cbc", | |||
.blocksize = 8, | 8, | |||
.keylen = sizeof (BF_KEY), | sizeof (BF_KEY), | |||
.key = NULL, | NULL, | |||
.keysize = 128, | 128, | |||
.set_encrypt_key = blowfish_set_key, | blowfish_set_key, | |||
.set_decrypt_key = blowfish_set_key, | blowfish_set_key, | |||
.cbc_encrypt = blowfish_encrypt, | blowfish_encrypt, | |||
.cbc_decrypt = blowfish_decrypt | blowfish_decrypt | |||
}, | }, | |||
#endif /* HAS_BLOWFISH */ | #endif /* HAS_BLOWFISH */ | |||
#ifdef HAS_AES | #ifdef HAS_AES | |||
{ | { | |||
.name = "aes128-cbc", | "aes128-cbc", | |||
.blocksize = 16, | 16, | |||
.keylen = sizeof(AES_KEY), | sizeof(AES_KEY), | |||
.key = NULL, | NULL, | |||
.keysize = 128, | 128, | |||
.set_encrypt_key = aes_set_encrypt_key, | aes_set_encrypt_key, | |||
.set_decrypt_key = aes_set_decrypt_key, | aes_set_decrypt_key, | |||
.cbc_encrypt = aes_encrypt, | aes_encrypt, | |||
.cbc_decrypt = aes_decrypt | aes_decrypt | |||
}, | }, | |||
{ | { | |||
.name = "aes192-cbc", | "aes192-cbc", | |||
.blocksize = 16, | 16, | |||
.keylen = sizeof(AES_KEY), | sizeof(AES_KEY), | |||
.key = NULL, | NULL, | |||
.keysize = 192, | 192, | |||
.set_encrypt_key = aes_set_encrypt_key, | aes_set_encrypt_key, | |||
.set_decrypt_key = aes_set_decrypt_key, | aes_set_decrypt_key, | |||
.cbc_encrypt = aes_encrypt, | aes_encrypt, | |||
.cbc_decrypt = aes_decrypt | aes_decrypt | |||
}, | }, | |||
{ | { | |||
.name = "aes256-cbc", | "aes256-cbc", | |||
.blocksize = 16, | 16, | |||
.keylen = sizeof(AES_KEY), | sizeof(AES_KEY), | |||
.key = NULL, | NULL, | |||
.keysize = 256, | 256, | |||
.set_encrypt_key = aes_set_encrypt_key, | aes_set_encrypt_key, | |||
.set_decrypt_key = aes_set_decrypt_key, | aes_set_decrypt_key, | |||
.cbc_encrypt = aes_encrypt, | aes_encrypt, | |||
.cbc_decrypt = aes_decrypt | aes_decrypt | |||
}, | }, | |||
#endif /* HAS_AES */ | #endif /* HAS_AES */ | |||
#ifdef HAS_DES | #ifdef HAS_DES | |||
{ | { | |||
.name = "3des-cbc", | "3des-cbc", | |||
.blocksize = 8, | 8, | |||
.keylen = sizeof(DES_key_schedule) * 3, | sizeof(DES_key_schedule) * 3, | |||
.key = NULL, | NULL, | |||
.keysize = 192, | 192, | |||
.set_encrypt_key = des3_set_key, | des3_set_key, | |||
.set_decrypt_key = des3_set_key, | des3_set_key, | |||
.cbc_encrypt = des3_encrypt, | des3_encrypt, | |||
.cbc_decrypt = des3_decrypt | des3_decrypt | |||
}, | }, | |||
{ | { | |||
.name = "3des-cbc-ssh1", | "3des-cbc-ssh1", | |||
.blocksize = 8, | 8, | |||
.keylen = sizeof(DES_key_schedule) * 3, | sizeof(DES_key_schedule) * 3, | |||
.key = NULL, | NULL, | |||
.keysize = 192, | 192, | |||
.set_encrypt_key = des3_set_key, | des3_set_key, | |||
.set_decrypt_key = des3_set_key, | des3_set_key, | |||
.cbc_encrypt = des3_1_encrypt, | des3_1_encrypt, | |||
.cbc_decrypt = des3_1_decrypt | des3_1_decrypt | |||
}, | }, | |||
#endif /* HAS_DES */ | #endif /* HAS_DES */ | |||
{ | { | |||
.name = NULL, | NULL, | |||
.blocksize = 0, | 0, | |||
.keylen = 0, | 0, | |||
.key = NULL, | NULL, | |||
.keysize = 0, | 0, | |||
.set_encrypt_key = NULL, | NULL, | |||
.set_decrypt_key = NULL, | NULL, | |||
.cbc_encrypt = NULL, | NULL, | |||
.cbc_decrypt = NULL | NULL | |||
} | } | |||
}; | }; | |||
#endif /* OPENSSL_CRYPTO */ | #endif /* OPENSSL_CRYPTO */ | |||
/* it allocates a new cipher structure based on its offset into the global table */ | /* it allocates a new cipher structure based on its offset into the global table */ | |||
static struct crypto_struct *cipher_new(int offset) { | static struct crypto_struct *cipher_new(int offset) { | |||
struct crypto_struct *cipher = NULL; | struct crypto_struct *cipher = NULL; | |||
cipher = malloc(sizeof(struct crypto_struct)); | cipher = malloc(sizeof(struct crypto_struct)); | |||
if (cipher == NULL) { | if (cipher == NULL) { | |||
skipping to change at line 766 | skipping to change at line 773 | |||
} | } | |||
#elif defined HAVE_LIBCRYPTO | #elif defined HAVE_LIBCRYPTO | |||
/* destroy the key */ | /* destroy the key */ | |||
memset(cipher->key, 0, cipher->keylen); | memset(cipher->key, 0, cipher->keylen); | |||
#endif | #endif | |||
SAFE_FREE(cipher->key); | SAFE_FREE(cipher->key); | |||
} | } | |||
SAFE_FREE(cipher); | SAFE_FREE(cipher); | |||
} | } | |||
CRYPTO *crypto_new(void) { | struct ssh_crypto_struct *crypto_new(void) { | |||
CRYPTO *crypto; | struct ssh_crypto_struct *crypto; | |||
crypto = malloc(sizeof(CRYPTO)); | crypto = malloc(sizeof(struct ssh_crypto_struct)); | |||
if (crypto == NULL) { | if (crypto == NULL) { | |||
return NULL; | return NULL; | |||
} | } | |||
ZERO_STRUCTP(crypto); | ||||
memset(crypto, 0, sizeof(CRYPTO)); | ||||
return crypto; | return crypto; | |||
} | } | |||
void crypto_free(CRYPTO *crypto){ | void crypto_free(struct ssh_crypto_struct *crypto){ | |||
if (crypto == NULL) { | if (crypto == NULL) { | |||
return; | return; | |||
} | } | |||
SAFE_FREE(crypto->server_pubkey); | SAFE_FREE(crypto->server_pubkey); | |||
cipher_free(crypto->in_cipher); | cipher_free(crypto->in_cipher); | |||
cipher_free(crypto->out_cipher); | cipher_free(crypto->out_cipher); | |||
bignum_free(crypto->e); | bignum_free(crypto->e); | |||
skipping to change at line 801 | skipping to change at line 806 | |||
bignum_free(crypto->x); | bignum_free(crypto->x); | |||
bignum_free(crypto->y); | bignum_free(crypto->y); | |||
bignum_free(crypto->k); | bignum_free(crypto->k); | |||
/* lot of other things */ | /* lot of other things */ | |||
/* i'm lost in my own code. good work */ | /* i'm lost in my own code. good work */ | |||
memset(crypto,0,sizeof(*crypto)); | memset(crypto,0,sizeof(*crypto)); | |||
SAFE_FREE(crypto); | SAFE_FREE(crypto); | |||
} | } | |||
static int crypt_set_algorithms2(SSH_SESSION *session){ | static int crypt_set_algorithms2(ssh_session session){ | |||
const char *wanted; | const char *wanted; | |||
int i = 0; | int i = 0; | |||
/* we must scan the kex entries to find crypto algorithms and set their a ppropriate structure */ | /* we must scan the kex entries to find crypto algorithms and set their a ppropriate structure */ | |||
/* out */ | /* out */ | |||
wanted = session->client_kex.methods[SSH_CRYPT_C_S]; | wanted = session->client_kex.methods[SSH_CRYPT_C_S]; | |||
while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { | while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) { | |||
i++; | i++; | |||
} | } | |||
skipping to change at line 858 | skipping to change at line 863 | |||
if (strstr(session->client_kex.methods[SSH_COMP_C_S], "zlib")) { | if (strstr(session->client_kex.methods[SSH_COMP_C_S], "zlib")) { | |||
session->next_crypto->do_compress_out = 1; | session->next_crypto->do_compress_out = 1; | |||
} | } | |||
if (strstr(session->client_kex.methods[SSH_COMP_S_C], "zlib")) { | if (strstr(session->client_kex.methods[SSH_COMP_S_C], "zlib")) { | |||
session->next_crypto->do_compress_in = 1; | session->next_crypto->do_compress_in = 1; | |||
} | } | |||
return SSH_OK; | return SSH_OK; | |||
} | } | |||
static int crypt_set_algorithms1(SSH_SESSION *session) { | static int crypt_set_algorithms1(ssh_session session) { | |||
int i = 0; | int i = 0; | |||
/* right now, we force 3des-cbc to be taken */ | /* right now, we force 3des-cbc to be taken */ | |||
while (ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name, | while (ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name, | |||
"3des-cbc-ssh1")) { | "3des-cbc-ssh1")) { | |||
i++; | i++; | |||
} | } | |||
if (ssh_ciphertab[i].name == NULL) { | if (ssh_ciphertab[i].name == NULL) { | |||
ssh_set_error(session, SSH_FATAL, "cipher 3des-cbc-ssh1 not found!"); | ssh_set_error(session, SSH_FATAL, "cipher 3des-cbc-ssh1 not found!"); | |||
skipping to change at line 887 | skipping to change at line 892 | |||
session->next_crypto->in_cipher = cipher_new(i); | session->next_crypto->in_cipher = cipher_new(i); | |||
if (session->next_crypto->in_cipher == NULL) { | if (session->next_crypto->in_cipher == NULL) { | |||
ssh_set_error(session, SSH_FATAL, "No space left"); | ssh_set_error(session, SSH_FATAL, "No space left"); | |||
return SSH_ERROR; | return SSH_ERROR; | |||
} | } | |||
return SSH_OK; | return SSH_OK; | |||
} | } | |||
int crypt_set_algorithms(SSH_SESSION *session) { | int crypt_set_algorithms(ssh_session session) { | |||
return (session->version == 1) ? crypt_set_algorithms1(session) : | return (session->version == 1) ? crypt_set_algorithms1(session) : | |||
crypt_set_algorithms2(session); | crypt_set_algorithms2(session); | |||
} | } | |||
// TODO Obviously too much cut and paste here | // TODO Obviously too much cut and paste here | |||
int crypt_set_algorithms_server(SSH_SESSION *session){ | int crypt_set_algorithms_server(ssh_session session){ | |||
char *server = NULL; | char *server = NULL; | |||
char *client = NULL; | char *client = NULL; | |||
char *match = NULL; | char *match = NULL; | |||
int i = 0; | int i = 0; | |||
/* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ | /* we must scan the kex entries to find crypto algorithms and set their appropriate structure */ | |||
enter_function(); | enter_function(); | |||
/* out */ | /* out */ | |||
server = session->server_kex.methods[SSH_CRYPT_S_C]; | server = session->server_kex.methods[SSH_CRYPT_S_C]; | |||
client = session->client_kex.methods[SSH_CRYPT_S_C]; | client = session->client_kex.methods[SSH_CRYPT_S_C]; | |||
End of changes. 28 change blocks. | ||||
106 lines changed or deleted | 115 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |