| /**********************************************************************
|
| // @@@ START COPYRIGHT @@@ |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| // @@@ END COPYRIGHT @@@ |
| //
|
| **********************************************************************/
|
|
|
| #include "security.h"
|
| #include "Key.h"
|
| #include "utils.h"
|
| #include "Cipher.h"
|
| #include "MessageDigest.h"
|
| #include "securityException.h"
|
| #include "openssl/rand.h"
|
| #include "openssl/bio.h"
|
| #include "StaticLocking.h"
|
| #include "secdefsCommon.h"
|
| #ifndef _WINDOWS
|
| #include <fcntl.h>
|
| #include <errno.h>
|
| #include <unistd.h>
|
| #else // _WINDOWS
|
| #include <Wincrypt.h>
|
| #include <windows.h>
|
| #endif
|
|
|
| #define SEED_SIZE 256
|
| //Used when /dev/urandom file does not exist on the system
|
| #define WEAK_SEED_SIZE 64
|
|
|
| #ifdef MXHPUXPA
|
| static unsigned int seedGenerator(char* seed);
|
| static int shellCommand(char* command, char* result, long dataLen);
|
| #endif
|
|
|
|
|
| // Constructors
|
| Security::Security()
|
| {
|
| m_pwdKey.data.password = NULL;
|
| m_keyObj = NULL;
|
| m_DataEncryptionBuffer = NULL;
|
| }
|
|
|
| Security::~Security()
|
| {
|
| if (m_keyObj)
|
| {
|
| delete m_keyObj;
|
| m_keyObj = NULL;
|
| }
|
| if(m_DataEncryptionBuffer != NULL)
|
| delete[] m_DataEncryptionBuffer;
|
| }
|
|
|
|
|
| Security::Security(const char *cert_file) throw (SecurityException)
|
| {
|
|
|
| if (!cert_file)
|
| throw SecurityException(INPUT_PARAMETER_IS_NULL, " - cert_file.");
|
|
|
| m_littleEndian = is_little_endian();
|
| m_pwdKey.data.password = NULL;
|
|
|
| /* if (m_keyObj)
|
| {
|
| delete m_keyObj;
|
| m_keyObj = NULL;
|
| }
|
| */
|
| m_keyObj = new Key();
|
|
|
| m_pwdKey.id[0] = '\1';
|
| m_pwdKey.id[1] = '\2';
|
| m_pwdKey.id[2] = '\3';
|
| m_pwdKey.id[3] = '\4';
|
|
|
| // Initialize rolename with 128 ' '
|
| memset(&m_pwdKey.rolename[0],'\0', ROLENAME_SIZE);
|
|
|
| try {
|
| m_keyObj->getPubKeyFromFile(cert_file);
|
| generateSessionKey();
|
| }catch (SecurityException se) {
|
| m_keyObj->Key::~Key();
|
| throw se;
|
| }
|
| m_DataEncryptionBuffer = NULL;
|
| }
|
|
|
| void Security::generateSessionKey() throw (SecurityException)
|
| {
|
| int seedSize;
|
| unsigned char seed[SEED_SIZE];
|
|
|
| #ifndef _WINDOWS
|
| // Reads 256 bytes from file /dev/urandom and adds them to
|
| // PRNG to use as seed of the random number generator
|
| int fd = open("/dev/urandom", O_RDONLY);
|
| if (fd < 0)
|
| #ifdef MXHPUXPA // HP-UX PA-RISC
|
| // /dev/urandom file does not exist on HP-UX PA-RISK. If customers
|
| // do not choose to install the HP-UX Strong Random Number Generator
|
| // package which can be obtained from this link, provide the following
|
| // weak Random Number Generator
|
| {
|
| seedSize = WEAK_SEED_SIZE;
|
| if (seedGenerator((char*)seed) != seedSize)
|
| throw SecurityException(UNABLE_TO_SEED_RNG, NULL);
|
| }
|
| #else // other UNIX platforms
|
| throw SecurityException(UNABLE_TO_SEED_RNG, NULL);
|
| seedSize = SEED_SIZE;
|
| int num = 0;
|
|
|
| // This can infinitely loops if /dev/urandom does not return 256 bytes
|
| // However, it's very very unlikely to happen.
|
| while (num != seedSize)
|
| {
|
| int count = 0;
|
| count = read(fd, &seed[num], seedSize - num);
|
| if (count > 0)
|
| num += count;
|
| else if (count == 0)
|
| {
|
| close(fd);
|
| throw SecurityException(FAILED_GENERATE_RANDOM_NUM, NULL);
|
| }
|
| else //count < 0
|
| {
|
| switch (errno)
|
| {
|
| #ifdef EINTR
|
| case EINTR: // Interrupted system call
|
| #endif
|
| #ifdef EAGAIN
|
| case EAGAIN: // Resource temporarily unavailable
|
| #endif
|
| /* No error, try again */
|
| break;
|
| default:
|
| close(fd);
|
| throw SecurityException(FAILED_GENERATE_RANDOM_NUM, NULL);
|
| }
|
| }
|
| }
|
| #endif //MXHPUXPA
|
| #else // _WINDOWS
|
| seedSize=SEED_SIZE;
|
| // Get a handle to the DLL module.
|
| HINSTANCE hLib = LoadLibrary(TEXT("advapi32.dll"));
|
| // Do the following to avoid the overhead of loading the entire CryptoAPI
|
| if (hLib) {
|
| BOOLEAN (APIENTRY *pfn)(void*, ULONG) =
|
| (BOOLEAN (APIENTRY *)(void*,ULONG))GetProcAddress(hLib, "SystemFunction036");
|
| if (pfn) {
|
| if(!pfn(seed, seedSize)) {
|
| throw SecurityException(UNABLE_TO_SEED_RNG, NULL);
|
| }
|
| }
|
| }
|
| else {
|
| throw SecurityException(FAILED_LOADING_LIB, NULL);
|
| }
|
| #endif // _WINDOWS
|
| getMutex();
|
| RAND_seed((const void *)seed, seedSize);
|
| // Check if the PRNG has been seeded with enough randomness
|
| if (!RAND_status ())
|
| {
|
| releaseMutex();
|
| throw SecurityException(UNABLE_TO_SEED_RNG, NULL);
|
| }
|
| // Generate 64 bytes of random number using the seed generated above
|
| int errcode = RAND_bytes((unsigned char*) &m_pwdKey.data.session_key[0],
|
| SESSION_KEYLEN + NONCE_SIZE);
|
| releaseMutex();
|
|
|
| if (errcode != 1)
|
| throw SecurityException(SESSION_KEY_GENERATION_FAILED, NULL);
|
|
|
| // Get nonce sequence
|
| memcpy((char*) &m_nonceSeq, &m_pwdKey.data.nonce[NONCE_RANDOM], NONCE_SEQNUM);
|
| if (!m_littleEndian)
|
| {
|
| m_nonceSeq = swapByteOrderOfLL(m_nonceSeq);
|
| // Store the swap bytes back in nonce
|
| //intLLong.llVal = m_nonceSeq;
|
| memcpy(&m_pwdKey.data.nonce[NONCE_RANDOM], (char*) &m_nonceSeq, NONCE_SEQNUM);
|
| }
|
|
|
| // Set time when session key is generated
|
| m_keyTime = get_msts();
|
| }
|
|
|
| void Security::encryptPwd( const char *pwd, const int pwdLen,
|
| const char *rolename, const int rolenameLen,
|
| const char *procInfo, const int procInfoLen,
|
| char *pwdkey, int *pwdkeyLen)
|
| throw (SecurityException)
|
| {
|
| // Get public key length
|
| int pubKeyLen = m_keyObj->getKeyLen();
|
| int maxPlainTextLen = pubKeyLen - UNUSEDBYTES;
|
|
|
| // Password + nonce + session key can't be longer than the public
|
| // key's length
|
| if ((NONCE_SIZE + SESSION_KEYLEN + pwdLen) > maxPlainTextLen)
|
| throw SecurityException(PWD_LENGTH_TOO_LONG, NULL);
|
|
|
| // Copy template with id etc
|
| memcpy(pwdkey, (char *)&m_pwdKey, PWDKEY_SIZE_LESS_LOGINDATA);
|
|
|
| // Null terminater is not needed when copying rolename
|
| // If rolename is longer than 128 characters, it will be truncated
|
| if (rolename)
|
| {
|
| if (rolenameLen > ROLENAME_SIZE)
|
| memcpy(&pwdkey[PWDID_SIZE], rolename, ROLENAME_SIZE);
|
| else
|
| memcpy(&pwdkey[PWDID_SIZE], rolename, rolenameLen);
|
| }
|
| // Copy 12 bytes of procInfo and 8 bytes of timestamp to password key
|
| // store procInfo in the digest starting from digest[20]
|
| memcpy(&pwdkey[PWDID_SIZE + ROLENAME_SIZE + DIGEST_LENGTH - PROCINFO_SIZE],
|
| &procInfo[0], (PROCINFO_SIZE + TIMESTAMP_SIZE));
|
|
|
| char * to_encrypt = new char[SESSION_KEYLEN + NONCE_SIZE + pwdLen];
|
|
|
| memcpy((char *)to_encrypt, (char *)&m_pwdKey.data,
|
| SESSION_KEYLEN + NONCE_SIZE);
|
| // Copy password to encrypt
|
| memcpy((char *)&to_encrypt[SESSION_KEYLEN + NONCE_SIZE], pwd, pwdLen);
|
|
|
| int plainTextLen = SESSION_KEYLEN + NONCE_SIZE + pwdLen;
|
| int cipherTextLen = 0;
|
|
|
| try {
|
| cipherTextLen = Cipher::encrypt((const unsigned char*) to_encrypt, plainTextLen,
|
| (unsigned char *)&pwdkey[PWDKEY_SIZE_LESS_LOGINDATA], m_keyObj, NULL, 0);
|
| if (cipherTextLen != pubKeyLen)
|
| throw SecurityException(CIPHER_TEXT_LEN_NOT_EQUAL_KEY_LEN, NULL);
|
| } catch (SecurityException se) {
|
| delete to_encrypt;
|
| throw se;
|
| }
|
|
|
| delete to_encrypt;
|
|
|
| // Create digest
|
| unsigned int md_len;
|
| int dglen = PROCINFO_SIZE + TIMESTAMP_SIZE + pubKeyLen;
|
|
|
| try {
|
| MessageDigest::digest(&m_pwdKey.data.session_key[0], SESSION_KEYLEN,
|
| (const char*) &pwdkey[PWDID_SIZE + ROLENAME_SIZE + DIGEST_LENGTH - PROCINFO_SIZE],
|
| dglen, (unsigned char *) &pwdkey[PWDID_SIZE + ROLENAME_SIZE], &md_len);
|
|
|
| if (md_len != DIGEST_LENGTH)
|
| throw SecurityException(BAD_MESSAGE_DIGEST_LEN, NULL);
|
| } catch (SecurityException se) {
|
| throw se;
|
| }
|
|
|
| *pwdkeyLen = PWDKEY_SIZE_LESS_LOGINDATA + pubKeyLen;
|
| }
|
|
|
|
|
|
|
| int Security::getPwdEBufferLen() throw (SecurityException)
|
| {
|
| int pubKLen = m_keyObj->getKeyLen();
|
|
|
| if (pubKLen != 0)
|
| return (pubKLen + PWDKEY_SIZE_LESS_LOGINDATA);
|
| else
|
| throw SecurityException(PUBKEY_LENGTH_IS_ZERO, NULL);
|
| }
|
|
|
| unsigned char* Security::getCertExpDate()
|
| {
|
| return m_keyObj->getCertExpDate();
|
| }
|
|
|
| int Security::getLittleEndian()
|
| {
|
| return m_littleEndian;
|
| }
|
|
|
| #ifdef MXHPUXPA
|
| #define DATA_MAX 1024
|
|
|
| static unsigned int seedGenerator(char* seed)
|
| {
|
| char buffer[DATA_MAX + DATA_MAX/2];
|
| unsigned long long ts = 0;
|
| unsigned int mdLen = 0;
|
| char md[WEAK_SEED_SIZE+1];
|
| char key[WEAK_SEED_SIZE];
|
|
|
| // Get process ID
|
| pid_t myPID = getpid();
|
| if (myPID == -1)
|
| return mdLen;
|
|
|
| // Get current timestamp
|
| ts = swapByteOrderOfLL(get_msts());
|
|
|
| // Get 512 bytes output from the finger command
|
| if (shellCommand("finger |sort -r -k5,5", &buffer[0], DATA_MAX/2) == 0)
|
| return mdLen;
|
|
|
| // reverse the ps command output based on timestamp hoping the first
|
| // 1024 bytes would be as fresh as possible
|
| if (shellCommand("ps -ef |sort -r -k5,5|cut -c 10-100",
|
| &buffer[DATA_MAX/2], DATA_MAX) == 0)
|
| return mdLen;
|
|
|
| // HMAC buffer using pid + the last 52 bytes of buffer + ts as key
|
| memcpy(&key[0], (char*)&myPID, 4);
|
| memcpy(&key[4], &buffer[DATA_MAX + DATA_MAX/2 - 52], 52);
|
| memcpy(&key[56], (char*)&ts,8);
|
| const EVP_MD *evp_md = EVP_sha256();
|
| HMAC(evp_md, (const void *) key, WEAK_SEED_SIZE,
|
| (const unsigned char *) buffer,
|
| DATA_MAX + DATA_MAX/2,
|
| (unsigned char *) &md[0], &mdLen);
|
|
|
| if (mdLen != 32)
|
| return 0;
|
| mdLen = 0; //refresh
|
|
|
| // HMAC again to get the other 32 bytes using the prvious
|
| // HMAC result as key and the previous 64 bytes key as data
|
| HMAC(evp_md, (const void *) &md[0], 32,
|
| (const unsigned char *) key, WEAK_SEED_SIZE,
|
| (unsigned char *) &md[32], &mdLen);
|
| if (mdLen != 32)
|
| return 0;
|
|
|
| return (2 * mdLen);
|
| }
|
|
|
| static int shellCommand(char* command, char* result, long dataLen)
|
| {
|
| FILE *fp;
|
| int status;
|
| int len=0;
|
| char buffer[DATA_MAX + 1];
|
|
|
| if ((fp = popen(command, "r")) != NULL)
|
| {
|
| // Want dataLen bytes but fgets dataLen - 1 bytes so add one to avoid
|
| // a second get
|
| while ( fgets( buffer, dataLen + 1, fp))
|
| {
|
| // You might not get that dataLen bytes from the command output
|
| // but it is alright since junk in the buffer can be used as
|
| // entropies also.
|
| int tmpLen = strlen(buffer);
|
|
|
| if (len >= dataLen)
|
| break;
|
| // output is less than expected bytes
|
| else if (tmpLen > dataLen - len)
|
| {
|
| memcpy(&result[len], buffer, dataLen - len);
|
| len += dataLen - len;
|
| break; //enough bytes
|
| }
|
| else
|
| {
|
| memcpy(&result[len], buffer,tmpLen);
|
| len += tmpLen;
|
| }
|
| }
|
|
|
| }
|
| else
|
| return 0;
|
|
|
| status = pclose(fp);
|
| // -1 if stream is not associated with a popen()ed command
|
| // 127 sh could not be executed for some reason
|
| // This stops the program, should we? No, just ignore.
|
| if (status == -1 || status == 127)
|
| return 1;
|
|
|
| return 1;
|
| }
|
| #endif //HPUXPA
|
|
|