diff --git a/SECURITY b/SECURITY index d1abc0c0..c527e8c2 100644 --- a/SECURITY +++ b/SECURITY @@ -61,24 +61,30 @@ encrypt_and_sign to prepare the message to be sent. When the executive wants to receive a message from the network, it uses authenticate_and_decrypt to verify the message is valid and decrypt it. -These two functions utilize the following algorithms: +There are currently two encryption methods available in corosync. +sha1/hmac/sober which are coded internally, and AES/SHA256 which +are in the Mozilla Network Security Services (libnss) library. + +The internal functions utilize the following algorithms: sha1 - hash algorithm secure for using with hmac hmac - produces a 16 byte digest from any length input sober - pseudo random number generator and stream cipher -The hmac algorithm requires a 16 byte key. -The sober algorithm requires a 16 byte private key. -The sober algorithm requires a 16 byte public initial vector. - -The private key is read from disk and stored in memory for use with the -sober algorithm to generate the three required keys. - Every message starts with a struct security { unsigned char digest[20]; A one way hash digest unsigned char salt[16]; A securely generated random number } +INTERNAL SECURITY CODE: +----------------------- +The hmac algorithm requires a 16 byte key. +The sober algorithm requires a 16 byte private key. +The sober algorithm requires a 16 byte public initial vector. + +The private key is read from disk and stored in memory for use with the +sober algorithm to generate the three required keys. + When a message is sent (encrypt_and_sign): ------------------------------------------ 1. sober is used to create a 16 byte random number (salt) using the md4 @@ -123,4 +129,59 @@ is randomly unique (within the 2^128 search space of the input to sober) to ensure that keys are never reused, nonce's are never reused, and hmac's are never reused. + +USING LIBNSS +------------ + +The process is similar in concept to the above, but most of the details are +hidden inside the NSS library. When corosync is started up libnss is initialised, +the private key is read into memory and stored for later use by the code. + +When a message is sent (encrypt_and_sign): +------------------------------------------ +- The message is encrypted using AES. +- A digest of that message is then created using SHA256 and based on the + private key. +- the message is then transmitted. + +When a message is received (decrypt_and_authenticate): +- A Digest of the encrypted message is created using the private key +- That digest is compared to the one in the message security_header +- If they do not match the packet is rejected +- If they do match then the message is decrypted using the private key. +- The message is processed. + + +Compatibility +------------- + +The default mode of operation is to allow for wire-compatibility with existing +openais systems. That means that the internal encryption system is used +and all received packets are expected to use that system. This allows a rolling +upgrade from openais to corosync. + +Once all nodes in the cluster are running corosync they can be changed to allow +the newer libnss-based encryption by setting the +totem { + crypto_accept: new +} +option in corosync.conf. + +This enables the new encryption system but does not switch it on. It simply +adds a byte to the end of the packets to indicate the encryption type. + +Once all nodes have been upgraded and 'crypto_accept: new' has been set, +the encryption type can be set using a single command: + +# corosync-cfgtool -c1 + +This will tell all cluster nodes to start using libnss encryption. Note that +it is possible to upgrade node individially by seetting the encryption type in +corosync.conf. The last byte of the packet indicates the decryption algorithm +that the receiver should use. + +Once all nodes are using libnss encryption, the option should be set in +in corosync.conf so that it takes effect at the next system reboot. + + Comments welcome mailto:corosync@lists.osdl.org diff --git a/configure.ac b/configure.ac index 2a41b5d7..3b45a490 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,7 @@ AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB AC_CHECK_PROGS([GROFF], [groff]) +AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) # Checks for libraries. AC_CHECK_LIB([dl], [dlopen]) @@ -176,6 +177,10 @@ AC_ARG_ENABLE([coverage], [ --enable-coverage : coverage analysis of the codebase. ], [ default="no" ]) +AC_ARG_ENABLE([nss], + [ --enable-nss : Network Security Services encryption. ], + [ default="yes" ]) + AC_ARG_WITH([lcrso-dir], [ --with-lcrso-dir=DIR : corosync lcrso files. ], [ LCRSODIR="$withval" ], @@ -257,6 +262,16 @@ else GDB_FLAGS="-g" fi +# Look for libnss +if test "x${enable_nss}" = xyes; then + if $PKGCONFIG --exists nss; then + NSS_CFLAGS="`$PKGCONFIG --cflags nss`" + NSS_LDFLAGS="`$PKGCONFIG --libs nss`" + AC_DEFINE_UNQUOTED([HAVE_LIBNSS], 1, [have libnss]) + PKG_FEATURES="$PKG_FEATURES nss" + fi +fi + # extra warnings EXTRA_WARNINGS="" @@ -320,7 +335,7 @@ fi # final build of *FLAGS CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS $OS_CFLAGS \ - $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS" + $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS $NSS_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS $OS_CPPFLAGS" LDFLAGS="$ENV_LDFLAGS $COVERAGE_LDFLAGS $OS_LDFLAGS" @@ -335,6 +350,8 @@ AC_SUBST([SONAME]) AC_SUBST([OS_DYFLAGS]) +AC_SUBST([NSS_LDFLAGS]) + AM_CONDITIONAL(BUILD_DARWIN, test -n "${DARWIN_OPTS}") AC_SUBST([DARWIN_OPTS]) diff --git a/exec/Makefile.am b/exec/Makefile.am index 48396bb8..ceb71bb1 100644 --- a/exec/Makefile.am +++ b/exec/Makefile.am @@ -94,7 +94,7 @@ else libtotem_pg.so.$(SONAME): $(TOTEM_OBJS) $(CC) -shared -o $@ \ -Wl,-soname=libtotem_pg.so.$(SOMAJOR) \ - $^ $(LDFLAGS) -lpthread + $^ $(LDFLAGS) $(NSS_LDFLAGS) -lpthread ln -sf libtotem_pg.so.$(SONAME) libtotem_pg.so ln -sf libtotem_pg.so.$(SONAME) libtotem_pg.so.$(SOMAJOR) diff --git a/exec/apidef.c b/exec/apidef.c index 34d8ebfa..f364561a 100644 --- a/exec/apidef.c +++ b/exec/apidef.c @@ -116,6 +116,7 @@ static struct corosync_api_v1 apidef_corosync_api_v1 = { .totem_ifaces_get = totempg_ifaces_get, .totem_ifaces_print = totempg_ifaces_print, .totem_ip_print = totemip_print, + .totem_crypto_set = totempg_crypto_set, .totem_callback_token_create = totempg_callback_token_create, .tpg_init = totempg_groups_initialize, .tpg_exit = NULL, /* missing from totempg api */ diff --git a/exec/totemconfig.c b/exec/totemconfig.c index f4c1f168..4bf016ef 100644 --- a/exec/totemconfig.c +++ b/exec/totemconfig.c @@ -56,6 +56,13 @@ #include #include +#ifdef HAVE_LIBNSS +#include +#include +#include +#include +#endif + #include "util.h" #include "totemconfig.h" #include "tlist.h" /* for HZ */ @@ -218,6 +225,46 @@ static void totem_volatile_config_read ( } +static void totem_get_crypto_type( + const struct objdb_iface_ver0 *objdb, + hdb_handle_t object_totem_handle, + struct totem_config *totem_config) +{ + const char *str; + + totem_config->crypto_accept = TOTEM_CRYPTO_ACCEPT_OLD; + if (!objdb_get_string (objdb, object_totem_handle, "crypto_accept", &str)) { + if (strcmp(str, "new") == 0) { + totem_config->crypto_accept = TOTEM_CRYPTO_ACCEPT_NEW; + } + } + + totem_config->crypto_type = TOTEM_CRYPTO_SOBER; + +#ifdef HAVE_LIBNSS + /* + * We must set these even if the key does not exist. + * Encryption type can be set on-the-fly using CFG + */ + totem_config->crypto_crypt_type = CKM_AES_CBC_PAD; + totem_config->crypto_sign_type = CKM_SHA256_RSA_PKCS; +#endif + + if (!objdb_get_string (objdb, object_totem_handle, "crypto_type", &str)) { + if (strcmp(str, "sober") == 0) { + return; + } +#ifdef HAVE_LIBNSS + if (strcmp(str, "nss") == 0) { + totem_config->crypto_type = TOTEM_CRYPTO_NSS; + + } +#endif + } +} + + + extern int totem_config_read ( struct objdb_iface_ver0 *objdb, struct totem_config *totem_config, @@ -263,6 +310,11 @@ printf ("couldn't find totem handle\n"); totem_config->secauth = 0; } } + + if (totem_config->secauth == 1) { + totem_get_crypto_type(objdb, object_totem_handle, totem_config); + } + if (!objdb_get_string (objdb, object_totem_handle, "rrp_mode", &str)) { strcpy (totem_config->rrp_mode, str); } diff --git a/exec/totemmrp.c b/exec/totemmrp.c index 949756ba..a9e6a7a0 100644 --- a/exec/totemmrp.c +++ b/exec/totemmrp.c @@ -210,6 +210,13 @@ int totemmrp_ifaces_get ( return (res); } +int totemmrp_crypto_set ( + unsigned int type) +{ + return totemsrp_crypto_set (totemsrp_handle_in, + type); +} + unsigned int totemmrp_my_nodeid_get (void) { return (totemsrp_my_nodeid_get (totemsrp_handle_in)); diff --git a/exec/totemmrp.h b/exec/totemmrp.h index 8ae93783..e10ae71d 100644 --- a/exec/totemmrp.h +++ b/exec/totemmrp.h @@ -108,6 +108,8 @@ extern unsigned int totemmrp_my_nodeid_get (void); extern int totemmrp_my_family_get (void); +extern int totemmrp_crypto_set (unsigned int); + extern int totemmrp_ring_reenable (void); #endif /* TOTEMMRP_H_DEFINED */ diff --git a/exec/totemnet.c b/exec/totemnet.c index 2b302730..54a6b1fd 100644 --- a/exec/totemnet.c +++ b/exec/totemnet.c @@ -69,8 +69,14 @@ #include "crypto.h" -#define MCAST_SOCKET_BUFFER_SIZE (TRANSMITS_ALLOWED * FRAME_SIZE_MAX) +#ifdef HAVE_LIBNSS +#include +#include +#include +#include +#endif +#define MCAST_SOCKET_BUFFER_SIZE (TRANSMITS_ALLOWED * FRAME_SIZE_MAX) #define NETIF_STATE_REPORT_UP 1 #define NETIF_STATE_REPORT_DOWN 2 @@ -101,6 +107,12 @@ struct totemnet_instance { prng_state totemnet_prng_state; +#ifdef HAVE_LIBNSS + SECItem *nss_sec_param; + PK11SymKey *nss_sym_key; + unsigned char nss_iv_data[16]; +#endif + unsigned char totemnet_private_key[1024]; unsigned int totemnet_private_key_len; @@ -234,9 +246,11 @@ do { \ level, (const char *)format, ##args); \ } while (0); -static int authenticate_and_decrypt ( + +static int authenticate_and_decrypt_sober ( struct totemnet_instance *instance, - struct iovec *iov) + struct iovec *iov, + unsigned int iov_len) { unsigned char keys[48]; struct security_header *header = iov[0].iov_base; @@ -280,7 +294,6 @@ static int authenticate_and_decrypt ( hmac_done (&instance->totemnet_hmac_state, digest_comparison, &len); if (memcmp (digest_comparison, header->hash_digest, len) != 0) { - log_printf (instance->totemnet_log_level_security, "Received message has invalid digest... ignoring.\n"); return (-1); } @@ -294,13 +307,333 @@ static int authenticate_and_decrypt ( return (0); } -static void encrypt_and_sign_worker ( + +static void init_sober_crypto( + struct totemnet_instance *instance) +{ + log_printf(instance->totemnet_log_level_notice, "Initialising SOBER128 crypto\n"); + rng_make_prng (128, PRNG_SOBER, &instance->totemnet_prng_state, NULL); +} + +#ifdef HAVE_LIBNSS + +static unsigned char *copy_from_iovec( + const struct iovec *iov, + unsigned int iov_len, + size_t *buf_size) +{ + int i; + size_t bufptr; + size_t buflen = 0; + unsigned char *newbuf; + + for (i=0; i buf_size) { + copylen = buf_size - bufptr; + } + memcpy(iov[i].iov_base, buf+bufptr, copylen); + bufptr += copylen; + if (iov[i].iov_len != copylen) { + iov[i].iov_len = copylen; + return; + } + } +} + + +static void init_nss_crypto( + struct totemnet_instance *instance) +{ + PK11SlotInfo* slot = NULL; + SECItem key_item, iv_item; + SECStatus rv; + + log_printf(instance->totemnet_log_level_notice, "Initialising NSS crypto\n"); + rv = NSS_NoDB_Init("."); + if (rv != SECSuccess) + { + log_printf(instance->totemnet_log_level_security, "NSS initialization failed (err %d)\n", + PR_GetError()); + goto out; + } + + slot = PK11_GetBestSlot(instance->totem_config->crypto_crypt_type, NULL); + if (slot == NULL) + { + log_printf(instance->totemnet_log_level_security, "Unable to find security slot (err %d)\n", + PR_GetError()); + goto out; + } + + /* + * Make the private key into a SymKey that we can use + */ + key_item.type = siBuffer; + key_item.data = instance->totem_config->private_key; + key_item.len = 32; /* Use 128 bits */ + + instance->nss_sym_key = PK11_ImportSymKey(slot, instance->totem_config->crypto_crypt_type, + PK11_OriginUnwrap, CKA_ENCRYPT|CKA_DECRYPT|CKA_SIGN, + &key_item, NULL); + if (instance->nss_sym_key == NULL) + { + log_printf(instance->totemnet_log_level_security, "Failure to import key into NSS (err %d)\n", + PR_GetError()); + goto out; + } + + /* set up the PKCS11 encryption paramters. + * when not using CBC mode, iv_item.data and iv_item.len can be 0, or you + * can simply pass NULL for the iv parameter in PK11_ParamFromIV func + */ + rng_get_bytes(instance->nss_iv_data, sizeof(instance->nss_iv_data), NULL); + iv_item.type = siBuffer; + iv_item.data = instance->nss_iv_data; + iv_item.len = sizeof(instance->nss_iv_data); + instance->nss_sec_param = PK11_ParamFromIV(instance->totem_config->crypto_crypt_type, &iv_item); + if (instance->nss_sec_param == NULL) + { + log_printf(instance->totemnet_log_level_security, "Failure to set up PKCS11 param (err %d)\n", + PR_GetError()); + goto out; + } + +out: + return; +} + +static int encrypt_and_sign_nss ( struct totemnet_instance *instance, unsigned char *buf, size_t *buf_len, const struct iovec *iovec, - size_t iov_len, - prng_state *prng_state_in) + unsigned int iov_len) +{ + PK11Context* enc_context = NULL; + SECStatus rv1, rv2; + int tmp1_outlen; + unsigned int tmp2_outlen; + unsigned char *inbuf; + unsigned char *data; + unsigned char *outdata; + size_t datalen; + SECItem no_params; + struct security_header *header; + + no_params.type = siBuffer; + no_params.data = 0; + no_params.len = 0; + + tmp1_outlen = tmp2_outlen = 0; + inbuf = copy_from_iovec(iovec, iov_len, &datalen); + if (!inbuf) { + log_printf(instance->totemnet_log_level_security, "malloc error copying buffer from iovec\n"); + return -1; + } + + data = inbuf + sizeof (struct security_header); + datalen -= sizeof (struct security_header); + + outdata = buf + sizeof (struct security_header); + header = (struct security_header *)buf; + + /* + * Create cipher context for encryption + */ + enc_context = PK11_CreateContextBySymKey(instance->totem_config->crypto_crypt_type, CKA_ENCRYPT, + instance->nss_sym_key, instance->nss_sec_param); + if (!enc_context) { + char err[1024]; + PR_GetErrorText(err); + err[PR_GetErrorTextLength()] = 0; + log_printf(instance->totemnet_log_level_security, "PK11_CreateContext failed (encrypt) crypt_type=%d (err %d): %s\n", + instance->totem_config->crypto_crypt_type, + PR_GetError(), err); + return -1; + } + rv1 = PK11_CipherOp(enc_context, outdata, + &tmp1_outlen, FRAME_SIZE_MAX - sizeof(struct security_header), + data, datalen); + rv2 = PK11_DigestFinal(enc_context, outdata + tmp1_outlen, &tmp2_outlen, + FRAME_SIZE_MAX - tmp1_outlen); + PK11_DestroyContext(enc_context, PR_TRUE); + + *buf_len = tmp1_outlen + tmp2_outlen; + free(inbuf); + + if (rv1 != SECSuccess || rv2 != SECSuccess) + goto out; + + /* Now do the digest */ + enc_context = PK11_CreateContextBySymKey(CKM_MD5, CKA_DIGEST, instance->nss_sym_key, &no_params); + if (!enc_context) { + char err[1024]; + PR_GetErrorText(err); + err[PR_GetErrorTextLength()] = 0; + log_printf(instance->totemnet_log_level_security, "encrypt: PK11_CreateContext failed (digest) err %d: %s\n", + PR_GetError(), err); + return -1; + } + + PK11_DigestBegin(enc_context); + + rv1 = PK11_DigestOp(enc_context, outdata, *buf_len); + rv2 = PK11_DigestFinal(enc_context, header->hash_digest, &tmp2_outlen, sizeof(header->hash_digest)); + + PK11_DestroyContext(enc_context, PR_TRUE); + + if (rv1 != SECSuccess || rv2 != SECSuccess) + goto out; + + memcpy(header->salt, instance->nss_iv_data, sizeof(instance->nss_iv_data)); + + *buf_len += sizeof(struct security_header); + return 0; + +out: + return -1; +} + + +static int authenticate_and_decrypt_nss ( + struct totemnet_instance *instance, + struct iovec *iov, + unsigned int iov_len) +{ + PK11Context* enc_context = NULL; + SECStatus rv1, rv2; + int tmp1_outlen; + unsigned int tmp2_outlen; + unsigned char outbuf[FRAME_SIZE_MAX]; + unsigned char digest[HMAC_HASH_SIZE]; + unsigned char *outdata; + int result_len; + unsigned char *data; + unsigned char *inbuf; + size_t datalen; + struct security_header *header = iov[0].iov_base; + SECItem no_params; + SECItem ivdata; + + no_params.type = siBuffer; + no_params.data = 0; + no_params.len = 0; + + tmp1_outlen = tmp2_outlen = 0; + if (iov_len > 1) { + inbuf = copy_from_iovec(iov, iov_len, &datalen); + if (!inbuf) { + log_printf(instance->totemnet_log_level_security, "malloc error copying buffer from iovec\n"); + return -1; + } + } + else { + inbuf = iov[0].iov_base; + datalen = iov[0].iov_len; + } + data = inbuf + sizeof (struct security_header); + datalen -= sizeof (struct security_header); + + outdata = outbuf + sizeof (struct security_header); + + /* Check the digest */ + enc_context = PK11_CreateContextBySymKey(CKM_MD5, CKA_DIGEST, instance->nss_sym_key, &no_params); + if (!enc_context) { + char err[1024]; + PR_GetErrorText(err); + err[PR_GetErrorTextLength()] = 0; + log_printf(instance->totemnet_log_level_security, "PK11_CreateContext failed (check digest) err %d: %s\n", + PR_GetError(), err); + return -1; + } + + PK11_DigestBegin(enc_context); + + rv1 = PK11_DigestOp(enc_context, data, datalen); + rv2 = PK11_DigestFinal(enc_context, digest, &tmp2_outlen, sizeof(digest)); + + PK11_DestroyContext(enc_context, PR_TRUE); + + if (rv1 != SECSuccess || rv2 != SECSuccess) { + log_printf(instance->totemnet_log_level_security, "Digest check failed\n"); + return -1; + } + + if (memcmp(digest, header->hash_digest, tmp2_outlen) != 0) { + log_printf(instance->totemnet_log_level_error, "Digest does not match\n"); + return -1; + } + + /* Create cipher context for decryption */ + ivdata.type = siBuffer; + ivdata.data = header->salt; + ivdata.len = sizeof(header->salt); + enc_context = PK11_CreateContextBySymKey(instance->totem_config->crypto_crypt_type, CKA_DECRYPT, + instance->nss_sym_key, &ivdata); + if (!enc_context) { + log_printf(instance->totemnet_log_level_security, "PK11_CreateContext (decrypt) failed (err %d)\n", + PR_GetError()); + return -1; + } + + rv1 = PK11_CipherOp(enc_context, outdata, &tmp1_outlen, + sizeof(outbuf) - sizeof (struct security_header), + data, datalen); + if (rv1 != SECSuccess) { + log_printf(instance->totemnet_log_level_security, "PK11_CipherOp (decrypt) failed (err %d)\n", + PR_GetError()); + } + rv2 = PK11_DigestFinal(enc_context, outdata + tmp1_outlen, &tmp2_outlen, + sizeof(outbuf) - tmp1_outlen); + PK11_DestroyContext(enc_context, PR_TRUE); + result_len = tmp1_outlen + tmp2_outlen + sizeof (struct security_header); + + /* Copy it back to the buffer */ + copy_to_iovec(iov, iov_len, outbuf, result_len); + if (iov_len > 1) + free(inbuf); + + if (rv1 != SECSuccess || rv2 != SECSuccess) + return -1; + + return 0; +} +#endif + +static int encrypt_and_sign_sober ( + struct totemnet_instance *instance, + unsigned char *buf, + size_t *buf_len, + const struct iovec *iovec, + unsigned int iov_len) { int i; unsigned char *addr; @@ -314,6 +647,7 @@ static void encrypt_and_sign_worker ( hmac_state hmac_st; prng_state keygen_prng_state; prng_state stream_prng_state; + prng_state *prng_state_in = &instance->totemnet_prng_state; header = (struct security_header *)buf; addr = buf + sizeof (struct security_header); @@ -374,8 +708,124 @@ static void encrypt_and_sign_worker ( hmac_done (&hmac_st, header->hash_digest, &len); *buf_len = outlen; + + return 0; } +static int encrypt_and_sign_worker ( + struct totemnet_instance *instance, + unsigned char *buf, + size_t *buf_len, + const struct iovec *iovec, + unsigned int iov_len) +{ + if (instance->totem_config->crypto_type == TOTEM_CRYPTO_SOBER || + instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_OLD) + return encrypt_and_sign_sober(instance, buf, buf_len, iovec, iov_len); +#ifdef HAVE_LIBNSS + if (instance->totem_config->crypto_type == TOTEM_CRYPTO_NSS) + return encrypt_and_sign_nss(instance, buf, buf_len, iovec, iov_len); +#endif + return -1; +} + +static int authenticate_and_decrypt ( + struct totemnet_instance *instance, + struct iovec *iov, + unsigned int iov_len) +{ + unsigned char type; + unsigned char *endbuf = iov[iov_len-1].iov_base; + int res = -1; + + /* + * Get the encryption type and remove it from the buffer + */ + type = endbuf[iov[iov_len-1].iov_len-1]; + iov[iov_len-1].iov_len -= 1; + + if (type == TOTEM_CRYPTO_SOBER) + res = authenticate_and_decrypt_sober(instance, iov, iov_len); + + /* + * Only try higher crypto options if NEW has been requested + */ + if (instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_NEW) { +#ifdef HAVE_LIBNSS + if (type == TOTEM_CRYPTO_NSS) + res = authenticate_and_decrypt_nss(instance, iov, iov_len); +#endif + } + + /* + * If it failed, then try decrypting the whole packet as it might be + * from aisexec + */ + if (res == -1) { + iov[iov_len-1].iov_len += 1; + res = authenticate_and_decrypt_sober(instance, iov, iov_len); + } + + return res; +} + +static void init_crypto( + struct totemnet_instance *instance) +{ + /* + * If we are expecting NEW crypto type then initialise all available + * crypto options. For OLD then we only need SOBER128. + */ + + init_sober_crypto(instance); + + if (instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_OLD) + return; + +#ifdef HAVE_LIBNSS + init_nss_crypto(instance); +#endif +} + +int totemnet_crypto_set (hdb_handle_t handle, + unsigned int type) +{ + struct totemnet_instance *instance; + int res = 0; + + res = hdb_handle_get (&totemnet_instance_database, handle, + (void *)&instance); + if (res != 0) { + res = ENOENT; + goto error_exit; + } + + /* + * Can't set crypto type if OLD is selected + */ + if (instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_OLD) { + res = -1; + } + else { + /* + * Validate number + */ + if (type == TOTEM_CRYPTO_SOBER || + type == TOTEM_CRYPTO_NSS) { + instance->totem_config->crypto_type = type; + log_printf(instance->totemnet_log_level_security, "Encryption type set to %d\n", type); + } + else { + res = -1; + } + } + hdb_handle_put (&totemnet_instance_database, handle); + +error_exit: + return res; +} + + static inline void ucast_sendmsg ( struct totemnet_instance *instance, struct totem_ip_address *system_to, @@ -408,8 +858,14 @@ static inline void ucast_sendmsg ( encrypt_data, &buf_len, iovec_encrypt, - iov_len_in + 1, - &instance->totemnet_prng_state); + iov_len_in + 1); + + if (instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_NEW) { + encrypt_data[buf_len++] = instance->totem_config->crypto_type; + } + else { + encrypt_data[buf_len++] = 0; + } iovec_encrypt[0].iov_base = encrypt_data; iovec_encrypt[0].iov_len = buf_len; @@ -472,8 +928,14 @@ static inline void mcast_sendmsg ( encrypt_data, &buf_len, iovec_encrypt, - iov_len_in + 1, - &instance->totemnet_prng_state); + iov_len_in + 1); + + if (instance->totem_config->crypto_accept == TOTEM_CRYPTO_ACCEPT_NEW) { + encrypt_data[buf_len++] = instance->totem_config->crypto_type; + } + else { + encrypt_data[buf_len++] = 0; + } iovec_encrypt[0].iov_base = encrypt_data; iovec_encrypt[0].iov_len = buf_len; @@ -546,8 +1008,7 @@ static void totemnet_mcast_worker_fn (void *thread_state, void *work_item_in) encrypt_and_sign_worker ( instance, totemnet_mcast_thread_state->iobuf, &buf_len, - work_item->iovec, work_item->iov_len + 1, - &totemnet_mcast_thread_state->prng_state); + work_item->iovec, work_item->iov_len + 1); iovec_sendmsg = &iovec_encrypted; iovec_sendmsg->iov_base = totemnet_mcast_thread_state->iobuf; @@ -660,8 +1121,9 @@ static int net_deliver_fn ( * Authenticate and if authenticated, decrypt datagram */ - res = authenticate_and_decrypt (instance, iovec); + res = authenticate_and_decrypt (instance, iovec, 1); if (res == -1) { + log_printf (instance->totemnet_log_level_security, "Received message has invalid digest... ignoring.\n"); log_printf (instance->totemnet_log_level_security, "Invalid packet data\n"); iovec->iov_len = FRAME_SIZE_MAX; @@ -698,7 +1160,7 @@ static int netif_determine ( res = totemip_iface_check (bindnet, bound_to, interface_up, interface_num, -+ 0); // TODO andrew can address this instance->totem_config->clear_node_high_bit); + 0); // TODO andrew can address this instance->totem_config->clear_node_high_bit); return (res); @@ -1200,7 +1662,7 @@ int totemnet_initialize ( instance->totemnet_private_key_len = totem_config->private_key_len; - rng_make_prng (128, PRNG_SOBER, &instance->totemnet_prng_state, NULL); + init_crypto(instance); /* * Initialize local variables for totemnet @@ -1233,8 +1695,6 @@ int totemnet_initialize ( instance->handle = *handle; - rng_make_prng (128, PRNG_SOBER, &instance->totemnet_prng_state, NULL); - totemip_localhost (instance->mcast_address.family, &localhost); netif_down_check (instance); diff --git a/exec/totemnet.h b/exec/totemnet.h index ea3f7760..2b8ed726 100644 --- a/exec/totemnet.h +++ b/exec/totemnet.h @@ -105,4 +105,8 @@ extern int totemnet_token_target_set ( hdb_handle_t handle, const struct totem_ip_address *token_target); +extern int totemnet_crypto_set ( + hdb_handle_t handle, + unsigned int type); + #endif /* TOTEMNET_H_DEFINED */ diff --git a/exec/totempg.c b/exec/totempg.c index 9c22fc48..7135be4e 100644 --- a/exec/totempg.c +++ b/exec/totempg.c @@ -1293,6 +1293,17 @@ int totempg_ifaces_get ( return (res); } +int totempg_crypto_set ( + unsigned int type) +{ + int res; + + res = totemmrp_crypto_set ( + type); + + return (res); +} + int totempg_ring_reenable (void) { int res; diff --git a/exec/totemrrp.c b/exec/totemrrp.c index 59982a53..386923b7 100644 --- a/exec/totemrrp.c +++ b/exec/totemrrp.c @@ -1710,6 +1710,26 @@ error_exit: return (res); } +int totemrrp_crypto_set ( + hdb_handle_t handle, + unsigned int type) +{ + int res; + struct totemrrp_instance *instance; + + res = hdb_handle_get (&totemrrp_instance_database, handle, + (void *)&instance); + if (res != 0) { + return (0); + } + + res = totemnet_crypto_set(instance->net_handles[0], type); + + hdb_handle_put (&totemrrp_instance_database, handle); + return (res); +} + + int totemrrp_ring_reenable ( hdb_handle_t handle) { diff --git a/exec/totemrrp.h b/exec/totemrrp.h index b5926b2f..aa6075d7 100644 --- a/exec/totemrrp.h +++ b/exec/totemrrp.h @@ -112,6 +112,10 @@ extern int totemrrp_ifaces_get ( char ***status, unsigned int *iface_count); +extern int totemrrp_crypto_set ( + hdb_handle_t handle, + unsigned int type); + extern int totemrrp_ring_reenable ( hdb_handle_t handle); diff --git a/exec/totemsrp.c b/exec/totemsrp.c index 50f587f6..8005d930 100644 --- a/exec/totemsrp.c +++ b/exec/totemsrp.c @@ -79,6 +79,7 @@ #include #include "totemsrp.h" #include "totemrrp.h" +#include "totemnet.h" #include "wthread.h" #include "crypto.h" @@ -942,6 +943,26 @@ error_exit: return (res); } +int totemsrp_crypto_set ( + hdb_handle_t handle, + unsigned int type) +{ + int res; + struct totemsrp_instance *instance; + + res = hdb_handle_get (&totemsrp_instance_database, handle, + (void *)&instance); + if (res != 0) { + return (0); + } + + res = totemrrp_crypto_set(instance->totemrrp_handle, type); + + hdb_handle_put (&totemsrp_instance_database, handle); + return (res); +} + + unsigned int totemsrp_my_nodeid_get ( hdb_handle_t handle) { diff --git a/exec/totemsrp.h b/exec/totemsrp.h index 5f76dffc..965dc886 100644 --- a/exec/totemsrp.h +++ b/exec/totemsrp.h @@ -108,6 +108,10 @@ extern unsigned int totemsrp_my_nodeid_get ( extern int totemsrp_my_family_get ( hdb_handle_t handle); +extern int totemsrp_crypto_set ( + hdb_handle_t handle, + unsigned int type); + extern int totemsrp_ring_reenable ( hdb_handle_t handle); diff --git a/include/corosync/cfg.h b/include/corosync/cfg.h index 04d3dbc5..b47a7e57 100644 --- a/include/corosync/cfg.h +++ b/include/corosync/cfg.h @@ -228,6 +228,11 @@ corosync_cfg_local_get ( corosync_cfg_handle_t handle, unsigned int *local_nodeid); +cs_error_t +corosync_cfg_crypto_set ( + corosync_cfg_handle_t handle, + unsigned int type); + #ifdef __cplusplus } #endif diff --git a/include/corosync/engine/coroapi.h b/include/corosync/engine/coroapi.h index a0c15005..333610ca 100644 --- a/include/corosync/engine/coroapi.h +++ b/include/corosync/engine/coroapi.h @@ -443,6 +443,7 @@ struct corosync_api_v1 { const char *(*totem_ip_print) (const struct totem_ip_address *addr); + int (*totem_crypto_set) (unsigned int type); int (*totem_callback_token_create) ( void **handle_out, diff --git a/include/corosync/ipc_cfg.h b/include/corosync/ipc_cfg.h index d97d6738..eca0c3db 100644 --- a/include/corosync/ipc_cfg.h +++ b/include/corosync/ipc_cfg.h @@ -52,7 +52,8 @@ enum req_lib_cfg_types { MESSAGE_REQ_CFG_TRYSHUTDOWN = 9, MESSAGE_REQ_CFG_REPLYTOSHUTDOWN = 10, MESSAGE_REQ_CFG_GET_NODE_ADDRS = 11, - MESSAGE_REQ_CFG_LOCAL_GET = 12 + MESSAGE_REQ_CFG_LOCAL_GET = 12, + MESSAGE_REQ_CFG_CRYPTO_SET = 13 }; enum res_lib_cfg_types { @@ -69,7 +70,8 @@ enum res_lib_cfg_types { MESSAGE_RES_CFG_TESTSHUTDOWN = 10, MESSAGE_RES_CFG_GET_NODE_ADDRS = 11, MESSAGE_RES_CFG_LOCAL_GET = 12, - MESSAGE_RES_CFG_REPLYTOSHUTDOWN = 13 + MESSAGE_RES_CFG_REPLYTOSHUTDOWN = 13, + MESSAGE_RES_CFG_CRYPTO_SET = 14, }; struct req_lib_cfg_statetrack { @@ -205,6 +207,15 @@ struct res_lib_cfg_local_get { mar_uint32_t local_nodeid __attribute__((aligned(8))); }; +struct req_lib_cfg_crypto_set { + coroipc_response_header_t header __attribute__((aligned(8))); + mar_uint32_t type __attribute__((aligned(8))); +}; + +struct res_lib_cfg_crypto_set { + coroipc_response_header_t header __attribute__((aligned(8))); +}; + typedef enum { AIS_AMF_ADMINISTRATIVETARGET_SERVICEUNIT = 0, AIS_AMF_ADMINISTRATIVETARGET_SERVICEGROUP = 1, diff --git a/include/corosync/totem/totem.h b/include/corosync/totem/totem.h index 590e6fca..2969900f 100644 --- a/include/corosync/totem/totem.h +++ b/include/corosync/totem/totem.h @@ -145,6 +145,12 @@ struct totem_config { unsigned int max_messages; const char *vsf_type; + + enum { TOTEM_CRYPTO_SOBER=0, TOTEM_CRYPTO_NSS } crypto_type; + enum { TOTEM_CRYPTO_ACCEPT_OLD=0, TOTEM_CRYPTO_ACCEPT_NEW } crypto_accept; + + int crypto_crypt_type; + int crypto_sign_type; }; #define TOTEM_CONFIGURATION_TYPE diff --git a/include/corosync/totem/totempg.h b/include/corosync/totem/totempg.h index 89bfd7c4..d693b84c 100644 --- a/include/corosync/totem/totempg.h +++ b/include/corosync/totem/totempg.h @@ -145,6 +145,8 @@ extern unsigned int totempg_my_nodeid_get (void); extern int totempg_my_family_get (void); +extern int totempg_crypto_set (unsigned int type); + extern int totempg_ring_reenable (void); #endif /* TOTEMPG_H_DEFINED */ diff --git a/lib/cfg.c b/lib/cfg.c index 95b23952..9b67f36e 100644 --- a/lib/cfg.c +++ b/lib/cfg.c @@ -822,3 +822,44 @@ error_exit: return (error); } + +cs_error_t +corosync_cfg_crypto_set ( + corosync_cfg_handle_t handle, + unsigned int type) +{ + struct cfg_instance *cfg_instance; + struct req_lib_cfg_crypto_set req_lib_cfg_crypto_set; + struct res_lib_cfg_crypto_set res_lib_cfg_crypto_set; + struct iovec iov; + cs_error_t error; + + + error = hdb_error_to_cs(hdb_handle_get (&cfg_hdb, handle, (void *)&cfg_instance)); + if (error != CS_OK) { + return (error); + } + + req_lib_cfg_crypto_set.header.id = MESSAGE_REQ_CFG_CRYPTO_SET; + req_lib_cfg_crypto_set.header.size = sizeof (struct req_lib_cfg_crypto_set); + req_lib_cfg_crypto_set.type = type; + + iov.iov_base = &req_lib_cfg_crypto_set; + iov.iov_len = sizeof (struct req_lib_cfg_crypto_set); + + pthread_mutex_lock (&cfg_instance->response_mutex); + + error = coroipcc_msg_send_reply_receive (cfg_instance->ipc_ctx, + &iov, + 1, + &res_lib_cfg_crypto_set, + sizeof (struct res_lib_cfg_crypto_set)); + + pthread_mutex_unlock (&cfg_instance->response_mutex); + + if (error == CS_OK) + error = res_lib_cfg_crypto_set.header.error; + + (void)hdb_handle_put (&cfg_hdb, handle); + return (error); +} diff --git a/services/cfg.c b/services/cfg.c index babd4260..80360407 100644 --- a/services/cfg.c +++ b/services/cfg.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,8 @@ LOGSYS_DECLARE_SUBSYS ("CFG"); enum cfg_message_req_types { MESSAGE_REQ_EXEC_CFG_RINGREENABLE = 0, MESSAGE_REQ_EXEC_CFG_KILLNODE = 1, - MESSAGE_REQ_EXEC_CFG_SHUTDOWN = 2 + MESSAGE_REQ_EXEC_CFG_SHUTDOWN = 2, + MESSAGE_REQ_EXEC_CFG_CRYPTO_SET = 3 }; #define DEFAULT_SHUTDOWN_TIMEOUT 5 @@ -120,6 +122,10 @@ static void message_handler_req_exec_cfg_shutdown ( const void *message, unsigned int nodeid); +static void message_handler_req_exec_cfg_crypto_set ( + const void *message, + unsigned int nodeid); + static void exec_cfg_killnode_endian_convert (void *msg); static void message_handler_req_lib_cfg_ringstatusget ( @@ -174,6 +180,10 @@ static void message_handler_req_lib_cfg_local_get ( void *conn, const void *msg); +static void message_handler_req_lib_cfg_crypto_set ( + void *conn, + const void *msg); + /* * Service Handler Definition */ @@ -256,6 +266,12 @@ static struct corosync_lib_handler cfg_lib_engine[] = .response_size = sizeof (struct res_lib_cfg_local_get), .response_id = MESSAGE_RES_CFG_LOCAL_GET, .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED + }, + { /* 13 */ + .lib_handler_fn = message_handler_req_lib_cfg_crypto_set, + .response_size = sizeof (struct res_lib_cfg_crypto_set), + .response_id = MESSAGE_RES_CFG_CRYPTO_SET, + .flow_control = CS_LIB_FLOW_CONTROL_NOT_REQUIRED } }; @@ -270,6 +286,9 @@ static struct corosync_exec_handler cfg_exec_engine[] = }, { /* 2 */ .exec_handler_fn = message_handler_req_exec_cfg_shutdown, + }, + { /* 3 */ + .exec_handler_fn = message_handler_req_exec_cfg_crypto_set, } }; @@ -342,6 +361,11 @@ struct req_exec_cfg_killnode { mar_name_t reason __attribute__((aligned(8))); }; +struct req_exec_cfg_crypto_set { + coroipc_request_header_t header __attribute__((aligned(8))); + mar_uint32_t type __attribute__((aligned(8))); +}; + struct req_exec_cfg_shutdown { coroipc_request_header_t header __attribute__((aligned(8))); }; @@ -630,6 +654,19 @@ static void message_handler_req_exec_cfg_shutdown ( LEAVE(); } +static void message_handler_req_exec_cfg_crypto_set ( + const void *message, + unsigned int nodeid) +{ + const struct req_exec_cfg_crypto_set *req_exec_cfg_crypto_set = message; + ENTER(); + + log_printf(LOGSYS_LEVEL_NOTICE, "Node %d requested set crypto to %d\n", nodeid, req_exec_cfg_crypto_set->type); + + api->totem_crypto_set(req_exec_cfg_crypto_set->type); + LEAVE(); +} + /* * Library Interface Implementation @@ -1058,3 +1095,40 @@ static void message_handler_req_lib_cfg_local_get (void *conn, const void *msg) api->ipc_response_send(conn, &res_lib_cfg_local_get, sizeof(res_lib_cfg_local_get)); } + + +static void message_handler_req_lib_cfg_crypto_set ( + void *conn, + const void *msg) +{ + const struct req_lib_cfg_crypto_set *req_lib_cfg_crypto_set = msg; + struct res_lib_cfg_crypto_set res_lib_cfg_crypto_set; + struct req_exec_cfg_crypto_set req_exec_cfg_crypto_set; + struct iovec iovec; + int ret = CS_ERR_INVALID_PARAM; + + req_exec_cfg_crypto_set.header.size = + sizeof (struct req_exec_cfg_crypto_set); + req_exec_cfg_crypto_set.header.id = SERVICE_ID_MAKE (CFG_SERVICE, + MESSAGE_REQ_EXEC_CFG_CRYPTO_SET); + + /* + * Set it locally first so we can tell if it is allowed + */ + if (api->totem_crypto_set(req_lib_cfg_crypto_set->type) == 0) { + + req_exec_cfg_crypto_set.type = req_lib_cfg_crypto_set->type; + + iovec.iov_base = (char *)&req_exec_cfg_crypto_set; + iovec.iov_len = sizeof (struct req_exec_cfg_crypto_set); + assert (api->totem_mcast (&iovec, 1, TOTEM_SAFE) == 0); + ret = CS_OK; + } + + res_lib_cfg_crypto_set.header.size = sizeof(res_lib_cfg_crypto_set); + res_lib_cfg_crypto_set.header.id = MESSAGE_RES_CFG_CRYPTO_SET; + res_lib_cfg_crypto_set.header.error = ret; + + api->ipc_response_send(conn, &res_lib_cfg_crypto_set, + sizeof(res_lib_cfg_crypto_set)); +} diff --git a/tools/corosync-cfgtool.c b/tools/corosync-cfgtool.c index 52e4f93c..8de2a70c 100644 --- a/tools/corosync-cfgtool.c +++ b/tools/corosync-cfgtool.c @@ -211,6 +211,26 @@ static void showaddrs_do(int nodeid) (void)corosync_cfg_finalize (handle); } + +static void crypto_do(unsigned int type) +{ + cs_error_t result; + corosync_cfg_handle_t handle; + + printf ("Setting crypto to mode %d\n", type); + result = corosync_cfg_initialize (&handle, NULL); + if (result != CS_OK) { + printf ("Could not initialize corosync configuration API error %d\n", result); + exit (1); + } + result = corosync_cfg_crypto_set (handle, type); + if (result != CS_OK) { + printf ("Could not set crypto mode (error = %d)\n", result); + } + (void)corosync_cfg_finalize (handle); + +} + static void killnode_do(unsigned int nodeid) { cs_error_t result; @@ -241,6 +261,7 @@ static void usage_do (void) printf ("\t-l\tLoad a service identified by name.\n"); printf ("\t-u\tUnload a service identified by name.\n"); printf ("\t-a\tDisplay the IP address(es) of a node\n"); + printf ("\t-c\tSet the cryptography mode of cluster communications\n"); printf ("\t-k\tKill a node identified by node id.\n"); printf ("\t-H\tShutdown corosync cleanly on this node.\n"); } @@ -257,7 +278,7 @@ xstrdup (char const *s) } int main (int argc, char *argv[]) { - const char *options = "srl:u:v:k:a:hH"; + const char *options = "srl:u:v:k:a:c:hH"; int opt; int service_load = 0; unsigned int nodeid; @@ -294,6 +315,9 @@ int main (int argc, char *argv[]) { case 'a': showaddrs_do( atoi(optarg) ); break; + case 'c': + crypto_do( atoi(optarg) ); + break; case 'v': version = atoi (optarg); break;