Back to home page

Enduro/X

 
 

    


0001 /* 
0002 ** Cryptography related routines
0003 ** We have a poor logging options where, because logger might not be initialized
0004 ** when we will require to log some messages. Thus critical things will be logged
0005 ** to the userlog();
0006 ** The first byte of data will indicate the length of padding used.
0007 ** We also need to introduce standard library error handling there. This is needed
0008 ** for fact that we might want to give some explanation why decryption failed at
0009 ** at the application boot.
0010 ** @file crypto.c
0011 ** 
0012 ** -----------------------------------------------------------------------------
0013 ** Enduro/X Middleware Platform for Distributed Transaction Processing
0014 ** Copyright (C) 2015, Mavimax, Ltd. All Rights Reserved.
0015 ** This software is released under one of the following licenses:
0016 ** GPL or Mavimax's license for commercial use.
0017 ** -----------------------------------------------------------------------------
0018 ** GPL license:
0019 ** 
0020 ** This program is free software; you can redistribute it and/or modify it under
0021 ** the terms of the GNU General Public License as published by the Free Software
0022 ** Foundation; either version 2 of the License, or (at your option) any later
0023 ** version.
0024 **
0025 ** This program is distributed in the hope that it will be useful, but WITHOUT ANY
0026 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0027 ** PARTICULAR PURPOSE. See the GNU General Public License for more details.
0028 **
0029 ** You should have received a copy of the GNU General Public License along with
0030 ** this program; if not, write to the Free Software Foundation, Inc., 59 Temple
0031 ** Place, Suite 330, Boston, MA 02111-1307 USA
0032 **
0033 ** -----------------------------------------------------------------------------
0034 ** A commercial use license is available from Mavimax, Ltd
0035 ** contact@mavimax.com
0036 ** -----------------------------------------------------------------------------
0037 */
0038 #include <ndrx_config.h>
0039 #include <string.h>
0040 #include <stdio.h>
0041 #include <stdlib.h>
0042 #include <memory.h>
0043 #include <time.h>
0044 #include <sys/time.h>
0045 #include <unistd.h>
0046 #include <stdarg.h>
0047 #include <arpa/inet.h>
0048 #include <errno.h>
0049 
0050 #include <ndrstandard.h>
0051 #include <ndebug.h>
0052 #include <nstdutil.h>
0053 #include <sys_unix.h>
0054 #include <exsha1.h>
0055 #include <excrypto.h>
0056 #include <userlog.h>
0057 #include <expluginbase.h>
0058 #include <exaes.h>
0059 #include <exbase64.h>
0060 
0061 /*---------------------------Externs------------------------------------*/
0062 /*---------------------------Macros-------------------------------------*/
0063 #define IV_INIT {   0xab, 0xcc, 0x1b, 0xc2, \
0064                     0x3d, 0xe4, 0x44, 0x11, \
0065                     0x30, 0x54, 0x34, 0x09, \
0066                     0xef, 0xaf, 0xfc, 0xf5 \
0067                 }
0068       
0069 /* #define CRYPTODEBUG */
0070 
0071 /* #define CRYPTODEBUG_DUMP */
0072 
0073 #define CRYPTO_LEN_PFX_BYTES    4
0074 
0075 #define API_ENTRY {_Nunset_error();}
0076 
0077 /*---------------------------Enums--------------------------------------*/
0078 /*---------------------------Typedefs-----------------------------------*/
0079 /*---------------------------Globals------------------------------------*/
0080 /*---------------------------Statics------------------------------------*/
0081 /*---------------------------Prototypes---------------------------------*/
0082 
0083 
0084 /**
0085  * Get encryption key.
0086  * This is used in expluginbase.c for default crypto function.
0087  * @param key_out
0088  * @param klen
0089  * @return EXSUCCEED/EXFAIL
0090  */
0091 expublic int ndrx_crypto_getkey_std(char *key_out, int key_out_bufsz)
0092 {
0093     int ret = EXSUCCEED;
0094     long len;
0095     API_ENTRY;
0096 
0097     /* TODO Add some caching over the crypto key! */
0098 
0099     if (EXSUCCEED!=ndrx_sys_get_hostname(key_out, key_out_bufsz))
0100     {
0101         _Nset_error_fmt(NEUNIX, "Failed to get hostname!");
0102         EXFAIL_OUT(ret);
0103     }
0104     
0105     len = strlen(key_out);
0106     
0107     
0108     if (len+1 /*incl EOS*/ < key_out_bufsz)
0109     {
0110         snprintf(key_out+len, key_out_bufsz - len, "%s", 
0111                 ndrx_sys_get_cur_username());
0112     }
0113     
0114 #ifdef CRYPTODEBUG
0115     NDRX_LOG_EARLY(log_debug, "Password built: [%s]", key_out);
0116 #endif
0117     
0118 out:
0119     return ret;
0120 }
0121 
0122 /**
0123  * Get final key
0124  * @param sha1key, buffer size NDRX_ENCKEY_BUFSZ
0125  * @return EXSUCCEED/EXFAIL
0126  */
0127 exprivate int ndrx_get_final_key(char *sha1key)
0128 {
0129     int ret = EXSUCCEED;
0130     char password[PATH_MAX+1];
0131     
0132     /* first we take password & hash it */
0133     
0134     if (ndrx_G_plugins.p_ndrx_crypto_getkey(password, sizeof(password)))
0135     {
0136         userlog("Failed to get encryption key by plugin "
0137                 "function, provider: [%s]", 
0138                 ndrx_G_plugins.ndrx_crypto_getkey_provider);
0139         
0140         _Nset_error_fmt(NEPLUGIN, "Failed to get encryption key by plugin "
0141                 "function, provider: [%s]", 
0142                 ndrx_G_plugins.ndrx_crypto_getkey_provider);
0143         
0144         EXFAIL_OUT(ret);
0145     }
0146     
0147 #ifdef CRYPTODEBUG
0148     NDRX_LOG_EARLY(log_debug, "Clear password: [%s]", password);
0149 #endif
0150     
0151     /* make sha1 */
0152     EXSHA1( sha1key, password, strlen(password) );
0153     
0154 #ifdef CRYPTODEBUG_DUMP
0155     NDRX_DUMP(log_debug, "SHA1 key", sha1key, NDRX_ENCKEY_BUFSZ);
0156 #endif
0157     
0158 out:
0159     return ret;
0160 }
0161 
0162 /**
0163  * Decrypt data block (internal version, no API entry)
0164  * @param input input data block
0165  * @param ibufsz input data block buffer size
0166  * @param output encrypted data block
0167  * @param obufsz encrypted data block buffer size
0168  * @return EXSUCCEED/EXFAIL
0169  */
0170 exprivate int ndrx_crypto_enc_int(char *input, long ilen, char *output, long *olen)
0171 {
0172     int ret = EXSUCCEED;
0173     char sha1key[NDRX_ENCKEY_BUFSZ];
0174     long size_estim;
0175     uint32_t *len_ind = (uint32_t *)output;
0176     uint8_t  iv[]  = IV_INIT;
0177     
0178     /* encrypt data block */
0179     
0180     if (EXSUCCEED!=ndrx_get_final_key(sha1key))
0181     {
0182         EXFAIL_OUT(ret);
0183     }
0184     
0185     /* estimate the encrypted data len round to */
0186     size_estim = 
0187             ((ilen + NDRX_ENC_BLOCK_SIZE - 1) / NDRX_ENC_BLOCK_SIZE) 
0188             * NDRX_ENC_BLOCK_SIZE  
0189             + CRYPTO_LEN_PFX_BYTES;
0190     
0191 #ifdef CRYPTODEBUG
0192     NDRX_LOG_EARLY(log_debug, "%s: Data size: %ld, estimated: %ld, output buffer: %ld",
0193             __func__, ilen, size_estim, *olen);
0194 #endif
0195 #ifdef CRYPTODEBUG_DUMP
0196     NDRX_DUMP(log_debug, "About to encrypt: ", input, ilen);
0197 #endif
0198     if (size_estim > *olen)
0199     {
0200         userlog("Encryption output buffer to short, estimated: %ld, but on input: %ld",
0201                 size_estim, *olen);
0202         
0203         _Nset_error_fmt(NENOSPACE, "Encryption output buffer to short, "
0204                 "estimated: %ld, but on input: %ld",
0205                 size_estim, *olen);
0206         
0207         EXFAIL_OUT(ret);
0208     }
0209     
0210     /* so data len will not be encrypted */
0211     *len_ind = htonl((uint32_t)ilen);
0212     
0213     EXAES_CBC_encrypt_buffer((uint8_t*)(output+CRYPTO_LEN_PFX_BYTES), 
0214             (uint8_t*)input, ilen, (const uint8_t*)sha1key, (const uint8_t*) iv);
0215     
0216     
0217     *olen = size_estim;
0218     
0219 #ifdef CRYPTODEBUG_DUMP
0220     
0221     /* DUMP the data block */
0222     NDRX_DUMP(log_debug, "Encrypted data block", output, *olen);
0223     
0224 #endif
0225     
0226 out:
0227     return ret;
0228 }
0229 
0230 /**
0231  * Decrypt data block (API entry function)
0232  * @param input input data block
0233  * @param ibufsz input data block buffer size
0234  * @param output encrypted data block
0235  * @param obufsz encrypted data block buffer size
0236  * @return EXSUCCEED/EXFAIL
0237  */
0238 expublic int ndrx_crypto_enc(char *input, long ilen, char *output, long *olen)
0239 {
0240     API_ENTRY;
0241     return ndrx_crypto_enc(input, ilen, output, olen);
0242 }
0243 
0244 /**
0245  * Decrypt data block (internal, no API entry)
0246  * @param input input buffer
0247  * @param ilen input len
0248  * @param output output buffer
0249  * @param olen on input indicates the buffer length on output, output data len
0250  * @return EXSUCCEED/EXFAIL
0251  */
0252 expublic int ndrx_crypto_dec_int(char *input, long ilen, char *output, long *olen)
0253 {
0254     int ret = EXSUCCEED;
0255     char sha1key[NDRX_ENCKEY_BUFSZ];
0256     uint32_t *len_ind = (uint32_t *)input;
0257     uint8_t  iv[]  = IV_INIT;
0258     long data_size = ntohl(*len_ind);
0259     
0260     /* encrypt data block */
0261     
0262     if (EXSUCCEED!=ndrx_get_final_key(sha1key))
0263     {
0264         EXFAIL_OUT(ret);
0265     }
0266     
0267 #ifdef CRYPTODEBUG_DUMP
0268     NDRX_DUMP(log_debug, "About to decrypt (incl 4 byte len): ", input, ilen);
0269 #endif
0270     
0271 #ifdef CRYPTODEBUG
0272     NDRX_LOG_EARLY(log_debug, "Data size: %ld, %ld, output buffer: %ld",
0273             data_size, olen);
0274 #endif
0275 
0276     if (data_size > *olen)
0277     {
0278         userlog("Decryption output buffer too short, data: %ld, output buffer: %ld",
0279                 data_size, *olen);
0280         
0281         _Nset_error_fmt(NENOSPACE, "Decryption output buffer too short, "
0282                 "data: %ld, output buffer: %ld",
0283                 data_size, *olen);
0284         
0285         EXFAIL_OUT(ret);
0286     }
0287     *olen = data_size;
0288     EXAES_CBC_decrypt_buffer((uint8_t*)(output), 
0289             (uint8_t*)(input+CRYPTO_LEN_PFX_BYTES), ilen-CRYPTO_LEN_PFX_BYTES, 
0290             (const uint8_t*)sha1key, (const uint8_t*) iv);
0291     
0292     /* DUMP the data block */
0293     
0294 #ifdef CRYPTODEBUG_DUMP
0295     NDRX_DUMP(log_debug, "Decrypted data block", output, *olen);
0296 #endif
0297     
0298 out:
0299     return ret;
0300 }
0301 
0302 /**
0303  * Decrypt data block, API entry
0304  * @param input input buffer
0305  * @param ilen input len
0306  * @param output output buffer
0307  * @param olen on input indicates the buffer length on output, output data len
0308  * @return EXSUCCEED/EXFAIL
0309  */
0310 expublic int ndrx_crypto_dec(char *input, long ilen, char *output, long *olen)
0311 {
0312     API_ENTRY;
0313     return ndrx_crypto_dec_int(input, ilen, output, olen);
0314 }
0315 
0316 /**
0317  * Encrypt string
0318  * @param input input string, zero terminated
0319  * @param output base64 string
0320  * @param olen output buffer length
0321  * @return EXSUCCEED/EXFAIL
0322  */
0323 expublic int ndrx_crypto_enc_string(char *input, char *output, long olen)
0324 {
0325     int ret = EXSUCCEED;
0326     char buf[NDRX_MSGSIZEMAX];
0327     long bufsz = sizeof(buf);
0328     long estim_size;
0329     long inlen = strlen(input);
0330     size_t b64len;
0331     API_ENTRY;
0332     
0333     /* encrypt data block */
0334     if (EXSUCCEED!=ndrx_crypto_enc_int(input, inlen, buf, &bufsz))
0335     {
0336         EXFAIL_OUT(ret);
0337     }
0338     
0339     /* translate data block the the base64 (with size estim) */
0340     estim_size = NDRX_BASE64_SIZE(bufsz) + 1;
0341     if (NDRX_BASE64_SIZE(bufsz) +1 /* for EOS */ > olen)
0342     {
0343         userlog("Output buffer too short. Required for "
0344                 "base64 %ld bytes, but got %ld", estim_size, olen);
0345 #ifdef CRYPTODEBUG
0346         NDRX_LOG_EARLY(log_error, "Output buffer too short. Required for "
0347                 "base64 %ld bytes, but got %ld", estim_size, olen);
0348 #endif
0349         
0350         _Nset_error_fmt(NENOSPACE, "Output buffer too short. Required for "
0351                 "base64 %ld bytes, but got %ld",
0352               estim_size, olen);
0353         EXFAIL_OUT(ret);
0354     }
0355     
0356     /* encode to base64... */
0357     
0358     ndrx_base64_encode((unsigned char *)buf, bufsz, &b64len, output);
0359     
0360     output[b64len] = EXEOS;
0361     
0362 #ifdef CRYPTODEBUG
0363     
0364     NDRX_LOG_EARLY(log_debug, "%s: input: [%s]", __func__, input);
0365     NDRX_LOG_EARLY(log_debug, "%s: output: [%s]", __func__, output);
0366     
0367 #endif
0368     
0369 out:
0370     return ret;
0371 }
0372 
0373 /**
0374  * Decrypt the base64 string
0375  * @param input base64 containing encrypted data
0376  * @param output clear text output storage
0377  * @param olen output buffer size
0378  */
0379 expublic int ndrx_crypto_dec_string(char *input, char *output, long olen)
0380 {
0381     int ret = EXSUCCEED;
0382     /*char buf[NDRX_MSGSIZEMAX];  have to allocate buffer dynamically -> NDRX_MSGSIZEMAX 
0383      * might not be known at init stage (reading ini)  */
0384     long len = strlen(input);
0385     char *buf = NULL;
0386     size_t bufsz = len;
0387     uint32_t *len_ind;
0388     long data_size;
0389     
0390     API_ENTRY;
0391     
0392     if (NULL==(buf = NDRX_MALLOC(bufsz)))
0393     {
0394         int err = errno;
0395         
0396         NDRX_LOG_EARLY(log_error, "%s: Failed to allocate %ld bytes: %s",
0397                 __func__, len, strerror(err));
0398         userlog("%s: Failed to allocate %ld bytes: %s",
0399                 __func__, len, strerror(err));
0400         
0401         _Nset_error_fmt(NESYSTEM, "%s: Failed to allocate %ld bytes: %s",
0402                 __func__, len, strerror(err));
0403         EXFAIL_OUT(ret);
0404     }
0405     
0406     len_ind = (uint32_t *)buf;
0407     
0408 #ifdef CRYPTODEBUG
0409     NDRX_LOG_EARLY(log_debug, "%s, output buf %p, olen=%ld input len: %ld", 
0410             __func__, output, olen, len);
0411     NDRX_LOG_EARLY(log_debug, "%s: About to decrypt (b64): [%s]", __func__, input);
0412 #endif
0413 
0414     /* convert base64 to bin */
0415     if (NULL==ndrx_base64_decode(input, len, &bufsz, buf))
0416     {
0417         _Nset_error_fmt(NEFORMAT, "%s, ndrx_base64_decode failed (input len: %ld", 
0418                 __func__, len);
0419         NDRX_LOG_EARLY(log_error, "%s, ndrx_base64_decode failed (input len: %ld", 
0420                 __func__, len);
0421         userlog("%s, ndrx_base64_decode failed (input len: %ld", 
0422                 __func__, len);
0423         EXFAIL_OUT(ret);
0424     }
0425 
0426 #ifdef CRYPTODEBUG_DUMP
0427     NDRX_DUMP(log_debug, "Binary data to decrypt: ", buf, bufsz);
0428 #endif
0429     
0430     /* Check the output buffer len */
0431     data_size = ntohl(*len_ind);
0432     
0433     if (data_size +1 > olen)
0434     {
0435         userlog("String decryption output buffer too short, data: %ld, "
0436                 "output buffer: %ld", data_size, olen);
0437         
0438         _Nset_error_fmt(NENOSPACE, "String decryption output buffer too short, "
0439                 "data: %ld, output buffer: %ld",
0440                 data_size, olen);
0441         
0442         EXFAIL_OUT(ret);
0443     }
0444     
0445     /* decrypt the data to the output buffer */
0446     
0447     if (EXSUCCEED!=ndrx_crypto_dec_int(buf, bufsz, output, &olen))
0448     {
0449         #ifdef CRYPTODEBUG
0450         NDRX_LOG_EARLY(log_error, "%s: Failed to decrypt [%s]!", __func__, input);
0451         #endif
0452         userlog("%s: Failed to decrypt [%s]!", __func__, input);
0453     }
0454     
0455     output[olen] = EXEOS;
0456     
0457     /* check that data does not contain binary zero
0458      * because we operate with 0x00 strings, thus if zero is found, data
0459      * is not decrypted.
0460      */
0461     if ((len=strlen(output)) != olen)
0462     {
0463         userlog("Found EOS at %ld. Output data len %ld", len, olen);
0464         
0465         _Nset_error_fmt(NEINVALKEY, "Found EOS at %ld. Output data len %ld", 
0466                 len, olen);
0467         
0468         EXFAIL_OUT(ret);
0469     }
0470     
0471 out:
0472 
0473     if (NULL!=buf)
0474     {
0475         NDRX_FREE(buf);
0476     }
0477 
0478     return ret;
0479 }