Back to home page

Enduro/X

 
 

    


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