├── params.json ├── serial.json ├── .github └── workflows │ ├── keyfactor-extension-generate-readme.yml │ ├── keyfactor-extension-update-catalog.yml │ └── makefile.yml ├── .gitignore ├── management.h ├── inventory.h ├── fetchlogs.h ├── openssl_compat.h ├── enrollment.h ├── lib ├── base64.h ├── json.h └── base64.c ├── wolfssl_wrapper ├── wolfssl_functions_used.txt └── wolfssl_wrapper.h ├── global.h ├── httpclient.h ├── csr.h ├── session.h ├── schedule.h ├── utils.h ├── logging.h ├── constants.h ├── makefile ├── openssl_wrapper └── openssl_wrapper.h ├── config.h ├── agent.h ├── csr.c ├── dto.h ├── fetchlogs.c ├── README-LICENSE.txt ├── README.md ├── enrollment.c ├── management.c ├── schedule.c └── inventory.c /params.json: -------------------------------------------------------------------------------- 1 | {"Serial": "50", "Model": "Linux"} 2 | -------------------------------------------------------------------------------- /serial.json: -------------------------------------------------------------------------------- 1 | { 2 | "ModelName": "IoT", 3 | "NextNumber": 2 4 | } -------------------------------------------------------------------------------- /.github/workflows/keyfactor-extension-generate-readme.yml: -------------------------------------------------------------------------------- 1 | name: Update README 2 | on: [push, workflow_dispatch] 3 | 4 | jobs: 5 | update_readme: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@master 10 | 11 | - uses: cuchi/jinja2-action@v1.2.0 12 | with: 13 | template: README.md.tpl 14 | output_file: README.md 15 | data_file: integration-manifest.json 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }} 18 | 19 | - uses: stefanzweifel/git-auto-commit-action@v4 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | with: 23 | push_options: '--force' 24 | commit_message: Update generated README 25 | commit_user_name: Keyfactor 26 | commit_user_email: keyfactor@keyfactor.github.io 27 | commit_author: Keyfactor 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac file system 2 | .DS_Store 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Object files 8 | *.o 9 | *.ko 10 | *.obj 11 | *.elf 12 | 13 | # Linker output 14 | *.ilk 15 | *.map 16 | *.exp 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | 42 | # Debug files 43 | *.dSYM/ 44 | *.su 45 | *.idb 46 | *.pdb 47 | 48 | # Kernel Module Compile Results 49 | *.mod* 50 | *.cmd 51 | .tmp_versions/ 52 | modules.order 53 | Module.symvers 54 | Mkfile.old 55 | dkms.conf 56 | 57 | # Custom files 58 | agent 59 | config.json 60 | config*.json 61 | certs/* 62 | 63 | # Certificate and key files 64 | *.crt 65 | *.key 66 | *.pub 67 | 68 | # Testing directories 69 | wolfssl_testing 70 | valgrind_output 71 | valgrind.log 72 | 73 | # Sig files 74 | signature/ 75 | .idea/ 76 | -------------------------------------------------------------------------------- /.github/workflows/keyfactor-extension-update-catalog.yml: -------------------------------------------------------------------------------- 1 | name: Generate Catalog Entry 2 | on: [push, workflow_dispatch] 3 | 4 | jobs: 5 | generate_entry: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@master 10 | 11 | - uses: actions/checkout@v2 12 | with: 13 | token: ${{ secrets.SDK_SYNC_PAT }} 14 | path: './catalog-temp/' 15 | repository: 'Keyfactor/integrations-catalog' 16 | 17 | - uses: cuchi/jinja2-action@v1.2.0 18 | with: 19 | template: ./catalog-temp/_integration.md.tpl 20 | output_file: ${{ format('./catalog-temp/_integrations/{0}.md', github.event.repository.name) }} 21 | data_file: integration-manifest.json 22 | variables: | 23 | repository= ${{ format('https://github.com/{0}', github.repository) }} 24 | env: 25 | GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }} 26 | 27 | - uses: EndBug/add-and-commit@v7 28 | with: 29 | author_name: 'Keyfactor' 30 | author_email: 'keyfactor@keyfactor.github.io' 31 | branch: 'main' 32 | message: ${{ format('Added the manifest for {0}', github.event.repository.name) }} 33 | add: ${{ format('_integrations/{0}.md --force', github.event.repository.name) }} 34 | cwd: './catalog-temp/' 35 | -------------------------------------------------------------------------------- /.github/workflows/makefile.yml: -------------------------------------------------------------------------------- 1 | name: SimpleSigning demo 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-and-sign: 8 | name: build and sign with signum 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Install dependencies 13 | run: | 14 | sudo apt-get update 15 | sudo apt-get install -y build-essential libcurl4-gnutls-dev curl libssl-dev automake autoconf libtool 16 | sudo apt autoremove -y 17 | 18 | - uses: actions/checkout@v2 19 | 20 | - name: clean and build 21 | run: | 22 | make clean 23 | make opentest 24 | 25 | - name: sign 26 | run: | 27 | curl --cacert certs/it-ss01-pkihosted-dev-c2company-com-chain.pem -F "workerName=PlainSigner" -F "file=@./agent" \https://it-ss01.pkihosted-dev.c2company.com/signserver/process > agent.sig 28 | 29 | - name: commit signature + binary #todo make this a release 30 | run: | 31 | git config --global user.name 'signature action' 32 | git config --global user.email 'sukhyung.shin@keyfactor.com' 33 | rm -rf signature 34 | mkdir signature 35 | mv agent signature/. 36 | mv agent.sig signature/. 37 | git add -f signature/agent 38 | git add -f signature/agent.sig 39 | git commit -m "automated signature" 40 | git push 41 | 42 | -------------------------------------------------------------------------------- /management.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef __MANAGEMENT_H__ 14 | #define __MANAGEMENT_H__ 15 | #include "dto.h" 16 | #include "config.h" 17 | 18 | int cms_job_manage(struct SessionJob* jobInfo, char* sessionToken, 19 | char** chainJob); 20 | 21 | #endif 22 | /******************************************************************************/ 23 | /******************************* END OF FILE **********************************/ 24 | /******************************************************************************/ -------------------------------------------------------------------------------- /inventory.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef __INVENTORY_H__ 14 | #define __INVENTORY_H__ 15 | 16 | #include "dto.h" 17 | #include "config.h" 18 | 19 | int cms_job_inventory(struct SessionJob* jobInfo, char* sessionToken); 20 | 21 | #endif /* __INVENTORY_H__ */ 22 | /******************************************************************************/ 23 | /******************************* END OF FILE **********************************/ 24 | /******************************************************************************/ -------------------------------------------------------------------------------- /fetchlogs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef KEYFACTOR_FETCHLOGS_H_ 14 | #define KEYFACTOR_FETCHLOGS_H_ 15 | 16 | #include "dto.h" 17 | #include "config.h" 18 | 19 | int cms_job_fetchLogs(struct SessionJob* jobInfo, char* sessionToken); 20 | 21 | #endif /* KEYFACTOR_FETCHLOGS_H_ */ 22 | /******************************************************************************/ 23 | /******************************* END OF FILE **********************************/ 24 | /******************************************************************************/ -------------------------------------------------------------------------------- /openssl_compat.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef OPENSSL_COMPAT_H_ 14 | #define OPENSSL_COMPAT_H_ 15 | 16 | #ifndef __WOLF_SSL__ 17 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 18 | void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, 19 | const BIGNUM**d) 20 | { 21 | if(n != NULL) 22 | { 23 | *n = r->n; 24 | } 25 | if(e != NULL) 26 | { 27 | *e = r->e; 28 | } 29 | if(d != NULL) 30 | { 31 | *d = r->d; 32 | } 33 | } 34 | #endif /* OPENSSL_VERSION_NUMBER */ 35 | #endif /* __WOLF_SSL__ */ 36 | 37 | #endif /* OPENSSL_COMPAT_H_ */ 38 | -------------------------------------------------------------------------------- /enrollment.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef ENROLLMENT_H_ 14 | #define ENROLLMENT_H_ 15 | 16 | #include "dto.h" 17 | #include "config.h" 18 | 19 | #define RSA_DEFAULT_EXP 65537 20 | 21 | int cms_job_enroll(struct SessionJob* jobInfo, char* sessionToken, 22 | char** chainJob); 23 | 24 | #endif /* ENROLLMENT_H_ */ 25 | /******************************************************************************/ 26 | /******************************* END OF FILE **********************************/ 27 | /******************************************************************************/ 28 | -------------------------------------------------------------------------------- /lib/base64.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CSS_BASE64_H 3 | #define CSS_BASE64_H 4 | 5 | // NSData+Base64.h 6 | // base64 7 | // 8 | // Created by Matt Gallagher on 2009/06/03. 9 | // Copyright 2009 Matt Gallagher. All rights reserved. 10 | // 11 | // This software is provided 'as-is', without any express or implied 12 | // warranty. In no event will the authors be held liable for any damages 13 | // arising from the use of this software. Permission is granted to anyone to 14 | // use this software for any purpose, including commercial applications, and to 15 | // alter it and redistribute it freely, subject to the following restrictions: 16 | // 17 | // 1. The origin of this software must not be misrepresented; you must not 18 | // claim that you wrote the original software. If you use this software 19 | // in a product, an acknowledgment in the product documentation would be 20 | // appreciated but is not required. 21 | // 2. Altered source versions must be plainly marked as such, and must not be 22 | // misrepresented as being the original software. 23 | // 3. This notice may not be removed or altered from any source 24 | // distribution. 25 | // 26 | 27 | // 2017-04-11 Jon Proch - Removed Objective-C portion, adjusted name casing 28 | 29 | #include 30 | #include 31 | 32 | void* base64_decode(const char *inputBuffer, size_t length, size_t* outputLength); 33 | 34 | char* base64_encode(const void* inputBuffer, size_t length, bool separateLines, size_t* outputLength); 35 | 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /wolfssl_wrapper/wolfssl_functions_used.txt: -------------------------------------------------------------------------------- 1 | WOLFSSL Structures or Defines 2 | WOLFSSL_X509 3 | WOLFSSL_EVP_PKEY 4 | WOLFSSL_EVP_MD 5 | WOLFSSL_BIO 6 | WOLFSSL_EVP_CIPHER 7 | WOLFSSL_SUCCESS 8 | 9 | wolfSSL Calls 10 | const WOLFSSL_EVP_MD* pSha1 = wolfSSL_EVP_sha1(); 11 | wolfSSL_EVP_aes_256_cbc() 12 | wolfSSL_OPENSSL_free 13 | wolfSSL_BIO_free 14 | wolfSSL_BIO_get_mem_data 15 | wolfSSL_BIO_new_mem_buf 16 | wolfSSL_BIO_new 17 | wolfSSL_BIO_puts 18 | wolfSSL_BIO_s_mem 19 | wolfSSL_ERR_peek_last_error 20 | wolfSSL_d2i_AutoPrivateKey 21 | wolfSSL_d2i_PKCS8PrivateKey_bio 22 | wolfSSL_d2i_PrivateKey 23 | wolfSSL_d2i_X509 24 | wolfSSL_i2d_X509 25 | wolfSSL_BN_cmp 26 | wolfSSL_EVP_PKEY_get1_EC_KEY 27 | wolfSSL_EVP_PKEY_base_id 28 | wolfSSL_EVP_PKEY_get1_RSA 29 | wolfSSL_EVP_PKEY_free 30 | wolfSSL_EC_KEY_free 31 | wolfSSL_EC_KEY_get0_public_key 32 | wolfSSL_EC_KEY_get0_group 33 | wolfSSL_EC_POINT_point2hex 34 | wolfSSL_RSA_free 35 | wolfSSL_RSA_get0_key 36 | wolfSSL_PEM_read 37 | wolfSSL_PEM_read_bio_X509 38 | wolfSSL_PEM_write_bio_PKCS8PrivateKey 39 | wolfSSL_PEM_write_bio_X509 40 | wolfSSL_PEM_write_X509 41 | wolfSSL_X509_digest 42 | wolfSSL_X509_free 43 | wolfSSL_X509_get_pubkey 44 | wolfSSL_Init 45 | wolfSSL_Cleanup 46 | 47 | WolfCrypt Calls 48 | wc_InitRng 49 | wc_InitRsaKey 50 | wc_MakeRsaKey 51 | wc_RsaKeyToDer 52 | wc_ecc_init 53 | wc_ecc_make_key_ex 54 | wc_EccKeyToDer 55 | wc_InitCert 56 | wc_MakeCertReq 57 | wc_SignCert 58 | wc_DerToPemEx -------------------------------------------------------------------------------- /global.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | /* Global header file for things like global defines, variables, etc. */ 14 | 15 | #ifndef _GLOBAL_H_ 16 | #define _GLOBAL_H_ 17 | 18 | /* Undefine this for HTTP version 2.0 */ 19 | #define __HTTP_1_1__ 20 | 21 | #undef __DEBUG__ 22 | #undef __PLATFORM_FIXED__ 23 | 24 | #ifdef __TPM__ 25 | extern char engine_id[21]; 26 | #endif 27 | 28 | #define UUID_LEN 37 // 36 char per RFC 4122 + 1 for \0 29 | 30 | #endif 31 | /******************************************************************************/ 32 | /******************************* END OF FILE **********************************/ 33 | /******************************************************************************/ -------------------------------------------------------------------------------- /httpclient.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef __HTTPCLIENT_H__ 14 | #define __HTTPCLIENT_H__ 15 | 16 | #define CONNECTION_TIMEOUT 60 17 | 18 | #define CLIENT_CERT_HEADER "X-ARR-ClientCert" 19 | 20 | extern bool add_client_cert_to_header; 21 | 22 | int http_post_json(const char* url, const char* username, const char* password, 23 | const char* trustStore, const char* clientCert, const char* clientKey, 24 | const char* clientKeyPass, char* postData, char** pRespData, int retryCount, 25 | int retryInterval); 26 | 27 | #endif /* __HTTPCLIENT_H__ */ 28 | /******************************************************************************/ 29 | /******************************* END OF FILE **********************************/ 30 | /******************************************************************************/ -------------------------------------------------------------------------------- /csr.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef __CSR_H__ 14 | #define __CSR_H__ 15 | 16 | #include "dto.h" 17 | #include "config.h" 18 | 19 | #if defined(__TPM__) 20 | bool generate_keypair(const char* keyType, int keySize, const char* path); 21 | #else 22 | bool generate_keypair(const char* keyType, int keySize); 23 | #endif 24 | 25 | char* generate_csr(const char* asciiSubject, size_t* csrLen, 26 | char** pMessage, enum AgentApiResultStatus* pStatus); 27 | 28 | 29 | unsigned long save_cert_key(const char* storePath, const char* keyPath, 30 | const char* password, const char* cert, char** pMessage, 31 | enum AgentApiResultStatus* pStatus); 32 | 33 | #endif /* __CSR_H__ */ 34 | 35 | /******************************************************************************/ 36 | /******************************* END OF FILE **********************************/ 37 | /******************************************************************************/ -------------------------------------------------------------------------------- /session.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef CSS_SESSION_H 14 | #define CSS_SESSION_H 15 | 16 | #include 17 | #include "constants.h" 18 | #include "config.h" 19 | #include "schedule.h" 20 | 21 | struct SessionInfo 22 | { 23 | char Token[GUID_SIZE]; 24 | char AgentId[GUID_SIZE]; 25 | time_t NextExecution; 26 | int UnreachableCount; 27 | int Interval; 28 | }; 29 | 30 | int register_session(struct SessionInfo* session, 31 | struct ScheduledJob** pJobList, uint64_t agentVersion); 32 | 33 | #if defined(__INFINITE_AGENT__) 34 | int heartbeat_session(struct SessionInfo* session, 35 | struct ScheduledJob** pJobList, uint64_t agentVersion); 36 | #endif 37 | 38 | #endif /* CSS_SESSION_H */ 39 | /******************************************************************************/ 40 | /******************************* END OF FILE **********************************/ 41 | /******************************************************************************/ 42 | -------------------------------------------------------------------------------- /schedule.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef SCHEDULE_H_ 14 | #define SCHEDULE_H_ 15 | 16 | #include 17 | #include "dto.h" 18 | 19 | struct ScheduledJob 20 | { 21 | struct SessionJob* Job; 22 | time_t NextExecution; 23 | 24 | struct ScheduledJob* NextJob; 25 | }; 26 | 27 | struct SessionJob* get_runnable_job(struct ScheduledJob** pList, time_t now); 28 | 29 | struct SessionJob* get_job_by_id(struct ScheduledJob** pList, 30 | const char* jobId); 31 | 32 | void clear_job_schedules(struct ScheduledJob** pList); 33 | 34 | void schedule_job(struct ScheduledJob** pList, struct SessionJob* job, 35 | time_t prev); 36 | 37 | time_t next_execution(char* sch, time_t prev); 38 | 39 | #endif /* SCHEDULE_H_ */ 40 | /******************************************************************************/ 41 | /******************************* END OF FILE **********************************/ 42 | /******************************************************************************/ -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef UTILS_H_ 14 | #define UTILS_H_ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | char* hex_encode(unsigned char* inBuf, int len); 21 | 22 | int append_line(char** msg, const char* line); 23 | 24 | int append_linef(char** msg, 25 | const char* fmt, ...) __attribute__ ((format (printf, 2, 3))); 26 | 27 | int backup_file(const char* file); 28 | 29 | int restore_file(const char* file); 30 | 31 | int read_file_bytes(const char* srcPath, unsigned char** pFileBytes, 32 | size_t* fileLen); 33 | 34 | int replace_file(const char* file, const char* contents, long len, bool backup); 35 | 36 | void to_lower_case( char s[], const int len ); 37 | 38 | int byte_to_hex_string( char hexStr[], int stringSize, 39 | unsigned char byteData[], int byteSize ); 40 | 41 | char* util_strip_string(const char* fromString, const char* stripString); 42 | char* bstrcat(const char* s1, const char* s2); 43 | int file_exists( const char *file ); 44 | bool is_directory( const char *file ); 45 | char* get_prefix_substring(const char* string, const char find); 46 | 47 | #endif /* UTILS_H_ */ 48 | /******************************************************************************/ 49 | /******************************* END OF FILE **********************************/ 50 | /******************************************************************************/ -------------------------------------------------------------------------------- /logging.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef LOGGING_H_ 14 | #define LOGGING_H_ 15 | 16 | #include 17 | 18 | void log_error(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 19 | void log_warn(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 20 | void log_info(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 21 | void log_verbose(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 22 | void log_debug(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 23 | void log_trace(const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); 24 | 25 | void log_set_trace(bool param); 26 | void log_set_debug(bool param); 27 | void log_set_verbosity(bool param); 28 | void log_set_info(bool param); 29 | void log_set_warn(bool param); 30 | void log_set_error(bool param); 31 | void log_set_off(bool param); 32 | 33 | bool is_log_off( void ); 34 | bool is_log_error( void ); 35 | bool is_log_warn( void ); 36 | bool is_log_info( void ); 37 | bool is_log_verbose( void ); 38 | bool is_log_debug( void ); 39 | bool is_log_trace( void ); 40 | 41 | bool load_log_buffer( void ); 42 | void write_log_file( void ); 43 | void free_log_heap( void ); 44 | 45 | #define LOG_INF __FILE__, __FUNCTION__, __LINE__ 46 | 47 | #endif /* LOGGING_H_ */ 48 | /******************************************************************************/ 49 | /******************************* END OF FILE **********************************/ 50 | /******************************************************************************/ -------------------------------------------------------------------------------- /wolfssl_wrapper/wolfssl_wrapper.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | /** @file wolfssl_wrapper.h */ 13 | #ifndef __OPENSSL_WRAPPER_H__ 14 | #define __OPENSSL_WRAPPER_H__ 15 | 16 | #include 17 | 18 | /* These two must be the first wolf includes */ 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | /**************************************************************************/ 25 | /****************** GLOBAL STRUCTURE PROTOTYPES ***************************/ 26 | /**************************************************************************/ 27 | struct PemInventoryItem 28 | { 29 | char* cert; /* the naked pem */ 30 | char* thumbprint_string; 31 | bool has_private_key; 32 | }; 33 | typedef struct PemInventoryItem PemInventoryItem; 34 | 35 | struct PemInventoryList 36 | { 37 | int item_count; 38 | PemInventoryItem** items; 39 | }; 40 | typedef struct PemInventoryList PemInventoryList; 41 | 42 | /**************************************************************************/ 43 | /******************* GLOBAL FUNCTION PROTOTYPES ***************************/ 44 | /**************************************************************************/ 45 | void PemInventoryItem_free(PemInventoryItem* pem); 46 | 47 | void PemInventoryList_free(PemInventoryList* list); 48 | 49 | int ssl_seed_rng(const char* b64entropy); 50 | 51 | bool ssl_generate_rsa_keypair(int keySize); 52 | bool ssl_generate_ecc_keypair(int keySize); 53 | 54 | char* ssl_generate_csr(const char* asciiSubject, size_t* csrLen, 55 | char** pMessage); 56 | 57 | unsigned long ssl_save_cert_key(const char* storePath, const char* keyPath, 58 | const char* password, const char* cert, char** pMessage); 59 | 60 | int ssl_read_store_inventory(const char* path, const char* password, 61 | PemInventoryList** ppPemList); 62 | 63 | bool ssl_PemInventoryItem_create(struct PemInventoryItem** pem, 64 | const char* certASCII); 65 | 66 | bool ssl_PemInventoryItem_create(struct PemInventoryItem** ppPEMout, 67 | const char* pCertASCII); 68 | 69 | bool ssl_Store_Cert_add(const char* storePath, const char* certASCII); 70 | 71 | bool ssl_remove_cert_from_store(const char* storePath, const char* searchThumb,\ 72 | const char* keyPath, const char* password); 73 | 74 | bool ssl_init(void); 75 | 76 | bool ssl_cleanup(void); 77 | 78 | bool ssl_is_cert_active(char* certFile); 79 | 80 | /* This is required to allow us to seed the wolfssl random with supplied data */ 81 | #define CUSTOM_RAND_TYPE byte /* Our custom function returns a byte at a time */ 82 | extern byte custom_rng_seed_generator(void); 83 | #undef CUSTOM_RAND_GENERATE /* remove the default function */ 84 | #define CUSTOM_RAND_GENERATE custom_rng_seed_generator /* Point to our func */ 85 | 86 | #endif 87 | 88 | /******************************************************************************/ 89 | /******************************* END OF FILE **********************************/ 90 | /******************************************************************************/ -------------------------------------------------------------------------------- /constants.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef __CONSTANTS_H__ 14 | #define __CONSTANTS_H__ 15 | 16 | /* These are the GUIDs of the capabilities supported by the Agent */ 17 | #define CAP_PEM_INVENTORY "a809ce1f-1eea-4738-a38e-15708c89c981" 18 | #define CAP_PEM_MANAGEMENT "1d411b36-ae72-433f-9f3f-8593e836a1af" 19 | #define CAP_PEM_REENROLLMENT "aa015d10-cffc-41f7-a9a4-c9615f6f3bdf" 20 | #define CAP_FETCH_LOGS "0D8CF0C8-56CA-4B8A-B16A-C062018E170D" 21 | 22 | /* KF v9 adds in text Capabilities - they are defined in agent.c */ 23 | extern const char* cap_pem_inventory; 24 | extern const char* cap_pem_management; 25 | extern const char* cap_pem_reenrollment; 26 | extern const char* cap_fetch_logs; 27 | 28 | /* The following capabilities are not implemented in the C-Agent */ 29 | #define CAP_AWS_INVENTORY "afb8c78d-1436-4c93-a8e7-0218c2cb6955" 30 | #define CAP_AWS_MANAGEMENT "56c1e4f9-366c-44b3-8cae-1b8f39e2026c" 31 | #define CAP_F5_INVENTORY "9b04e8de-e2ce-4a50-bd3f-31037d3ee751" 32 | #define CAP_F5_MANAGEMENT "e9a34338-c99f-46da-94d2-4f97dc6943af" 33 | #define CAP_FTP_INVENTORY "76f99194-c129-4954-b76e-11c80816643e" 34 | #define CAP_FTP_MANAGEMENT "4ae72d54-7f91-4359-9da5-e1f633031070" 35 | #define CAP_IIS_INVENTORY "5b9cf048-e95f-4331-a510-0cdfabac1703" 36 | #define CAP_IIS_MANAGEMENT "ce2325e3-801c-4576-9b0a-5fdcaf66aa88" 37 | #define CAP_IIS_REENROLLMENT "b80019d6-df3d-4a8c-9e60-1977b806f545" 38 | #define CAP_JKS_DISCOVERY "74d9f7c5-b2ac-4f21-a72b-29b5adc90651" 39 | #define CAP_JKS_INVENTORY "67b63010-d738-47c3-87a2-9f289466c881" 40 | #define CAP_JKS_MANAGEMENT "b614f9e2-c56b-421c-b548-03d42eb32f8a" 41 | #define CAP_JKS_REENROLLMENT "48743e90-108e-4dc3-b81c-7fb830215d1f" 42 | #define CAP_NETSCALER_INVENTORY "78ff1c19-893f-4e5e-90a5-0459f1823778" 43 | #define CAP_NETSCALER_MANAGEMENT "b7c94ad1-8a33-45ea-98b3-d77ca14e7830" 44 | #define CAP_PEM_DISCOVERY "e98151de-53ad-4b99-b52a-f37118ec0c5c" 45 | 46 | #define EMPTY_SESSION "c0ffee00-feed-f00d-cafe-c0ffeec0ffee" 47 | 48 | /* 32 hex chars + 4 dashes + 1 NULL-terminator */ 49 | #define GUID_SIZE 37 50 | 51 | enum InventoryStatus 52 | { 53 | INV_STAT_ADD = 1, 54 | INV_STAT_MOD = 2, 55 | INV_STAT_REM = 3, 56 | INV_STAT_UNCH = 4 57 | }; 58 | 59 | enum AgentPlatform 60 | { 61 | PLAT_UNK = 0, 62 | PLAT_NET = 1, 63 | PLAT_JAVA = 2, 64 | PLAT_MAC = 3, 65 | PLAT_ANDROID = 4, 66 | PLAT_NATIVE = 5 67 | }; 68 | 69 | enum AgentApiResultStatus 70 | { 71 | STAT_UNK = 0, 72 | STAT_SUCCESS = 1, 73 | STAT_WARN = 2, 74 | STAT_ERR = 3 75 | }; 76 | 77 | enum JobCompleteStatus 78 | { 79 | JOB_COMP_UNK = 0, 80 | JOB_COMP_PROC = 1, 81 | JOB_COMP_SUCCESS = 2, 82 | JOB_COMP_WARN = 3, 83 | JOB_COMP_ERR = 4 84 | }; 85 | 86 | enum OperationType 87 | { 88 | OP_UNK = 0, 89 | OP_INV = 1, 90 | OP_ADD = 2, 91 | OP_REM = 3, 92 | OP_CREATE = 4, 93 | OP_CREATE_ADD = 5 94 | }; 95 | 96 | #endif 97 | 98 | /******************************************************************************/ 99 | /******************************* END OF FILE **********************************/ 100 | /******************************************************************************/ -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc -std=gnu99 2 | #WARN_FLAGS = -Wall -Wextra -Werror 3 | 4 | CFLAGS += -fPIC 5 | # WARNING OPTIONS 6 | # turn on typical warnings 7 | CFLAGS += -Wall 8 | CFLAGS += -Wextra 9 | # turn warnings into errors 10 | CFLAGS += -Werror 11 | 12 | # suppress warnings for some things 13 | CFLAGS += -Wno-unused-parameter 14 | CFLAGS += -Wno-missing-field-initializers 15 | CFLAGS += -Wno-missing-braces 16 | CFLAGS += -Wno-unused-variable 17 | CFLAGS += -Wno-unused-but-set-variable 18 | CFLAGS += -Wno-unused-label 19 | CFLAGS += -Wno-unused-function 20 | CFLAGS += -Wno-pointer-sign 21 | CFLAGS += -Wno-deprecated-declarations 22 | 23 | CFLAGS += -fno-strict-aliasing 24 | CFLAGS += -Wno-ignored-qualifiers 25 | 26 | DEBUG_FLAGS = -g0 -O0 27 | DEFINES = 28 | #DEFINES += -D__RUN_CHAIN_JOBS__ 29 | #DEFINES += -D__INFINITE_AGENT__ 30 | 31 | WOLFLIBS = -I ./ -I/usr/local/include/wolfssl -I/usr/local/include/curl \ 32 | -L/usr/local/lib -L/usr/local/include/wolfssl/wolfcrypt \ 33 | -L/usr/local/include/wolfssl 34 | WOLFLIBS += -lcurl -lwolfssl 35 | WOLFLIBS += -no-pie 36 | 37 | OPENLIBS = -I ./ -I/usr/local/include/curl -L/usr/local/lib 38 | OPENLIBS = -lcrypto -lcurl 39 | 40 | # TPM specific variables for the tpm2tss stack 41 | # The following TSSLIBS definition is for the Raspberry Pi 42 | RPI_TSSLIBS = -L/usr/lib/arm-linux-gnueabihf/engines-1.1/ -L/usr/lib/arm-linux-gnueabihf/engines-3/ -ltpm2tss 43 | # The following TSSLIBS definition is for a linux machine 44 | TSSLIBS = -ltpm2tss -L/usr/lib/x86_64-linux-gnu/engines-1.1/ -L/usr/lib/arm-linux-gnueabihf/engines-3/ 45 | 46 | vpath %.c ./ ./lib ./wolfssl_wrapper 47 | SRC := $(wildcard *.c) \ 48 | $(wildcard lib/*.c) \ 49 | $(wildcard wolfssl_wrapper/*.c) 50 | OBJS = $(SRC:%.c=%.o) 51 | 52 | OSRC := $(wildcard *.c) \ 53 | $(wildcard lib/*.c) \ 54 | $(wildcard openssl_wrapper/*.c) 55 | OOBJ = $(OSRC:%.c=%.o) 56 | 57 | # The base wolf build for a 64-bit os 58 | wolftest: DEFINES += -D__WOLF_SSL__ 59 | wolftest: ${OBJS} 60 | ${CC} ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o agent $^ ${WOLFLIBS} 61 | 62 | # The base wolfSSL build to create a shared library 63 | wolflib: DEFINES += -D__WOLF_SSL__ -D__MAKE_LIBRARY__ 64 | wolflib: ${OBJS} 65 | ${CC} -shared ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o libagent.so $^ ${WOLFLIBS} 66 | 67 | # How to install the shared library 68 | wolfinstall: libagent.so 69 | sudo cp libagent.so /usr/lib 70 | sudo chmod 755 /usr/lib/libagent.so 71 | 72 | # The wolfSSL build for any 32-bit OS like RaspOS 73 | wolfpi: DEFINES += -D__WOLF_SSL__ -Wno-format 74 | wolfpi: ${OBJS} 75 | ${CC} ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o agent $^ ${WOLFLIBS} 76 | 77 | # The base openSSL build for a 64-bit OS 78 | opentest: DEFINES += -D__OPEN_SSL__ 79 | opentest: ${OOBJ} 80 | ${CC} ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o agent $^ ${OPENLIBS} 81 | 82 | # The base openSSL build to create a shared library 83 | openlib: DEFINES += -D__OPEN_SSL__ -D__MAKE_LIBRARY__ 84 | openlib: ${OOBJ} 85 | ${CC} -shared ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o libagent.so $^ ${OPENLIBS} 86 | 87 | # The openSSL build for any 32-bit OS like RaspOS 88 | openpi: DEFINES += -D__OPEN_SSL__ -Wno-format 89 | openpi: ${OOBJ} 90 | ${CC} ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o agent $^ ${OPENLIBS} 91 | 92 | # How to install the shared library 93 | openinstall: libagent.so 94 | sudo cp libagent.so /usr/lib 95 | sudo chmod 755 /usr/lib/libagent.so 96 | 97 | # The base build for a Raspberry Pi with a TPM installed 98 | rpi9670test: DEFINES += -D__OPEN_SSL__ -D__TPM__ -Wno-format 99 | rpi9670test: ${OOBJ} 100 | ${CC} ${CFLAGS} ${DEBUG_FLAGS} ${DEFINES} -o agent $^ ${OPENLIBS} ${RPI_TSSLIBS} 101 | 102 | # define the builds 103 | %.o: %.c 104 | $(info building $@ from $<) 105 | - @${CC} ${CFLAGS} ${DEFINES} ${WARN_FLAGS} ${DEBUG_FLAGS} ${C_STD} -c -o $@ $< 106 | 107 | # define the clean or delete commands 108 | .PHONY: deleteallobs 109 | deleteallobs: 110 | rm -rf ${OBJS} ${OOBJ} agent 111 | 112 | .PHONY: cleanall 113 | cleanall: deleteallobs 114 | 115 | .PHONY: clean 116 | clean: deleteallobs 117 | -------------------------------------------------------------------------------- /lib/json.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) 3 | All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | #ifndef CCAN_JSON_H 25 | #define CCAN_JSON_H 26 | 27 | #include 28 | #include 29 | 30 | typedef enum { 31 | JSON_NULL, 32 | JSON_BOOL, 33 | JSON_STRING, 34 | JSON_NUMBER, 35 | JSON_ARRAY, 36 | JSON_OBJECT, 37 | } JsonTag; 38 | 39 | typedef struct JsonNode JsonNode; 40 | 41 | struct JsonNode 42 | { 43 | /* only if parent is an object or array (NULL otherwise) */ 44 | JsonNode *parent; 45 | JsonNode *prev, *next; 46 | 47 | /* only if parent is an object (NULL otherwise) */ 48 | char *key; /* Must be valid UTF-8. */ 49 | 50 | JsonTag tag; 51 | union { 52 | /* JSON_BOOL */ 53 | bool bool_; 54 | 55 | /* JSON_STRING */ 56 | char *string_; /* Must be valid UTF-8. */ 57 | 58 | /* JSON_NUMBER */ 59 | double number_; 60 | 61 | /* JSON_ARRAY */ 62 | /* JSON_OBJECT */ 63 | struct { 64 | JsonNode *head, *tail; 65 | } children; 66 | }; 67 | }; 68 | 69 | /*** Encoding, decoding, and validation ***/ 70 | 71 | JsonNode *json_decode (const char *json); 72 | char *json_encode (const JsonNode *node); 73 | char *json_encode_string (const char *str); 74 | char *json_stringify (const JsonNode *node, const char *space); 75 | void json_delete (JsonNode *node); 76 | 77 | bool json_validate (const char *json); 78 | 79 | /*** Lookup and traversal ***/ 80 | 81 | JsonNode *json_find_element (JsonNode *array, int index); 82 | JsonNode *json_find_member (JsonNode *object, const char *key); 83 | 84 | JsonNode *json_first_child (const JsonNode *node); 85 | 86 | #define json_foreach(i, object_or_array) \ 87 | for ((i) = json_first_child(object_or_array); \ 88 | (i) != NULL; \ 89 | (i) = (i)->next) 90 | 91 | /*** Construction and manipulation ***/ 92 | 93 | JsonNode *json_mknull(void); 94 | JsonNode *json_mkbool(bool b); 95 | JsonNode *json_mkstring(const char *s); 96 | JsonNode *json_mknumber(double n); 97 | JsonNode *json_mkarray(void); 98 | JsonNode *json_mkobject(void); 99 | 100 | void json_append_element(JsonNode *array, JsonNode *element); 101 | void json_prepend_element(JsonNode *array, JsonNode *element); 102 | void json_append_member(JsonNode *object, const char *key, JsonNode *value); 103 | void json_prepend_member(JsonNode *object, const char *key, JsonNode *value); 104 | 105 | void json_remove_from_parent(JsonNode *node); 106 | 107 | /*** Debugging ***/ 108 | 109 | /* 110 | * Look for structure and encoding problems in a JsonNode or its descendents. 111 | * 112 | * If a problem is detected, return false, writing a description of the problem 113 | * to errmsg (unless errmsg is NULL). 114 | */ 115 | bool json_check(const JsonNode *node, char errmsg[256]); 116 | 117 | char* json_get_value_string(JsonNode* node); 118 | 119 | char* json_get_member_string(JsonNode* objNode, char* key); 120 | 121 | double json_get_value_number(JsonNode* node, double defaultVal); 122 | 123 | double json_get_member_number(JsonNode* objNode, char* key, double defaultVal); 124 | 125 | bool json_get_value_bool(JsonNode* node, bool defaultVal); 126 | 127 | bool json_get_member_bool(JsonNode* objNode, char* key, bool defaultVal); 128 | 129 | int json_array_size(JsonNode* arrNode); 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /openssl_wrapper/openssl_wrapper.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | #ifndef __OPENSSL_WRAPPER_H__ 13 | #define __OPENSSL_WRAPPER_H__ 14 | 15 | #include 16 | 17 | /**************************************************************************/ 18 | /****************** GLOBAL STRUCTURE PROTOTYPES ***************************/ 19 | /**************************************************************************/ 20 | 21 | /** */ 22 | /* These are the AGENT versions of the ssl_PemInventoryItems and are */ 23 | /* allocated this ssl abstraction layer. They are freed in the AGENT layer. */ 24 | /* */ 25 | 26 | /** */ 27 | /* Define a PEM for use by the platform, the PEM may have */ 28 | /* an encoded private key stored with it (or not). If not, */ 29 | /* there is an encoded file with the private key that is */ 30 | /* separate from the cert. */ 31 | /* */ 32 | /* The thumbprint is an ASCII sha1 hash of the cert */ 33 | /* */ 34 | struct PemInventoryItem 35 | { 36 | char* cert; 37 | char* thumbprint_string; 38 | bool has_private_key; 39 | }; 40 | typedef struct PemInventoryItem PemInventoryItem; 41 | 42 | /** */ 43 | /* Define a list of PEM certs held inside of a certificate store. */ 44 | /* This is for use by the platform's inventory function. */ 45 | /* */ 46 | struct PemInventoryList 47 | { 48 | int item_count; 49 | PemInventoryItem** items; 50 | }; 51 | typedef struct PemInventoryList PemInventoryList; 52 | 53 | /**************************************************************************/ 54 | /******************* GLOBAL FUNCTION PROTOTYPES ***************************/ 55 | /**************************************************************************/ 56 | void PemInventoryItem_free(PemInventoryItem* pem); 57 | 58 | void PemInventoryList_free(PemInventoryList* list); 59 | 60 | int ssl_seed_rng(const char* b64entropy); 61 | 62 | #if defined(__TPM__) 63 | bool ssl_generate_rsa_keypair(int keySize, const char* file); 64 | #else 65 | bool ssl_generate_rsa_keypair(int keySize); 66 | #endif 67 | 68 | bool ssl_generate_ecc_keypair(int keySize); 69 | 70 | char* ssl_generate_csr(const char* asciiSubject, size_t* csrLen, 71 | char** pMessage); 72 | 73 | unsigned long ssl_save_cert_key(const char* storePath, const char* keyPath, 74 | const char* password, const char* cert, char** pMessage); 75 | 76 | int ssl_read_store_inventory(const char* path, const char* password, 77 | PemInventoryList** pPemList); 78 | 79 | bool ssl_PemInventoryItem_create(struct PemInventoryItem** pem, 80 | const char* certASCII); 81 | 82 | bool ssl_Store_Cert_add(const char* storePath, const char* certASCII); 83 | 84 | bool ssl_remove_cert_from_store(const char* storePath, const char* searchThumb,\ 85 | const char* keyPath, const char* password); 86 | 87 | void ssl_init(void); 88 | 89 | void ssl_cleanup(void); 90 | 91 | bool ssl_is_cert_active(char* certFile); 92 | 93 | 94 | #endif /* OPENSSL_WRAPPER_H */ 95 | 96 | /******************************************************************************/ 97 | /******************************* END OF FILE **********************************/ 98 | /******************************************************************************/ -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | #ifndef __CONFIG_H__ 13 | #define __CONFIG_H__ 14 | 15 | #include 16 | #include 17 | 18 | #define MAX_CONFIG_FILE_LEN 4096 /* Config file should never be this long */ 19 | #define DATE_TIME_LEN 14 /* YYYYMMDDHHMMSS */ 20 | 21 | struct ConfigData 22 | { 23 | char* Hostname; /**< The hostname of the Keyfactor Platform */ 24 | char* VirtualDirectory; /**< The proxy used by the Platform */ 25 | bool UseSsl; /**< true = https, false = http */ 26 | char* AgentId; /**< Set by the platform during registration */ 27 | char* AgentName; /**< Set by the agent's config.json file */ 28 | char* ClientParameterPath; /**< Any Agent Registration Parameters */ 29 | char* Username; /**< DOMAIN\username for the Keyfactor Platform */ 30 | char* Password; /**< Password for the Keyfactor Platform */ 31 | char* TrustStore; /**< File with the certificates the agent trusts */ 32 | char* AgentCert; /**< File with the Agent's cert */ 33 | char* AgentKey; /**< File with the Agent's private key */ 34 | char* AgentKeyPassword; /**< Password to decrypt the Agent's priv key */ 35 | char* CSRKeyType; /**< The key type to use for the Agent (ECC/RSA) */ 36 | int CSRKeySize; /**< The key size for the Agent */ 37 | char* CSRSubject; /**< Subject for the agent CSR. Must be valid X509 */ 38 | bool EnrollOnStartup; /**< Persistent variable. True until agent enrolls*/ 39 | bool UseBootstrapCert; /**< True if agent uses bootstrap certs to enroll*/ 40 | char* BootstrapCert; /**< Path & filename of bootstrap cert */ 41 | char* BootstrapKey; /**< Path & filename of the bootstrap key */ 42 | char* BootstrapKeyPassword; /**< String holding password for key */ 43 | int httpRetries; /**< # of times to retry a failed HTTP connection */ 44 | int retryInterval; /**< Interval (in seconds) between HTTP retries */ 45 | char* LogFile; /**< File where agent logs are stored. */ 46 | size_t LogFileIndex; /**< Last byte written to the log file */ 47 | bool UseAgentCert; /**< true = the agent uses a cert for mTLS authentication, false = no mTLS is used */ 48 | }; 49 | 50 | /******************************************************************************/ 51 | /***************************** GLOBAL VARIABLES *******************************/ 52 | /******************************************************************************/ 53 | extern struct ConfigData* ConfigData; 54 | extern bool config_loaded; 55 | extern char* config_location; 56 | extern bool use_host_as_agent_name; 57 | 58 | /******************************************************************************/ 59 | /***************************** GLOBAL FUNCTIONS *******************************/ 60 | /******************************************************************************/ 61 | 62 | /** 63 | * Convert a configuration json string into a configuration structure. Call 64 | * this function directly if you are not using the config_load() function 65 | * to grab the configuration from the file config.json directly. 66 | * 67 | * For example: 68 | * You are using a securely encoded file & need to decode it before the 69 | * json is readable. Perform the decoding & pass the decoded string to this 70 | * function. 71 | * 72 | * NOTE: This configuration structure needs to be freed by calling 73 | * ConfigData_free(ConfigData*) before ending the process thread. 74 | * 75 | * @param - [Input] : buf = the configuration json string, NULL terminated 76 | * @return - success : a filled out configuration structure 77 | * failure : NULL 78 | */ 79 | struct ConfigData* config_decode(const char* buf); 80 | 81 | /** 82 | * Load data from the configuration file into the configuration data structure 83 | * The configuration file "config.json" must be in the same directory as 84 | * the agent. 85 | * 86 | * NOTE: This configuration structure needs to be freed by calling 87 | * ConfigData_free(ConfigData*) before ending the process thread. 88 | * 89 | * @param - none 90 | * @return - a reference to a filled out ConfigData element 91 | */ 92 | struct ConfigData* config_load( void ); 93 | 94 | /** 95 | * Convert the configuration data structure into a json string. Call 96 | * this function directly if you are not using the config_save() function 97 | * to save the configuration to the file config.json file directly. 98 | * 99 | * For example: 100 | * You are using a securely encoded file & need to encode it before saving 101 | * the file to disk. 102 | * 103 | * @return - A json encoded string 104 | */ 105 | char* config_to_json( void ); 106 | 107 | /** 108 | * Save the configuration structure to "config.json" on the file system 109 | * 110 | * @param - [Input] config = a reference to a filled out ConfigData element 111 | * @return - success : true 112 | * failure : false 113 | */ 114 | bool config_save( void ); 115 | 116 | /** 117 | * Build the url from information in the config data and the relative 118 | * endpoint passed from the platform. NOTE: v7.4+ of the platform 119 | * has a bug where it will always pass KeyfactorAgents/ make sure to 120 | * strip that from the string passed in relPath. 121 | * 122 | * @param - [Input] : config = configuration data structure 123 | * @param - [Input] : relPath = the endpoint the Platfrom wants to hit 124 | * @param - [Input] : vdirFromConfig = yes if you need to use a proxy at the 125 | * platform side. no otherwise. 126 | * @return - success : a completed URL 127 | * failure : NULL 128 | */ 129 | char* config_build_url(const char* relPath, bool vdirFromConfig); 130 | 131 | /** 132 | * Sanity check on the configuration file to check for errors before launching 133 | * Should run after the log files are up and running to log errors to disk. 134 | * 135 | * @param - none, operates on the global ConfigData variable 136 | * @return - success : true 137 | * failure : false 138 | */ 139 | bool validate_configuration( void ); 140 | 141 | /** 142 | * Release memory associated with the ConfigData element 143 | * 144 | * @returns none 145 | */ 146 | void ConfigData_free( void ); 147 | 148 | #endif /* __CONFIG_H__ */ 149 | 150 | /******************************************************************************/ 151 | /******************************* END OF FILE **********************************/ 152 | /******************************************************************************/ -------------------------------------------------------------------------------- /agent.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef AGENT_H_ 14 | #define AGENT_H_ 15 | 16 | #include "session.h" 17 | #include "schedule.h" 18 | #include "config.h" 19 | 20 | int run_job(struct SessionJob* job); 21 | int init_platform( int argc, char* argv[] ); 22 | bool release_platform( void ); 23 | #ifdef __MAKE_LIBRARY__ 24 | int KF_main( int argc, char* argv[]); 25 | #endif 26 | 27 | /******************************************************************************/ 28 | /************************* SYSTEM GLOBAL VARIABLES ****************************/ 29 | /******************************************************************************/ 30 | extern struct SessionInfo SessionData; 31 | extern struct ScheduledJob* JobList; 32 | extern struct ConfigData* ConfigData; 33 | extern struct ScheduledJob* currentJob; /* Defined in schedule.c */ 34 | 35 | #if defined(__OPEN_SSL__) 36 | extern char engine_id[21]; 37 | #endif 38 | 39 | #if defined(__TPM__) 40 | #include 41 | extern ENGINE* e; 42 | #endif 43 | 44 | /* Versioning Information */ 45 | /* 2.0.0.0 = Created wrapper class */ 46 | /* 2.1.0.0 = Added TPM for raspberry pi into version */ 47 | /* 2.5.0.0 = Added the following functionality: */ 48 | /* * Log to file upon agent shutting down */ 49 | /* * Agent runs through all jobs once, */ 50 | /* this allows cron to schedule it */ 51 | /* * Added warning log level */ 52 | /* * Added a priority queue for agent jobs upon initial retrieval */ 53 | /* * Ignore any chained jobs - inventory jobs will always */ 54 | /* run immediate */ 55 | /* * Check if a store is a directory before reading/writing */ 56 | /* * Check if re-enrollment, inventory, or management jobs */ 57 | /* are targeting the agent certificate & don't run those jobs. */ 58 | /* * Added agent cert re-enrollment on Error response to reenroll */ 59 | /* 2.5.1.0 = Added the following: */ 60 | /* * Fixed a bug in openSSL cleanup causing segfaults */ 61 | /* * Added a check to the inventory and management jobs to */ 62 | /* validate cert store exists */ 63 | /* * Added sanity checks on the initial configuration file */ 64 | /* * Set default logging level to INFO */ 65 | /* 2.5.2.0 = Fixed bugs in openSSL layer when performing management jobs */ 66 | /* 2.6.0.0 = Modified Agent to work with Keyfactor Platform v8.5.2 */ 67 | /* Includes fixes to wrapper layers for managing keypairs */ 68 | /* 2.6.1.0 = Added -c switch to allow config file to be passed as a parameter */ 69 | /* 2.7.0.0 = Added -h switch to use hostname_datetime for agent name */ 70 | /* Added second registration hit for use with RegistrationHandler */ 71 | /* 2.7.1.0 = Fixed bug with agent cert expiry */ 72 | /* 2.7.2.0 = Fixed bug with HResult being returned instead of CodeString */ 73 | /* 2.8.0.0 = Added the following: */ 74 | /* * Updated licensing information */ 75 | /* * Added Bootstrap certificate support via config file */ 76 | /* 2.8.1.0 = Fixed logging to file bug */ 77 | /* 2.8.2.0 = Fixed some memory leaks */ 78 | /* 2.8.3.0 = Fixed more memory leak posibilities */ 79 | /* 2.8.4.0 = Fixed re-registration issue where AgentId was not updated */ 80 | /* 2.8.5.0 = Changed logging functionality */ 81 | /* 2.8.6.0 = Minor bug fixes */ 82 | /* 2.8.7.0 = Fixed Agent cert renewal issue for A0100007 and A0100008 codes */ 83 | /* 2.8.8.0 = Agent now re-registers as new agent if its own certificate */ 84 | /* has expired */ 85 | /* 2.9.0.0 = Added custom client parameters stub to sessions. These params */ 86 | /* get added to every hit of /Session/Register */ 87 | /* 2.9.1.0 = Fixed issue where CSRs max length was too small for RSA/4096 */ 88 | /* 2.9.2.0 = Fixed but in getting date time for agent's name */ 89 | /* 2.10.0.0 = Fixed issue with openSSL management remove job */ 90 | /* 2.11.0.0 = Allow agent to be made into a library */ 91 | /* 2.12.0.0 = Update bootstrap certificate use case */ 92 | /* 2.14.0.0 = Add command line switch to send X-ARR-ClientCert header to KF */ 93 | /* 2.14.1.0 = Updated logging associated with v2.14.0.0 */ 94 | /* 2.14.2.0 = Updated parameter processing & usage printing */ 95 | /* 2.14.2.1 = Set up long options & cleaned usage output */ 96 | /* 2.14.3.0 = Upgraded for EJBCA DN State Key & openssl v3.0 compatibility */ 97 | /* 2.15.0.0 = Minor bug improvements */ 98 | /* 2.15.1.0 = Updated for v12 API modifications */ 99 | /* 2.15.2.0 = Updated DTO for Warning to allow agent rotation */ 100 | /* 2.16.0.0 = Updated TPM for default passkey */ 101 | /* 2.16.0.1 = Updated Makefile for OpenSSL v3.x */ 102 | /* 2.16.1.0 = Modified agent to not send GUID capabilities. Compile bug fix */ 103 | /* 2.17.0.0 = Modified agent to accept ECDSA as well as ECC for a keytype */ 104 | /* 2.18.0.0 = Modified agent to skip using agent certificate & openssl fix */ 105 | /* 2.19.0.0 = Modified agent to fix bug with writing agent certificate to file*/ 106 | #define AGENT_MAJOR 2ULL 107 | #define AGENT_MINOR 19ULL 108 | #define AGENT_MICRO 0ULL 109 | #define AGENT_BUILD 0ULL 110 | 111 | #define AGENT_VERSION \ 112 | ((AGENT_MAJOR << 48) | \ 113 | (AGENT_MINOR << 32) | \ 114 | (AGENT_MICRO << 16) | \ 115 | (AGENT_BUILD)) 116 | 117 | #endif /* AGENT_H_ */ 118 | -------------------------------------------------------------------------------- /csr.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include 14 | #include "csr.h" 15 | #include "logging.h" 16 | #include "global.h" 17 | 18 | #ifdef __WOLF_SSL__ 19 | #include "wolfssl_wrapper/wolfssl_wrapper.h" 20 | #else 21 | #ifdef __OPEN_SSL__ 22 | #include "openssl_wrapper/openssl_wrapper.h" 23 | #else 24 | #ifdef __TPM__ 25 | #else 26 | #endif 27 | #endif 28 | #endif 29 | 30 | /******************************************************************************/ 31 | /*************************** GLOBAL VARIABLES *********************************/ 32 | /******************************************************************************/ 33 | 34 | /******************************************************************************/ 35 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 36 | /******************************************************************************/ 37 | 38 | /******************************************************************************/ 39 | /************************** LOCAL GLOBAL VARIABLES ****************************/ 40 | /******************************************************************************/ 41 | 42 | /******************************************************************************/ 43 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 44 | /******************************************************************************/ 45 | 46 | /******************************************************************************/ 47 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 48 | /******************************************************************************/ 49 | /** */ 50 | /* Generate a new keypair by calling the correct function in the */ 51 | /* SSL wrapper layer */ 52 | /* */ 53 | /* @param - [Input] keyType: The type of key (ECC or RSA) */ 54 | /* @param - [Input] keySize: The size of the key (192, 256, etc.) */ 55 | /* @return - success: true */ 56 | /* failure: false */ 57 | /* */ 58 | #if defined(__TPM__) 59 | bool generate_keypair(const char* keyType, int keySize, const char* path) 60 | #else 61 | bool generate_keypair(const char* keyType, int keySize) 62 | #endif 63 | { 64 | bool bResult = false; 65 | 66 | log_verbose("%s::%s(%d) : Generating key pair with type %s and length %d", 67 | LOG_INF, keyType, keySize); 68 | 69 | if(strcasecmp(keyType, "RSA") == 0) 70 | { 71 | #if defined(__TPM__) 72 | if ( (NULL == path) || (0 == strcasecmp("",path)) ) 73 | { 74 | log_error("%s::%s(%d) : Error, you must specify a private key " 75 | "path when using a TPM", LOG_INF); 76 | return false; 77 | } 78 | bResult = ssl_generate_rsa_keypair(keySize, path); 79 | #else 80 | bResult = ssl_generate_rsa_keypair(keySize); 81 | #endif 82 | } 83 | else if(( strcasecmp(keyType, "ECC") == 0) || 84 | ( strcasecmp(keyType, "ECDSA") == 0)) 85 | { 86 | #if defined(__TPM__) 87 | log_error("%s::%s(%d) : Error, SLB9670 with tpm2tss engine does " 88 | "not support ECC keygen", LOG_INF); 89 | #else 90 | bResult = ssl_generate_ecc_keypair(keySize); 91 | #endif 92 | } 93 | else 94 | { 95 | log_error("%s::%s(%d) : Invalid key type %s", 96 | LOG_INF, keyType); 97 | } 98 | 99 | return bResult; 100 | } /* generate_keypair */ 101 | 102 | /** */ 103 | /* Request the crypto layer to generate a new CSR using the subject provided. */ 104 | /* This request expects an ASCII CSR to be returned. */ 105 | /* */ 106 | /* @param - [Input] : asciiSubject string with the subject line */ 107 | /* e.g., CN=1234,OU=NA,O=Keyfactor,C=US */ 108 | /* @param - [Output] : csrLen the # of ASCII characters in the csr */ 109 | /* @return - success : the CSR string minus the header and footer */ 110 | /* failure : NULL */ 111 | /* */ 112 | char* generate_csr(const char* asciiSubject, size_t* csrLen, char** pMessage, 113 | enum AgentApiResultStatus* pStatus) 114 | { 115 | char* csrString = NULL; 116 | *pStatus = STAT_UNK; 117 | csrString = ssl_generate_csr(asciiSubject, csrLen, pMessage); 118 | if ( NULL != csrString ) 119 | { 120 | *pStatus = STAT_SUCCESS; 121 | } 122 | else 123 | { 124 | *pStatus = STAT_ERR; 125 | } 126 | return csrString; 127 | } /* generate_csr */ 128 | 129 | /** */ 130 | /* Request the crypto layer to save the cert and key to the locations */ 131 | /* requested. The crypto layer uses the temporary key it has generated */ 132 | /* to store into the location requested. */ 133 | /* */ 134 | /* @param - [Input] : storePath = the store location for the cert */ 135 | /* @param - [Input] : keyPath = the location to save the key, if NULL or */ 136 | /* blank, store the encoded key appended to the cert. */ 137 | /* @param - [Input] : password = the password for the private key */ 138 | /* @param - [Input] : cert = The cert in an ASCII encoded string */ 139 | /* @param - [Output]: pMessage = a string array containing any messages */ 140 | /* we want to pass back to the calling function */ 141 | /* @param - [Output]: pStatus = The status code to report back to the API */ 142 | /* @return - success : 0 */ 143 | /* failure : an unsigned long error code */ 144 | /* */ 145 | unsigned long save_cert_key(const char* storePath, const char* keyPath, 146 | const char* password, const char* cert, 147 | char** pMessage, enum AgentApiResultStatus* pStatus) 148 | { 149 | unsigned long err = 0; 150 | err = ssl_save_cert_key(storePath, keyPath, password, cert, pMessage); 151 | if ( 0 != err ) 152 | { 153 | *pStatus = STAT_ERR; 154 | } 155 | else 156 | { 157 | *pStatus = STAT_SUCCESS; 158 | } 159 | return err; 160 | } /* save_cert_key */ 161 | 162 | /******************************************************************************/ 163 | /******************************* END OF FILE **********************************/ 164 | /******************************************************************************/ -------------------------------------------------------------------------------- /dto.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #ifndef CSS_DTO_H 14 | #define CSS_DTO_H 15 | 16 | #include 17 | #include 18 | #include "constants.h" 19 | 20 | 21 | struct AgentApiResult 22 | { 23 | enum AgentApiResultStatus Status; 24 | struct 25 | { 26 | int Code; 27 | char* Message; 28 | char* CodeString; 29 | } Error; 30 | }; 31 | 32 | struct ClientParameter 33 | { 34 | char* Key; 35 | char* Value; 36 | }; 37 | 38 | struct SessionRegisterReq 39 | { 40 | char* TenantId; 41 | char* ClientMachine; 42 | enum AgentPlatform AgentPlatform; 43 | char** Capabilities; 44 | int Capabilities_count; 45 | uint64_t AgentVersion; 46 | char* AgentId; 47 | struct ClientParameter** ClientParameters; 48 | int ClientParameters_count; 49 | char* CSR; 50 | }; 51 | 52 | struct SessionJob 53 | { 54 | char* JobTypeId; 55 | char* JobId; 56 | char* ConfigurationEndpoint; 57 | char* CompletionEndpoint; 58 | char* Cron; 59 | char* Schedule; 60 | int Priority; 61 | }; 62 | 63 | struct SessionRegisterResp 64 | { 65 | struct AgentApiResult Result; 66 | struct 67 | { 68 | char* Token; 69 | char* AgentId; 70 | int HeartbeatInterval; 71 | struct SessionJob** Jobs; 72 | int Jobs_count; 73 | struct ClientParameter** ClientParameters; 74 | int ClientParameters_count; 75 | char* ClientMachine; 76 | char* Certificate; 77 | } Session; 78 | }; 79 | 80 | #if defined(__INFINITE_AGENT__) 81 | struct SessionHeartbeatReq 82 | { 83 | char* SessionToken; 84 | char* TenantId; 85 | char* ClientMachine; 86 | enum AgentPlatform AgentPlatform; 87 | }; 88 | 89 | struct SessionHeartbeatResp 90 | { 91 | struct AgentApiResult Result; 92 | int HeartbeatInterval; 93 | bool SessionValid; 94 | }; 95 | #endif /* Infinite Agent */ 96 | 97 | struct CommonConfigReq 98 | { 99 | char* SessionToken; 100 | char* JobId; 101 | }; 102 | 103 | struct CommonCompleteReq 104 | { 105 | char* SessionToken; 106 | char* JobId; 107 | enum JobCompleteStatus Status; 108 | uint64_t AuditId; 109 | char* Message; 110 | }; 111 | 112 | struct CommonCompleteResp 113 | { 114 | struct AgentApiResult Result; 115 | }; 116 | 117 | struct ManagementConfigResp 118 | { 119 | uint64_t AuditId; 120 | bool JobCancelled; 121 | struct AgentApiResult Result; 122 | struct 123 | { 124 | enum OperationType OperationType; 125 | char* ClientMachine; 126 | char* StorePath; 127 | char* StorePassword; 128 | int StoreType; 129 | int Category; 130 | char* Alias; 131 | bool PrivateKeyEntry; 132 | char* EntryPassword; 133 | char* Thumbprint; 134 | char* EntryContents; 135 | char* PfxPassword; 136 | bool Overwrite; 137 | char* PrivateKeyPath; 138 | } Job; 139 | }; 140 | 141 | struct ManagementCompleteResp 142 | { 143 | struct AgentApiResult Result; 144 | char* InventoryJob; 145 | }; 146 | 147 | struct InventoryCurrentItem 148 | { 149 | char* Alias; 150 | bool PrivateKeyEntry; 151 | char** Thumbprints; 152 | int Thumbprints_count; 153 | }; 154 | 155 | struct InventoryConfigResp 156 | { 157 | char* InventoryEndpoint; 158 | uint64_t AuditId; 159 | bool JobCancelled; 160 | struct AgentApiResult Result; 161 | struct 162 | { 163 | char* ClientMachine; 164 | char* StorePath; 165 | char* StorePassword; 166 | int Category; 167 | struct InventoryCurrentItem** Inventory; 168 | int Inventory_count; 169 | } Job; 170 | }; 171 | 172 | struct InventoryUpdateItem 173 | { 174 | char* Alias; 175 | bool PrivateKeyEntry; 176 | int ItemStatus; 177 | bool UseChainLevel; 178 | char** Certificates; 179 | int Certificates_count; 180 | }; 181 | 182 | struct InventoryUpdateList 183 | { 184 | int count; 185 | struct InventoryUpdateItem** items; 186 | }; 187 | 188 | struct InventoryUpdateReq 189 | { 190 | char* SessionToken; 191 | char* JobId; 192 | struct InventoryUpdateList Inventory; 193 | }; 194 | 195 | struct InventoryUpdateResp 196 | { 197 | struct AgentApiResult Result; 198 | }; 199 | 200 | struct EnrollmentConfigResp 201 | { 202 | uint64_t AuditId; 203 | bool JobCancelled; 204 | struct AgentApiResult Result; 205 | char* Entropy; 206 | int KeySize; 207 | char* KeyType; 208 | char* Subject; 209 | char* ClientMachine; 210 | char* StorePath; 211 | char* StorePassword; 212 | char* EnrollEndpoint; 213 | char* PrivateKeyPath; 214 | char* Properties; 215 | }; 216 | 217 | struct EnrollmentEnrollReq 218 | { 219 | char* SessionToken; 220 | char* JobId; 221 | char* CSRText; 222 | }; 223 | 224 | struct EnrollmentEnrollResp 225 | { 226 | struct AgentApiResult Result; 227 | char* Certificate; 228 | }; 229 | 230 | struct EnrollmentCompleteResp 231 | { 232 | struct AgentApiResult Result; 233 | char* InventoryJob; 234 | }; 235 | 236 | struct FetchLogsConfigResp 237 | { 238 | int64_t AuditId; 239 | bool JobCancelled; 240 | struct AgentApiResult Result; 241 | int32_t MaxCharactersToReturn; 242 | }; 243 | 244 | struct FetchLogsCompleteReq 245 | { 246 | char* Log; 247 | 248 | char* SessionToken; 249 | char* JobId; 250 | enum JobCompleteStatus Status; 251 | uint64_t AuditId; 252 | char* Message; 253 | }; 254 | 255 | bool AgentApiResult_log(struct AgentApiResult result, char** pMessage, \ 256 | enum AgentApiResultStatus* pStatus); 257 | 258 | struct SessionRegisterReq* SessionRegisterReq_new(char* clientParamPath); 259 | 260 | void SessionRegisterReq_free(struct SessionRegisterReq* req); 261 | 262 | char* SessionRegisterReq_toJson(struct SessionRegisterReq* req); 263 | 264 | void SessionRegisterResp_free(struct SessionRegisterResp* resp); 265 | 266 | struct SessionRegisterResp* SessionRegisterResp_fromJson(char* jsonString); 267 | 268 | void SessionRegisterResp_freeJobs(struct SessionRegisterResp*); 269 | 270 | void SessionJob_free(struct SessionJob* job); 271 | 272 | #if defined(__INFINITE_AGENT__) 273 | struct SessionHeartbeatReq* SessionHeartbeatReq_new(); 274 | 275 | void SessionHeartbeatReq_free(struct SessionHeartbeatReq* req); 276 | 277 | char* SessionHeartbeatReq_toJson(struct SessionHeartbeatReq* req); 278 | 279 | void SessionHeartbeatResp_free(struct SessionHeartbeatResp* resp); 280 | 281 | struct SessionHeartbeatResp* SessionHeartbeatResp_fromJson(char* jsonString); 282 | #endif /* Infinite Agent */ 283 | 284 | struct CommonConfigReq* CommonConfigReq_new(); 285 | 286 | void CommonConfigReq_free(struct CommonConfigReq* req); 287 | 288 | char* CommonConfigReq_toJson(struct CommonConfigReq* req); 289 | 290 | struct CommonCompleteReq* CommonCompleteReq_new(); 291 | 292 | void CommonCompleteReq_free(struct CommonCompleteReq* req); 293 | 294 | char* CommonCompleteReq_toJson(struct CommonCompleteReq* req); 295 | 296 | void CommonCompleteResp_free(struct CommonCompleteResp* resp); 297 | 298 | struct CommonCompleteResp* CommonCompleteResp_fromJson(char* jsonString); 299 | 300 | void ManagementConfigResp_free(struct ManagementConfigResp* resp); 301 | 302 | struct ManagementConfigResp* ManagementConfigResp_fromJson(char* jsonString); 303 | 304 | void ManagementCompleteResp_free(struct ManagementCompleteResp* resp); 305 | 306 | struct ManagementCompleteResp* ManagementCompleteResp_fromJson(char* jsonString); 307 | 308 | void InventoryConfigResp_free(struct InventoryConfigResp* resp); 309 | 310 | struct InventoryConfigResp* InventoryConfigResp_fromJson(char* jsonString); 311 | 312 | void InventoryUpdateReq_free(struct InventoryUpdateReq* req); 313 | 314 | char* InventoryUpdateReq_toJson(struct InventoryUpdateReq* req); 315 | 316 | void InventoryUpdateResp_free(struct InventoryUpdateResp* resp); 317 | 318 | struct InventoryUpdateResp* InventoryUpdateResp_fromJson(char* jsonString); 319 | 320 | void EnrollmentConfigResp_free(struct EnrollmentConfigResp* resp); 321 | 322 | struct EnrollmentConfigResp* EnrollmentConfigResp_fromJson(char* jsonString); 323 | 324 | void EnrollmentEnrollReq_free(struct EnrollmentEnrollReq* req); 325 | 326 | char* EnrollmentEnrollReq_toJson(struct EnrollmentEnrollReq* req); 327 | 328 | void EnrollmentEnrollResp_free(struct EnrollmentEnrollResp* resp); 329 | 330 | struct EnrollmentEnrollResp* EnrollmentEnrollResp_fromJson(char* jsonString); 331 | 332 | void EnrollmentCompleteResp_free(struct EnrollmentCompleteResp* resp); 333 | 334 | struct EnrollmentCompleteResp* EnrollmentCompleteResp_fromJson(char* jsonString); 335 | 336 | void FetchLogsConfigResp_free(struct FetchLogsConfigResp* req); 337 | 338 | struct FetchLogsConfigResp* FetchLogsConfigResp_fromJson(char* jsonString); 339 | 340 | void FetchLogsCompleteReq_free(struct FetchLogsCompleteReq* req); 341 | 342 | char* FetchLogsCompleteReq_toJson(struct FetchLogsCompleteReq* req); 343 | 344 | struct FetchLogsCompleteReq* FetchLogsCompleteReq_new(); 345 | 346 | bool SessionRegisterReq_addNewClientParameter(struct SessionRegisterReq* req, \ 347 | const char* key, const char* value); 348 | 349 | #endif 350 | /******************************************************************************/ 351 | /******************************* END OF FILE **********************************/ 352 | /******************************************************************************/ -------------------------------------------------------------------------------- /lib/base64.c: -------------------------------------------------------------------------------- 1 | 2 | // NSData+Base64.m 3 | // base64 4 | // 5 | // Created by Matt Gallagher on 2009/06/03. 6 | // Copyright 2009 Matt Gallagher. All rights reserved. 7 | // 8 | // This software is provided 'as-is', without any express or implied 9 | // warranty. In no event will the authors be held liable for any damages 10 | // arising from the use of this software. Permission is granted to anyone to 11 | // use this software for any purpose, including commercial applications, and to 12 | // alter it and redistribute it freely, subject to the following restrictions: 13 | // 14 | // 1. The origin of this software must not be misrepresented; you must not 15 | // claim that you wrote the original software. If you use this software 16 | // in a product, an acknowledgment in the product documentation would be 17 | // appreciated but is not required. 18 | // 2. Altered source versions must be plainly marked as such, and must not be 19 | // misrepresented as being the original software. 20 | // 3. This notice may not be removed or altered from any source 21 | // distribution. 22 | // 23 | 24 | // 2017-04-11 Jon Proch - Removed Objective-C portion, adjusted name casing 25 | // 2017-05-04 Jon Proch - Changed manner in which j is incremented, as the previous implementation 26 | // resulted in incorrect output length in the case that the input string required no padding '=' 27 | // and had non-base64 chars (e.g. \n) at the end 28 | 29 | #include "base64.h" 30 | #include 31 | #include 32 | 33 | // 34 | // Mapping from 6 bit pattern to ASCII character. 35 | // 36 | static unsigned char base64EncodeLookup[65] = 37 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 38 | 39 | // 40 | // Definition for "masked-out" areas of the base64DecodeLookup mapping 41 | // 42 | #define xx 65 43 | 44 | // 45 | // Mapping from ASCII character to 6 bit pattern. 46 | // 47 | static unsigned char base64DecodeLookup[256] = 48 | { 49 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 50 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 51 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, 52 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, 53 | xx, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 54 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, 55 | xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 56 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, 57 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 58 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 59 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 60 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 61 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 63 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 64 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 65 | }; 66 | 67 | // 68 | // Fundamental sizes of the binary and base64 encode/decode units in bytes 69 | // 70 | #define BINARY_UNIT_SIZE 3 71 | #define BASE64_UNIT_SIZE 4 72 | 73 | // 74 | // NewBase64Decode 75 | // 76 | // Decodes the base64 ASCII string in the inputBuffer to a newly malloced 77 | // output buffer. 78 | // 79 | // inputBuffer - the source ASCII string for the decode 80 | // length - the length of the string or -1 (to specify strlen should be used) 81 | // outputLength - if not-NULL, on output will contain the decoded length 82 | // 83 | // returns the decoded buffer. Must be free'd by caller. Length is given by 84 | // outputLength. 85 | // 86 | void* base64_decode( 87 | const char *inputBuffer, 88 | size_t length, 89 | size_t *outputLength) 90 | { 91 | if ((int)length == -1) 92 | { 93 | length = strlen(inputBuffer); 94 | } 95 | 96 | size_t outputBufferSize = 97 | ((length+BASE64_UNIT_SIZE-1) / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE; 98 | unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize); 99 | 100 | size_t i = 0; 101 | size_t j = 0; 102 | while (i < length) 103 | { 104 | // 105 | // Accumulate 4 valid characters (ignore everything else) 106 | // 107 | unsigned char accumulated[BASE64_UNIT_SIZE]; 108 | size_t accumulateIndex = 0; 109 | while (i < length) 110 | { 111 | unsigned char decode = base64DecodeLookup[(unsigned int)inputBuffer[i++]]; // ### RAL Added explicit type cast 112 | if (decode != xx) 113 | { 114 | accumulated[accumulateIndex] = decode; 115 | accumulateIndex++; 116 | 117 | if (accumulateIndex == BASE64_UNIT_SIZE) 118 | { 119 | break; 120 | } 121 | } 122 | } 123 | 124 | // 125 | // Store the 6 bits from each of the 4 characters as 3 bytes 126 | // 127 | // (Uses improved bounds checking suggested by Alexandre Colucci) 128 | // 129 | if(accumulateIndex >= 2) 130 | outputBuffer[j++] = (accumulated[0] << 2) | (accumulated[1] >> 4); 131 | if(accumulateIndex >= 3) 132 | outputBuffer[j++] = (accumulated[1] << 4) | (accumulated[2] >> 2); 133 | if(accumulateIndex >= 4) 134 | outputBuffer[j++] = (accumulated[2] << 6) | accumulated[3]; 135 | } 136 | 137 | if (outputLength) 138 | { 139 | *outputLength = j; 140 | } 141 | return outputBuffer; 142 | } 143 | 144 | // 145 | // NewBase64Encode 146 | // 147 | // Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced 148 | // output buffer. 149 | // 150 | // inputBuffer - the source data for the encode 151 | // length - the length of the input in bytes 152 | // separateLines - if zero, no CR/LF characters will be added. Otherwise 153 | // a CR/LF pair will be added every 64 encoded chars. 154 | // outputLength - if not-NULL, on output will contain the encoded length 155 | // (not including terminating 0 char) 156 | // 157 | // returns the encoded buffer. Must be free'd by caller. Length is given by 158 | // outputLength. 159 | // 160 | char* base64_encode( 161 | const void *buffer, 162 | size_t length, 163 | bool separateLines, 164 | size_t *outputLength) 165 | { 166 | const unsigned char *inputBuffer = (const unsigned char *)buffer; 167 | 168 | #define MAX_NUM_PADDING_CHARS 2 169 | #define OUTPUT_LINE_LENGTH 64 170 | #define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE) 171 | #define CR_LF_SIZE 2 172 | 173 | // 174 | // Byte accurate calculation of final buffer size 175 | // 176 | size_t outputBufferSize = 177 | ((length / BINARY_UNIT_SIZE) 178 | + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) 179 | * BASE64_UNIT_SIZE; 180 | if (separateLines) 181 | { 182 | outputBufferSize += 183 | (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE; 184 | } 185 | 186 | // 187 | // Include space for a terminating zero 188 | // 189 | outputBufferSize += 1; 190 | 191 | // 192 | // Allocate the output buffer 193 | // 194 | char *outputBuffer = (char *)malloc(outputBufferSize); 195 | if (!outputBuffer) 196 | { 197 | return NULL; 198 | } 199 | 200 | size_t i = 0; 201 | size_t j = 0; 202 | const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length; 203 | size_t lineEnd = lineLength; 204 | 205 | while (true) 206 | { 207 | if (lineEnd > length) 208 | { 209 | lineEnd = length; 210 | } 211 | 212 | for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) 213 | { 214 | // 215 | // Inner loop: turn 48 bytes into 64 base64 characters 216 | // 217 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 218 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) 219 | | ((inputBuffer[i + 1] & 0xF0) >> 4)]; 220 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2) 221 | | ((inputBuffer[i + 2] & 0xC0) >> 6)]; 222 | outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F]; 223 | } 224 | 225 | if (lineEnd == length) 226 | { 227 | break; 228 | } 229 | 230 | // 231 | // Add the newline 232 | // 233 | outputBuffer[j++] = '\r'; 234 | outputBuffer[j++] = '\n'; 235 | lineEnd += lineLength; 236 | } 237 | 238 | if (i + 1 < length) 239 | { 240 | // 241 | // Handle the single '=' case 242 | // 243 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 244 | outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4) 245 | | ((inputBuffer[i + 1] & 0xF0) >> 4)]; 246 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2]; 247 | outputBuffer[j++] = '='; 248 | } 249 | else if (i < length) 250 | { 251 | // 252 | // Handle the double '=' case 253 | // 254 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2]; 255 | outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4]; 256 | outputBuffer[j++] = '='; 257 | outputBuffer[j++] = '='; 258 | } 259 | outputBuffer[j] = 0; 260 | 261 | // 262 | // Set the output length and return the buffer 263 | // 264 | if (outputLength) 265 | { 266 | *outputLength = j; 267 | } 268 | return outputBuffer; 269 | } 270 | 271 | -------------------------------------------------------------------------------- /fetchlogs.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include 14 | #include 15 | #include 16 | #include "fetchlogs.h" 17 | #include "logging.h" 18 | #include "httpclient.h" 19 | 20 | /******************************************************************************/ 21 | /***************************** LOCAL DEFINES *********************************/ 22 | /******************************************************************************/ 23 | 24 | /******************************************************************************/ 25 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 26 | /******************************************************************************/ 27 | 28 | /******************************************************************************/ 29 | /************************** LOCAL GLOBAL VARIABLES ****************************/ 30 | /******************************************************************************/ 31 | 32 | /******************************************************************************/ 33 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 34 | /******************************************************************************/ 35 | static int get_fetchlogs_config(const char* sessionToken, const char* jobId, 36 | const char* endpoint, struct FetchLogsConfigResp** pConf) 37 | { 38 | char* url = NULL; 39 | 40 | log_verbose("%s::%s(%d) : Sending config request: %s", LOG_INF, jobId); 41 | struct CommonConfigReq* req = NULL; 42 | req = CommonConfigReq_new(); 43 | if (!req) { 44 | log_error("%s::%s(%d) : Error creating Common Config Request", LOG_INF); 45 | return 999; 46 | } 47 | 48 | req->JobId = strdup(jobId); 49 | req->SessionToken = strdup(sessionToken); 50 | char* jsonReq = CommonConfigReq_toJson(req); 51 | char* jsonResp = NULL; 52 | url = config_build_url(endpoint, true); 53 | 54 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 55 | ConfigData->TrustStore, ConfigData->AgentCert, 56 | ConfigData->AgentKey, ConfigData->AgentKeyPassword, 57 | jsonReq, &jsonResp, ConfigData->httpRetries, 58 | ConfigData->retryInterval); 59 | 60 | if(res == 0) { 61 | *pConf = FetchLogsConfigResp_fromJson(jsonResp); 62 | } else { 63 | log_error("%s::%s(%d) : Config retrieval failed with error code %d", LOG_INF, res); 64 | } 65 | 66 | if (jsonReq) free(jsonReq); 67 | if (jsonResp) free(jsonResp); 68 | if (req) CommonConfigReq_free(req); 69 | if (url) free(url); 70 | 71 | return res; 72 | } 73 | 74 | static int get_logs(char* logFilePath, int maxCharactersToRead, char** log) 75 | { 76 | FILE* pLog; 77 | char* logContent = NULL; 78 | 79 | if((pLog = fopen(logFilePath, "r")) == NULL) { 80 | log_error("%s::%s(%d) : Failed to open log file.", LOG_INF); 81 | return 1; 82 | } 83 | 84 | if(fseek(pLog, 0, SEEK_END) != 0) { 85 | log_error("%s::%s(%d) : End of file not found.", LOG_INF); 86 | goto fail; 87 | } 88 | 89 | long int fileSize = ftell(pLog); 90 | if(fseek(pLog, -1 * maxCharactersToRead, SEEK_END) != 0) { 91 | rewind(pLog); 92 | } 93 | 94 | if(fileSize - ftell(pLog) == maxCharactersToRead) { 95 | while(true) { 96 | if ((char)fgetc(pLog) == '\n') { 97 | (void)fseek(pLog, 1, SEEK_CUR); 98 | break; 99 | } else if(fgetc(pLog) == EOF) { 100 | if(fseek(pLog, -1 * maxCharactersToRead, SEEK_END) != 0) { 101 | (void)rewind(pLog); 102 | } 103 | break; 104 | } else { 105 | (void)fseek(pLog, 1, SEEK_CUR); 106 | } 107 | } 108 | } 109 | 110 | logContent = calloc((size_t)maxCharactersToRead, sizeof(*logContent)); 111 | if (!logContent) { 112 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 113 | goto fail; 114 | } 115 | size_t readCount = fread(logContent, sizeof(char),(size_t)maxCharactersToRead, pLog); 116 | if ((readCount < (size_t)maxCharactersToRead) && (0 != ferror(pLog))) { 117 | 118 | } 119 | 120 | *log = logContent; 121 | if (pLog) (void)fclose(pLog); 122 | return 0; 123 | 124 | fail: 125 | if (pLog) (void)fclose(pLog); 126 | return 1; 127 | } 128 | 129 | static int send_fetchlogs_job_complete(const char* sessionToken, 130 | const char* jobId, const char* endpoint, int jobStatus, long auditId, 131 | const char* message, const char* log, struct CommonCompleteResp** pComp) 132 | { 133 | char* url = NULL; 134 | 135 | log_verbose("%s::%s(%d) : Sending complete request: %ld for session: %s", LOG_INF, auditId, sessionToken); 136 | struct FetchLogsCompleteReq* req = FetchLogsCompleteReq_new(); 137 | if (!req) { 138 | log_error("%s::%s(%d) : Error creating Fetch Logs Complete Request Structure", LOG_INF); 139 | return 999; 140 | } 141 | 142 | if (sessionToken) { 143 | req->SessionToken = strdup(sessionToken); 144 | } else { 145 | req->SessionToken = strdup("Error no session token"); 146 | } 147 | 148 | if (jobId) { 149 | req->JobId = strdup(jobId); 150 | } else { 151 | req->JobId = strdup("Error no JobId"); 152 | } 153 | 154 | req->Status = jobStatus; 155 | req->AuditId = auditId; 156 | if (message) { 157 | req->Message = strdup(message); 158 | } else { 159 | req->Message = strdup(""); 160 | } 161 | 162 | if (log) { 163 | req->Log = strdup(log); 164 | } else { 165 | req->Log = strdup("Log retrieval error!"); 166 | } 167 | 168 | 169 | char* jsonReq = FetchLogsCompleteReq_toJson(req); 170 | char* jsonResp = NULL; 171 | 172 | url = config_build_url(endpoint, true); 173 | 174 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 175 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 176 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp, 177 | ConfigData->httpRetries, ConfigData->retryInterval); 178 | 179 | if(res == 0) { 180 | *pComp = CommonCompleteResp_fromJson(jsonResp); 181 | } else { 182 | log_error("%s::%s(%d) : Job completion failed with error code %d", LOG_INF, res); 183 | } 184 | 185 | if (jsonReq) free(jsonReq); 186 | if (jsonResp) free(jsonResp); 187 | if (url) free(url); 188 | if (req) FetchLogsCompleteReq_free(req); 189 | return res; 190 | } 191 | 192 | /******************************************************************************/ 193 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 194 | /******************************************************************************/ 195 | int cms_job_fetchLogs(struct SessionJob* jobInfo, char* sessionToken) 196 | { 197 | int res = 0; 198 | int returnable = 0; 199 | struct FetchLogsConfigResp* fetchLogsConf = NULL; 200 | char* statusMessage = strdup(""); 201 | enum AgentApiResultStatus status = STAT_UNK; 202 | 203 | log_info("%s::%s(%d) : Starting fetch logs job %s", 204 | LOG_INF, jobInfo->JobId); 205 | 206 | res = get_fetchlogs_config(sessionToken, jobInfo->JobId, 207 | jobInfo->ConfigurationEndpoint, &fetchLogsConf); 208 | 209 | if(res == 0 && fetchLogsConf && AgentApiResult_log(fetchLogsConf->Result, 210 | &statusMessage, &status)) 211 | { 212 | if(fetchLogsConf->JobCancelled) 213 | { 214 | returnable = 1; 215 | log_info("%s::%s(%d) : Job has been cancelled and will not be run", 216 | LOG_INF); 217 | } 218 | else 219 | { 220 | log_verbose("%s::%s(%d) : Audit Id: %ld", LOG_INF, 221 | fetchLogsConf->AuditId); 222 | char* log = NULL; 223 | struct CommonCompleteResp* compResponse = NULL; 224 | 225 | /* pull the last 4000 characters from the log file. */ 226 | /* (trim beginning to the first \n if necessary). */ 227 | status = STAT_SUCCESS; 228 | if( 0 != get_logs(ConfigData->LogFile, 229 | fetchLogsConf->MaxCharactersToReturn, &log) ) 230 | { 231 | status = STAT_ERR; 232 | } 233 | 234 | /* Complete job. */ 235 | res = send_fetchlogs_job_complete(sessionToken, jobInfo->JobId, 236 | jobInfo->CompletionEndpoint, (status+1), 237 | fetchLogsConf->AuditId, statusMessage, log, &compResponse); 238 | 239 | log_verbose("%s::%s : %s", __FILE__, __FUNCTION__, log); 240 | free(log); 241 | CommonCompleteResp_free(compResponse); 242 | } 243 | } 244 | 245 | /* free memory */ 246 | FetchLogsConfigResp_free(fetchLogsConf); 247 | free(statusMessage); 248 | 249 | return returnable; 250 | } 251 | /******************************************************************************/ 252 | /******************************* END OF FILE **********************************/ 253 | /******************************************************************************/ -------------------------------------------------------------------------------- /README-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C-Agent 2 | ![Build Status](https://img.shields.io/badge/build-passing-brightgreen) ![License](https://img.shields.io/badge/license-Apache%20License%202.0-blue) 3 | 4 | The C-Agent is a reference implementation of a Keyfactor Remote Agent geared toward use in IoT based solutions. 5 | The Keyfactor-CAgent can be built for three (3) different modes: 6 | - **openSSL** 7 | - **wolfSSL** 8 | - **Raspberry Pi SPI TPM** (Coming Soon) 9 | --- 10 | ## Table of Contents 11 | 1. [Overview](#overview) 12 | 2. [OpenSSL Build](#openssl-build) 13 | - [Install Dependencies](#install-dependencies) 14 | - [Clone the Repository](#clone-the-repository) 15 | - [Build the Agent](#build-the-agent) 16 | 3. [WolfSSL Build](#wolfssl-build) 17 | - [Install Dependencies](#install-dependencies-1) 18 | - [Build WolfSSL](#build-wolfssl) 19 | - [Build cURL](#build-curl) 20 | - [Build the Agent](#build-the-agent-1) 21 | 4. [TPM Build (Coming Soon)](#tpm-build-coming-soon) 22 | 5. [Common Configuration Steps](#common-configuration-steps) 23 | - [Set Up Directories and Trust Store](#set-up-directories-and-trust-store) 24 | - [Modify Configuration File](#modify-configuration-file) 25 | - [Run the Agent](#run-the-agent) 26 | 6. [Appendix](#appendix) 27 | - [Agent Switches](#agent-switches) 28 | - [Complete Configuration File Data](#complete-configuration-file-data) 29 | 7. [License](#license) 30 | 8. [Contributing](#contributing) 31 | --- 32 | ## OpenSSL build 33 | ## Install the __dependencies__ depending on your Linux distribution: 34 | 35 | #### Debian based (e.g., Poky, Ubuntu, Raspian, Raspberry Pi OS, etc.) 36 | sudo apt update 37 | sudo apt install -y build-essential git libcurl4-gnutls-dev curl libssl-dev 38 | 39 | #### RHEL based (RHEL, CentOS, Rocky, etc.) 40 | sudo dnf update 41 | sudo dnf install -y "Development Tools" 42 | sudo dnf install -y git curl-devel curl openssl-devel 43 | 44 | ## Clone the git repository 45 | cd ~ 46 | git clone https://github.com/Keyfactor/Keyfactor-CAgent 47 | 48 | ## Build the agent against the OpenSSL target (64-bit OSes - not Raspberry Pi) 49 | cd ~/Keyfactor-CAgent 50 | make clean 51 | make opentest -j$(nproc) 52 | 53 | ## Build the agent against the openSSL target for 32-bit OSes like RaspOS 54 | cd ~/Keyfactor-CAgent 55 | make clean 56 | make openpi -j$(nproc) 57 | 58 | ## Configure and run the Agent (see below) 59 | 60 | --- 61 | 62 | ## WolfSSL Build 63 | 64 | ### Install Dependencies 65 | 66 | #### Debian-based Distributions 67 | ```bash 68 | sudo apt update 69 | sudo apt install -y build-essential git automake autoconf libtool pkg-config wget 70 | ``` 71 | 72 | #### RHEL-based Distributions 73 | ```bash 74 | sudo dnf update 75 | sudo dnf groupinstall -y "Development Tools" 76 | sudo dnf install -y build-essential git automake autoconf libtool pkg-config wget 77 | ``` 78 | 79 | ### Build WolfSSL 80 | ```bash 81 | cd ~ 82 | wget https://github.com/wolfSSL/wolfssl/archive/v5.0.0-stable.tar.gz 83 | tar -xzf v5.0.0-stable.tar.gz 84 | cd wolfssl-5.0.0-stable 85 | ./autogen.sh 86 | ./configure --enable-tls13 --enable-all 87 | make 88 | sudo make install 89 | sudo ldconfig -v | grep libwolfssl 90 | ``` 91 | 92 | Ensure the output includes: 93 | ``` 94 | libwolfssl.so.30 -> libwolfssl.so.30.0.0 95 | ``` 96 | 97 | ### Build cURL with WolfSSL 98 | ```bash 99 | cd ~ 100 | wget https://github.com/curl/curl/archive/refs/tags/curl-7_81_0.tar.gz 101 | tar -xvf curl-7_81_0.tar.gz 102 | cd curl-curl-7_81_0/ 103 | autoreconf -fi 104 | ./configure --with-wolfssl 105 | make -j$(nproc) 106 | sudo make install 107 | sudo ldconfig 108 | ``` 109 | 110 | ### Build the Agent 111 | 112 | #### 64-bit OSes 113 | ```bash 114 | cd ~/Keyfactor-CAgent 115 | make clean 116 | make wolftest -j$(nproc) 117 | ``` 118 | 119 | #### 32-bit OSes 120 | ```bash 121 | cd ~/Keyfactor-CAgent 122 | make clean 123 | make wolftest -j$(nproc) 124 | ``` 125 | 126 | --- 127 | 128 | ## TPM Build (Coming Soon) 129 | 130 | This feature is under development. Stay tuned for updates. 131 | 132 | --- 133 | ## Common Configuration Steps 134 | 135 | ### Set Up Directories and Trust Store 136 | ```bash 137 | sudo mkdir --parents /home/keyfactor/Keyfactor-CAgent/certs/ 138 | sudo chown $(whoami):$(whoami) /home/keyfactor/Keyfactor-CAgent/certs 139 | nano /home/keyfactor/Keyfactor-CAgent/certs/trust.store 140 | ``` 141 | Copy the PEM-formatted certificate into the `trust.store` file. 142 | 143 | ### Modify Configuration File 144 | ```bash 145 | cd ~/Keyfactor-CAgent 146 | nano config.json 147 | ``` 148 | #### Add these data lines into the file, replacing the Hostname, Username, and Password entries with the relevant data from your Marketplace instance. 149 | #### Also replace the Agent name and CSR Subject with a unique name to your Marketplace instance. 150 | #### Also note this file is case senstivive! 151 | { 152 | "AgentId": "", 153 | "AgentName": "UniqueName", 154 | "Hostname": "www.yourtestdrive.com", 155 | "Username": "testdrive\\yourusername", 156 | "Password": "yourpassword", 157 | "VirtualDirectory": "KeyfactorAgents", 158 | "TrustStore": "/home/keyfactor/Keyfactor-CAgent/certs/trust.store", 159 | "AgentCert": "/home/keyfactor/Keyfactor-CAgent/certs/Agent-cert.pem", 160 | "AgentKey": "/home/keyfactor/Keyfactor-CAgent/certs/Agent-key.pem", 161 | "CSRKeyType": "ECC", 162 | "CSRKeySize": 256, 163 | "CSRSubject": "CN=UniqueName", 164 | "EnrollOnStartup": true, 165 | "UseSsl": true, 166 | "LogFile": "agent.log" 167 | } 168 | 169 | #### The Agent uses a config.json file to provide inputs into the system. 170 | #### For this reference example, we use unencrypted passwords, usernames, and the like. 171 | #### In a full implementation, this data is either secured in trusted element space and/or encrypted and stored as a blob. 172 | #### To allow easier exploration, this is not done here & is implemented depending on physical hardware and business requirements. 173 | 174 | --- 175 | 176 | ### Run the Agent 177 | ```bash 178 | cd ~/Keyfactor-CAgent 179 | ./agent -l t 180 | ``` 181 | 182 | --- 183 | 184 | # 185 | # APPENDIX A: Agent switches 186 | 187 | ./agent -l enables logging (default is info) 188 | ./agent -l t is the greatest detail mode and includes traced curl output 189 | ./agent -l d lists all message details other than trace details 190 | ./agent -l v lists all verbose and below messages 191 | ./agent -l i lists all info, warn, and error messages 192 | ./agent -l w lists all warning and error messages 193 | ./agent -l e lists only error messages 194 | ./agent -l o turns off all output messages 195 | 196 | ./agent -c overrides the default configuration file 197 | 198 | ./agent -h overrides the agent name with $HOSTNAME_$DATETIME 199 | 200 | ./agent -a adds the client certificate presented for mTLS into a header field named X-ARR-ClientCert 201 | Use this (along with client certificate authentication) in Keyfactor if the platform 202 | is configured to look for this certificate header. 203 | 204 | ./agent -e the crypto engine to use (e.g., tpm2tss) NOTE: 205 | must compile the TPM version of the agent 206 | 207 | # APPENDIX B: Complete Configuration file data 208 | __AgentID__ : Assigned by Keyfactor Control. Please leave blank. 209 | 210 | __AgentName__ : Leave blank if using the `-h` argument with the agent. This is if not using the `-h` switch. 211 | 212 | __ClientParameterPath__ : Used to pass optional client parameters to the Registration session. 213 | 214 | __Hostname__ : Either the IP address or the FQDN of the Keyfactor Control Web Address 215 | 216 | __Password__ : If using basic authentication to the Keyfactor Control Platform, then the password for the user. This can also be omitted if a reverse proxy is injecting authentication credentials into the HTTP header. 217 | 218 | __Username__ : If using basic authentication to the Keyfactor Control Platform, then the domain and username to log into the Keyfactor Control Platform (remember, the \ character must be escaped -- e.g., KEYFACTOR\\Administrator). This can also be omitted if a reverse proxy is injecting authentication credentials into the HTTP header. 219 | 220 | __VirtualDirectory__ : Set this to KeyfactorAgents if you are not using a reverse proxy. If you are using a reverse-proxy, set it to the virtual directory that is mapped to KeyfactorAgents. 221 | 222 | __TrustStore__ : The location of additional certificates that are trusted by the Agent. This list is appended to the standard CA certificate store located in `/etc/ssl/certs/ca-certificates.crt` for Ubuntu. 223 | 224 | __UseAgentCert__ : Defaults to true if not supplied. True = use an agent managed cert for mTLS. False = only use 1-way TLS for the agent. */ 225 | 226 | __AgentCert__ : The (eventual) location of the Agent's certificate. This is the certificate used by the Agent to call into the platform. 227 | 228 | __AgentKey__ : The (eventual) location of the Agent's private key. This is the key used by the Agent to call into the platform. 229 | 230 | __AgentKeyPassword__ : An optional passphrase for decoding the Agent Key. Note, if a TPM, Secure Element, or secure area is used, this **must be defined**. 231 | 232 | __CSRKeyType__ : The Key type for the AgentKey (ECC or RSA). This must match the template defined in the Keyfactor Platform. 233 | 234 | __CSRKeySize__ : The Key size for the AgentKey. This must be equal to or greater than the minimum size defined in the template. 235 | 236 | __CSRSubject__ : If the `-h` command line switch is used, this field is not used. This field is if the command line switch is not used. 237 | 238 | __EnrollOnStartup__ : true = The agent will register itself with the platform. The agent will set this to false once the agent has registered and been approved. 239 | 240 | __UseBootstrapCert__ : true = Use a bootstrap certificate when registering with the platform. false = otherwise. 241 | 242 | __BootstrapCert__ : If UseBootstrapCert is true, this is required & is the path/filename for the certificate. 243 | 244 | __BootstrapKey__ : If UseBootstrapCert is false, this is required & is the path/filename of the private key for the bootstrap certificate. 245 | 246 | __BootstrapKeyPassword__ : An optional passphrase used to decode the bootstrap key. 247 | 248 | __UseSsl__ : true = https:// is used. false = http:// is used. Both refer to the Keyfactor Control Platform communications. Really, this should always be true in a production environment. 249 | 250 | __Serialize__ : true = use a shared network file to grab a serial number/name combination for the AgentName and CN. false = otherwise.Typically this is an nfs file store that is on the production line. Most IoT implementations do not use this method, as UKIDs and names are defined by the host and UKID chips (e.g., [1-wire EEPROM with 64-bit UKID](https://www.microchip.com/wwwproducts/en/AT21CS01) ) 251 | 252 | __SerialFile__ : The location of a mounted nfs file store & file to use in the serialization operation. 253 | 254 | __LogFile__ : The path/filename of a log file for the agent. **NOTE:** This log file uses the same logging level as the agent. (See command line arguments for the agent) 255 | 256 | __httpRetries__ : The number of times the agent will attempt to connect to the Keyfactor Control platform before recording an error. Minimum value is 1. 257 | 258 | __retryInterval__ : The time delay (in seconds) between httpRetries. 259 | 260 | __LogFileIndex__ : This is used as an index into the LogFile to allow for a rolling maximum sized log file. 261 | 262 | #### __WARNING:__ if the Agent's LogFile is deleted, this **has** to be set to zero (0). 263 | 264 | ## Contributing 265 | 266 | Contributions are welcome! Submit issues or pull requests on [GitHub](https://github.com/Keyfactor/Keyfactor-CAgent). 267 | 268 | --- 269 | 270 | ## License 271 | 272 | This project is licensed under the Apache-2.0 License. See the `README-LICENSE.txt` file for details. 273 | -------------------------------------------------------------------------------- /enrollment.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include 14 | #include 15 | #include 16 | #include "lib/base64.h" 17 | #include "enrollment.h" 18 | #include "httpclient.h" 19 | #include "utils.h" 20 | #include "logging.h" 21 | #include "csr.h" 22 | #include "config.h" 23 | 24 | #if defined(__WOLF_SSL__) 25 | #include "wolfssl_wrapper/wolfssl_wrapper.h" 26 | #else 27 | #if defined(__OPEN_SSL__) 28 | #include "openssl_wrapper/openssl_wrapper.h" 29 | #else 30 | #if defined(__TPM__) 31 | #else 32 | #endif 33 | #endif 34 | #endif 35 | 36 | /******************************************************************************/ 37 | /*************************** GLOBAL VARIABLES *********************************/ 38 | /******************************************************************************/ 39 | 40 | /******************************************************************************/ 41 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 42 | /******************************************************************************/ 43 | 44 | /******************************************************************************/ 45 | /************************** LOCAL GLOBAL VARIABLES ****************************/ 46 | /******************************************************************************/ 47 | 48 | /******************************************************************************/ 49 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 50 | /******************************************************************************/ 51 | 52 | /** */ 53 | /* Ask the platform to provide the details for the reenrollment job. */ 54 | /* */ 55 | /* @param [Input] sessionToken The session GUID currently established between*/ 56 | /* the agent and the platform. */ 57 | /* @param [Input] jobId is the GUID for the reenrollment job */ 58 | /* @param [Input] endpoint is the relative URL to retrieve the config from */ 59 | /* @param [Output] pManConf is the enrollment job configuration response */ 60 | /* from the platform. */ 61 | /* @return http response */ 62 | /* */ 63 | static int get_enroll_config(const char* sessionToken, const char* jobId, 64 | const char* endpoint, struct EnrollmentConfigResp** pManConf) 65 | { 66 | char* url = NULL; 67 | 68 | log_verbose("%s::%s(%d) : Sending enrollment config request: %s", LOG_INF, jobId); 69 | struct CommonConfigReq* req = CommonConfigReq_new(); 70 | if (!req) { 71 | log_error("%s::%s(%d) : Error creating common config request structure", LOG_INF); 72 | return 999; 73 | } 74 | 75 | req->JobId = strdup(jobId); 76 | req->SessionToken = strdup(sessionToken); 77 | char* jsonReq = CommonConfigReq_toJson(req); 78 | char* jsonResp = NULL; 79 | url = config_build_url(endpoint, true); 80 | 81 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 82 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 83 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp 84 | ,ConfigData->httpRetries,ConfigData->retryInterval); 85 | 86 | if(res == 0) { 87 | *pManConf = EnrollmentConfigResp_fromJson(jsonResp); 88 | } 89 | else { 90 | log_error("%s::%s(%d) : Config retrieval failed with error code %d", 91 | LOG_INF, res); 92 | } 93 | 94 | if (jsonReq) free(jsonReq); 95 | if (jsonResp) free(jsonResp); 96 | if (url) free(url); 97 | if (req) CommonConfigReq_free(req); 98 | 99 | return res; 100 | } /* get_enroll_config */ 101 | 102 | /** */ 103 | /* Send the reenrollment data to the platform. This includes the CSR for */ 104 | /* the platform to sign. */ 105 | /* */ 106 | /* @param [Input] sessionToken The session GUID currently established between*/ 107 | /* the agent and the platform. */ 108 | /* @param [Input] jobId is the GUID for the reenrollment job */ 109 | /* @param [Input] Endpoint is the relative URL to send the reenrollment to */ 110 | /* @param [Input] csr is the naked PEM for the CSR */ 111 | /* @param [Output] pEnrResp is the response from the platform to the */ 112 | /* reenrollment data sent by the agent. */ 113 | /* @return http response */ 114 | /* */ 115 | static int send_enrollment(const char* sessionToken, const char* jobId, 116 | const char* endpoint, const char* csr, 117 | struct EnrollmentEnrollResp** pEnrResp) 118 | { 119 | char* url = NULL; 120 | 121 | log_verbose("%s::%s(%d) : Sending enrollment request: %s", LOG_INF, jobId); 122 | struct EnrollmentEnrollReq* enrReq = calloc(1, sizeof(*enrReq)); 123 | if (!enrReq) { 124 | log_error("%s::%s(%d) : Error creating enrollment request", LOG_INF); 125 | return 999; 126 | } 127 | 128 | enrReq->SessionToken = strdup(sessionToken); 129 | enrReq->JobId = strdup(jobId); 130 | enrReq->CSRText = strdup(csr); 131 | char* jsonReq = EnrollmentEnrollReq_toJson(enrReq); 132 | char* jsonResp = NULL; 133 | url = config_build_url(endpoint, true); 134 | 135 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 136 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 137 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp, 138 | ConfigData->httpRetries, ConfigData->retryInterval); 139 | 140 | if(res == 0) { 141 | *pEnrResp = EnrollmentEnrollResp_fromJson(jsonResp); 142 | } else { 143 | log_error("%s::%s(%d) : Enrollment failed with error code %d", LOG_INF, res); 144 | } 145 | 146 | if (jsonReq) free(jsonReq); 147 | if (jsonResp) free(jsonResp); 148 | if (url) free(url); 149 | if (enrReq) EnrollmentEnrollReq_free(enrReq); 150 | 151 | return res; 152 | } /* send_enrollment */ 153 | 154 | /** */ 155 | /* Tell the platform that the reenrollment job has completed. */ 156 | /* */ 157 | /* */ 158 | /* @param [Input] sessionToken The session GUID currently established between*/ 159 | /* the agent and the platform. */ 160 | /* @param [Input] jobId is the GUID for the reenrollment job */ 161 | /* @param [Input] Endpoint is the relative URL to send the complete to */ 162 | /* @param [Input] jobStatus is an enum JobCompleteStatus result */ 163 | /* @param [Input] auditId is the audit number from the platform */ 164 | /* @param [Input] message contains details info to send to the platform */ 165 | /* typically associated with errors encountered during the */ 166 | /* reenrollment job */ 167 | /* @param [Output] pEnrComp is the response from the platform to the */ 168 | /* reenrollment complete message sent by the agent. */ 169 | /* @return http response */ 170 | /* */ 171 | static int send_enroll_job_complete(const char* sessionToken, const char* jobId, 172 | const char* endpoint, int jobStatus, long auditId, const char* message, 173 | struct EnrollmentCompleteResp** pEnrComp) 174 | { 175 | char* url = NULL; 176 | 177 | log_verbose("%s::%s(%d) : Sending enrollment complete request: %ld for" 178 | " session: %s", LOG_INF, auditId, sessionToken); 179 | struct CommonCompleteReq* req = CommonCompleteReq_new(); 180 | if (!req) { 181 | log_error("%s::%s(%d) : Error creating common complete request structure", LOG_INF); 182 | return 999; 183 | } 184 | 185 | req->SessionToken = strdup(sessionToken); 186 | req->JobId = strdup(jobId); 187 | req->Status = jobStatus; 188 | req->AuditId = auditId; 189 | req->Message = strdup(message); 190 | char* jsonReq = CommonCompleteReq_toJson(req); 191 | char* jsonResp = NULL; 192 | url = config_build_url(endpoint, true); 193 | 194 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 195 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 196 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp, 197 | ConfigData->httpRetries,ConfigData->retryInterval); 198 | 199 | if(res == 0) { 200 | *pEnrComp = EnrollmentCompleteResp_fromJson(jsonResp); 201 | } else { 202 | log_error("%s::%s(%d) : Job completion failed with error code %d", LOG_INF, res); 203 | } 204 | 205 | if (jsonReq) free(jsonReq); 206 | if (jsonResp) free(jsonResp); 207 | if (url) free(url); 208 | if (req) CommonCompleteReq_free(req); 209 | 210 | return res; 211 | } /* send_enroll_job_complete */ 212 | 213 | /******************************************************************************/ 214 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 215 | /******************************************************************************/ 216 | 217 | /** */ 218 | /* The control flow for a reenrollment job. This flow consists of: */ 219 | /* 1.) Ask the platform for the job configuration details. */ 220 | /* 2.) Generate a keypair */ 221 | /* 3.) Create a CSR */ 222 | /* 4.) Send that information to the platform. */ 223 | /* 5.) If there is a cert that comes back: */ 224 | /* a.) Store the private key */ 225 | /* b.) Store the cert */ 226 | /* 6.) Tell the platform we have finished the job */ 227 | /* */ 228 | /* @param [Input] jobInfo is the basic job id and call-in endpoint */ 229 | /* @param [Input] sessionToken is the current session GUID */ 230 | /* @param [Output] the job to run next (provided by the platform) */ 231 | /* @return 0 if job is finished */ 232 | /* 1 if job is canceled */ 233 | /* */ 234 | int cms_job_enroll(struct SessionJob* jobInfo, char* sessionToken, 235 | char** chainJob) 236 | { 237 | int res = 0; 238 | int returnable = 0; 239 | struct EnrollmentConfigResp* enrConf = NULL; 240 | char* statusMessage = strdup(""); 241 | enum AgentApiResultStatus status = STAT_UNK; 242 | 243 | log_info("%s::%s(%d) : Starting enrollment job %s", 244 | LOG_INF, jobInfo->JobId); 245 | 246 | res = get_enroll_config(sessionToken, jobInfo->JobId, 247 | jobInfo->ConfigurationEndpoint, &enrConf); 248 | 249 | log_verbose("%s::%s(%d) : KeyType: %s", LOG_INF, enrConf->KeyType); 250 | log_verbose("%s::%s(%d) : Store to reenroll = %s", 251 | LOG_INF, enrConf->StorePath); 252 | 253 | /* Validate returned data */ 254 | if (enrConf) 255 | { 256 | bool failed = false; 257 | /* Verify the target store isn't a directory */ 258 | if (is_directory(enrConf->StorePath)) 259 | { 260 | log_error("%s::%s(%d) : The store path must be a file and" 261 | " not a directory.", LOG_INF); 262 | append_linef(&statusMessage, "The store path must be a file" 263 | " and not a directory."); 264 | failed = true; 265 | } 266 | 267 | if (ConfigData->UseAgentCert) { 268 | /* Verify the target store isn't the Agent store */ 269 | if (enrConf->StorePath) 270 | { 271 | if (0 == strcasecmp(ConfigData->AgentCert, enrConf->StorePath)) 272 | { 273 | 274 | log_warn("%s::%s(%d) : Attempting to re-enroll the agent " 275 | "cert is not allowed.", LOG_INF); 276 | append_linef(&statusMessage, "Attempting to re-enroll the " 277 | "agent cert is not allowed."); 278 | failed = true; 279 | } 280 | } 281 | } 282 | 283 | /* Send failure to platform */ 284 | if (failed) 285 | { 286 | struct EnrollmentCompleteResp* enrComp = NULL; 287 | send_enroll_job_complete(sessionToken, jobInfo->JobId, 288 | jobInfo->CompletionEndpoint, STAT_ERR, enrConf->AuditId, 289 | statusMessage, &enrComp); 290 | EnrollmentCompleteResp_free(enrComp); 291 | goto exit; 292 | } 293 | } 294 | else 295 | { 296 | log_error("%s::%s(%d) : Error, no enrollment configuration " 297 | "returned by platform", LOG_INF); 298 | goto exit; 299 | } 300 | 301 | if( res == 0 && 302 | AgentApiResult_log(enrConf->Result, &statusMessage, &status) ) 303 | { 304 | if(enrConf->JobCancelled) 305 | { 306 | returnable = 1; 307 | log_info("%s::%s(%d) : Job has been cancelled and will not be run", 308 | LOG_INF); 309 | } 310 | else 311 | { 312 | char* csrString = NULL; 313 | struct EnrollmentEnrollResp* enrResp = NULL; 314 | struct EnrollmentCompleteResp* enrComp = NULL; 315 | 316 | long auditId = enrConf->AuditId; 317 | log_verbose("%s::%s(%d) : Audit Id: %ld", LOG_INF, auditId); 318 | 319 | log_trace("%s::%s(%d) : Checking for supplied entropy from" 320 | " the platform", LOG_INF); 321 | if (enrConf->Entropy) 322 | { 323 | if((0 != strcasecmp("",enrConf->Entropy)) && 324 | (0 < strlen(enrConf->Entropy))) 325 | { 326 | log_verbose("%s::%s(%d) : Seeding RNG with provided" 327 | " entropy", LOG_INF); 328 | ssl_seed_rng(enrConf->Entropy); 329 | } 330 | } 331 | 332 | if(status < STAT_ERR) 333 | { 334 | /* Generate the keypair and store it in a temp location in */ 335 | /* the SSL wrapper function */ 336 | #if defined(__TPM__) 337 | if ( (NULL == enrConf->PrivateKeyPath) || 338 | (0 == strcasecmp("",enrConf->PrivateKeyPath)) ) 339 | { 340 | log_error("%s::%s(%d) : A TPM requires a PrivateKeyPath", 341 | LOG_INF); 342 | status = STAT_ERR; 343 | append_linef(&statusMessage, "%s::%s(%d) : A TPM " 344 | "requires a PrivateKeyPath", LOG_INF); 345 | } 346 | if ( !(generate_keypair(enrConf->KeyType, enrConf->KeySize, 347 | enrConf->PrivateKeyPath)) ) 348 | #else 349 | if( !(generate_keypair(enrConf->KeyType, enrConf->KeySize)) ) 350 | #endif 351 | { 352 | log_error("%s::%s(%d) : Unable to generate key pair " 353 | "with type %s and length %d", LOG_INF, enrConf->KeyType, 354 | enrConf->KeySize); 355 | status = STAT_ERR; 356 | append_linef(&statusMessage, "Unable to generate key " 357 | "pair with type %s and length %d", enrConf->KeyType, 358 | enrConf->KeySize); 359 | } 360 | } 361 | 362 | if(status < STAT_ERR) 363 | { 364 | /* Generate a CSR based on the keypair in the wrapper */ 365 | size_t csrLen; 366 | log_trace("%s::%s(%d) : Generating CSR", LOG_INF); 367 | csrString = ssl_generate_csr(enrConf->Subject, &csrLen, 368 | &statusMessage); 369 | if(!csrString) 370 | { 371 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 372 | append_linef(&statusMessage, "%s::%s(%d) : Out of memory", 373 | LOG_INF); 374 | status = STAT_ERR; 375 | } 376 | else 377 | { 378 | log_verbose("%s::%s(%d) : Successfully created CSR", 379 | LOG_INF); 380 | } 381 | } 382 | 383 | if(status < STAT_ERR) 384 | { 385 | /* Send the CSR to the Platform for signing */ 386 | res = send_enrollment(sessionToken, jobInfo->JobId, 387 | enrConf->EnrollEndpoint, csrString, &enrResp); 388 | if(res == 0 && enrResp) 389 | { 390 | AgentApiResult_log(enrResp->Result, &statusMessage, &status); 391 | } 392 | else 393 | { 394 | log_error("%s::%s(%d) : Enrollment failed with error" 395 | " code %d", LOG_INF, res); 396 | status = STAT_ERR; 397 | append_linef(&statusMessage, "Enrollment failed with " 398 | "error code %d", res); 399 | } 400 | } 401 | 402 | if(status < STAT_ERR && enrResp && enrResp->Certificate) 403 | { 404 | /* Save the temporary keypair (stored in the SSL wrapper) */ 405 | /* into a location */ 406 | res = save_cert_key(enrConf->StorePath, enrConf->PrivateKeyPath, 407 | enrConf->StorePassword, enrResp->Certificate, 408 | &statusMessage, &status); 409 | } 410 | 411 | /* Send the normal job complete */ 412 | res = send_enroll_job_complete(sessionToken, jobInfo->JobId, 413 | jobInfo->CompletionEndpoint, status+1, auditId, 414 | statusMessage, &enrComp); 415 | 416 | #if defined(__RUN_CHAIN_JOBS__) 417 | if(enrComp) 418 | { 419 | if( AgentApiResult_log(enrComp->Result, NULL, NULL) && 420 | enrComp->InventoryJob && 421 | chainJob) 422 | { 423 | *chainJob = strdup(enrComp->InventoryJob); 424 | } 425 | } 426 | #endif 427 | 428 | if(status >= STAT_ERR) 429 | { 430 | log_info("%s::%s(%d) : Enrollment job %s failed with error: %s", 431 | LOG_INF, jobInfo->JobId, statusMessage); 432 | } 433 | else if(status == STAT_WARN) 434 | { 435 | log_warn("%s::%s(%d) : Enrollment job %s completed with" 436 | " warning: %s", LOG_INF, jobInfo->JobId, statusMessage); 437 | } 438 | else 439 | { 440 | log_info("%s::%s(%d) : Enrollment job %s completed " 441 | "successfully", LOG_INF, jobInfo->JobId); 442 | } 443 | 444 | if (csrString) 445 | { 446 | free(csrString); 447 | } 448 | if (enrResp) 449 | { 450 | EnrollmentEnrollResp_free(enrResp); 451 | } 452 | if (enrComp) 453 | { 454 | EnrollmentCompleteResp_free(enrComp); 455 | } 456 | } 457 | } 458 | 459 | exit: 460 | if (enrConf) 461 | { 462 | EnrollmentConfigResp_free(enrConf); 463 | } 464 | if (statusMessage) 465 | { 466 | free(statusMessage); 467 | } 468 | 469 | return returnable; 470 | } /* cms_job_enroll */ 471 | /******************************************************************************/ 472 | /******************************* END OF FILE **********************************/ 473 | /******************************************************************************/ -------------------------------------------------------------------------------- /management.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include "management.h" 14 | #include "httpclient.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "lib/base64.h" 20 | #include 21 | #include "utils.h" 22 | #include "logging.h" 23 | 24 | #ifdef __WOLF_SSL__ 25 | #include "wolfssl_wrapper/wolfssl_wrapper.h" 26 | #else 27 | #ifdef __OPEN_SSL__ 28 | #include "openssl_wrapper/openssl_wrapper.h" 29 | #else 30 | #ifdef __TPM__ 31 | #else 32 | #endif 33 | #endif 34 | #endif 35 | 36 | /******************************************************************************/ 37 | /***************************** LOCAL DEFINES *********************************/ 38 | /******************************************************************************/ 39 | 40 | /******************************************************************************/ 41 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 42 | /******************************************************************************/ 43 | 44 | /******************************************************************************/ 45 | /************************* LOCAL GLOBAL VARIABLES *****************************/ 46 | /******************************************************************************/ 47 | 48 | /******************************************************************************/ 49 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 50 | /******************************************************************************/ 51 | /** */ 52 | /* Ask the platform for the detailed configuration for the management job */ 53 | /* */ 54 | /* @param - [Input] sessionToken = the GUID for the curl session */ 55 | /* @param - [Input] jobId = the platform's GUID for this job */ 56 | /* @param - [Input] endpoint = the relative URL to hit for the config request*/ 57 | /* @param - [Output] pManConf = The platform's response is placed into this */ 58 | /* @return - success : 0 */ 59 | /* failure : an HTTP response code */ 60 | /* */ 61 | static int get_management_config(const char* sessionToken, const char* jobId, 62 | const char* endpoint, struct ManagementConfigResp** pManConf) 63 | { 64 | char* url = NULL; 65 | 66 | log_verbose("%s::%s(%d) : Sending management config request: %s", LOG_INF, jobId); 67 | struct CommonConfigReq* req = CommonConfigReq_new(); 68 | if (!req) { 69 | log_error("%s::%s(%d) : Error creating new request structure", LOG_INF); 70 | return 999; 71 | } 72 | req->JobId = strdup(jobId); 73 | req->SessionToken = strdup(sessionToken); 74 | 75 | char* jsonReq = CommonConfigReq_toJson(req); 76 | 77 | char* jsonResp = NULL; 78 | 79 | url = config_build_url(endpoint, true); 80 | 81 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 82 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 83 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp, 84 | ConfigData->httpRetries,ConfigData->retryInterval); 85 | 86 | if(res == 0) { 87 | *pManConf = ManagementConfigResp_fromJson(jsonResp); 88 | } else { 89 | log_error("%s::%s(%d) : Config retrieval failed with error code %d", LOG_INF, res); 90 | } 91 | 92 | if (jsonReq) free(jsonReq); 93 | if (jsonResp) free(jsonResp); 94 | if (url) free(url); 95 | if (req) CommonConfigReq_free(req); 96 | 97 | return res; 98 | } /* get_management_config */ 99 | 100 | /** */ 101 | /* Send the job status (success/failure) and any associated data to the */ 102 | /* platform. */ 103 | /* */ 104 | /* @param - [Input] sessionToken = the GUID for the curl session */ 105 | /* @param - [Input] jodId = the platform GUID reference for the job */ 106 | /* @param - [Input] endpoint = the */ 107 | /* */ 108 | static int send_management_job_complete(const char* sessionToken, 109 | const char* jobId, const char* endpoint, int jobStatus, long auditId, 110 | const char* message, struct ManagementCompleteResp** pManComp) 111 | { 112 | char* url = NULL; 113 | 114 | log_verbose("%s::%s(%d) : Sending management complete request: %ld " 115 | "for session: %s", LOG_INF, auditId, sessionToken); 116 | struct CommonCompleteReq* req = CommonCompleteReq_new(); 117 | if (!req) { 118 | log_error("%s::%s(%d) : Error creating new request structure", LOG_INF); 119 | return 999; 120 | } 121 | 122 | req->SessionToken = strdup(sessionToken); 123 | req->JobId = strdup(jobId); 124 | req->Status = jobStatus; 125 | req->AuditId = auditId; 126 | req->Message = strdup(message); 127 | 128 | char* jsonReq = CommonCompleteReq_toJson(req); 129 | 130 | char* jsonResp = NULL; 131 | 132 | url = config_build_url(endpoint, true); 133 | 134 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 135 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 136 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp, 137 | ConfigData->httpRetries,ConfigData->retryInterval); 138 | 139 | if(res == 0) 140 | { 141 | *pManComp = ManagementCompleteResp_fromJson(jsonResp); 142 | } 143 | else 144 | { 145 | log_error("%s::%s(%d) : Job completion failed with error code %d", 146 | LOG_INF, res); 147 | } 148 | 149 | if (jsonReq) free(jsonReq); 150 | if (jsonResp) free(jsonResp); 151 | if (url) free(url); 152 | if (req) CommonCompleteReq_free(req); 153 | 154 | return res; 155 | } /* send_management_job_complete */ 156 | 157 | /** */ 158 | /* Perform the management job to add the certificate downloaded from the */ 159 | /* platform into a certificate store. */ 160 | /* */ 161 | /* @param - [Input] : storePath = the store's location */ 162 | /* @param - [Input] : certASCII = the naked PEM */ 163 | /* @param - [Output] : pMessage = An array of messages we want to pass back */ 164 | /* to the calling function */ 165 | /* @param - [Output] : pStatus = the result of the add operation */ 166 | /* @return - success : 0 */ 167 | /* - failure : any other integer */ 168 | /* */ 169 | static int add_cert_to_store(const char* storePath, const char* certASCII, 170 | char** pMessage, enum AgentApiResultStatus* pStatus) 171 | { 172 | int ret = 0; 173 | int i; 174 | bool foundCert; 175 | PemInventoryItem* certToAdd = NULL; 176 | PemInventoryList* pemList = NULL; 177 | 178 | /* 1.) Convert the cert to a PemInventoryItem, */ 179 | /* which has a cert & thumbprint */ 180 | 181 | log_trace("%s::%s(%d) : Creating a new PemInventoryItem for certificate", 182 | LOG_INF); 183 | if ( !ssl_PemInventoryItem_create(&certToAdd, certASCII) ) 184 | { 185 | log_error("%s::%s(%d) : Error creating cert thumbprint or invalid cert", 186 | LOG_INF); 187 | append_linef(pMessage, "%s::%s(%d) : Error creating cert thumbprint" 188 | " or invalid cert", LOG_INF); 189 | *pStatus = STAT_ERR; 190 | return -1; 191 | } 192 | log_trace("%s::%s(%d) : New certificate has a thumbprint of %s", 193 | LOG_INF, certToAdd->thumbprint_string); 194 | 195 | /* 2.) Read all the certs in the cert store */ 196 | log_trace("%s::%s(%d) : Reading cert store %s's inventory", 197 | LOG_INF, storePath); 198 | if ( 0 != ssl_read_store_inventory(storePath, NULL, &pemList) ) 199 | { 200 | log_error("%s::%s(%d) : Error reading PEM store at %s", 201 | LOG_INF, storePath); 202 | append_linef(pMessage, "%s::%s(%d) : Error reading PEM store at %s", 203 | LOG_INF, storePath); 204 | if ( certToAdd ) 205 | { 206 | PemInventoryItem_free(certToAdd); 207 | } 208 | if ( pemList ) 209 | { 210 | PemInventoryList_free(pemList); 211 | } 212 | *pStatus = STAT_ERR; 213 | return -1; 214 | } 215 | log_trace("%s::%s(%d) : Found %d certs in store", 216 | LOG_INF, pemList->item_count); 217 | for(i = 0; pemList->item_count > i; i++) 218 | { 219 | log_trace("%s::%s(%d) : Thumbprint #%d found: %s", 220 | LOG_INF, i, pemList->items[i]->thumbprint_string); 221 | } 222 | 223 | /* 3.) Does the cert exist in the store already? */ 224 | foundCert = false; 225 | i = 0; 226 | log_trace("%s::%s(%d) : Checking if cert to add is already in the store", 227 | LOG_INF); 228 | while ( (pemList->item_count > i) && 229 | (false == foundCert) && 230 | (certToAdd) ) 231 | { 232 | log_trace("%s::%s(%d) : comparing thubmprints\n%s\n%s", 233 | LOG_INF, certToAdd->thumbprint_string, 234 | pemList->items[i]->thumbprint_string); 235 | if ( 0 == strcasecmp(certToAdd->thumbprint_string, 236 | pemList->items[i]->thumbprint_string) ) 237 | { 238 | foundCert = true; 239 | } 240 | i++; 241 | } 242 | 243 | log_verbose("%s::%s(%d) : Found cert: %s", 244 | LOG_INF, (foundCert ? "yes" : "no")); 245 | 246 | /* 4.) If the cert doesn't exist, add it too the store */ 247 | if( false == foundCert ) 248 | { 249 | log_trace("%s::%s(%d) : Adding cert with thumbprint %s to store %s", 250 | LOG_INF, certToAdd->thumbprint_string, storePath); 251 | if ( !ssl_Store_Cert_add(storePath, certASCII) ) 252 | { 253 | log_error("%s::%s(%d) Error writing cert to store", LOG_INF); 254 | append_linef(pMessage, "%s::%s(%d) Error writing cert to store", 255 | LOG_INF); 256 | *pStatus = STAT_ERR; 257 | ret = -1; 258 | } 259 | else 260 | { 261 | log_verbose("%s::%s(%d) Certificate successfully written to store", 262 | LOG_INF); 263 | *pStatus = STAT_SUCCESS; 264 | ret = 0; 265 | } 266 | } 267 | else 268 | { 269 | log_warn("%s::%s(%d) : WARNING: Certificate with thumbprint %s " 270 | "was already present in store %s", LOG_INF, 271 | certToAdd->thumbprint_string, storePath); 272 | append_linef(pMessage, "%s::%s(%d) : WARNING: Certificate with " 273 | "thumbprint %s was already present in store %s", 274 | LOG_INF, certToAdd->thumbprint_string, storePath); 275 | *pStatus = STAT_WARN; 276 | ret = 0; 277 | } 278 | 279 | /* 5.) Cleanup */ 280 | if ( certToAdd ) PemInventoryItem_free(certToAdd); 281 | if ( pemList ) PemInventoryList_free(pemList); 282 | 283 | return ret; 284 | } /* add_cert_to_store */ 285 | 286 | /** */ 287 | /* Remove a certificate (and any associated stored key) from a store */ 288 | /* */ 289 | /* @param - [Input] : storePath = the location of the store */ 290 | /* @param - [Input] : searchThumb = the sha1 hash of the certificate to */ 291 | /* remove */ 292 | /* @param - [Input] : keyPath = (optional) the path to the key for the */ 293 | /* certificate. If NULL, the key is assumed to live */ 294 | /* inside the store */ 295 | /* @param - [Input] : password = the password for an encrypted key */ 296 | /* @param - [Output] : pMessage = An array of messages we want to pass */ 297 | /* back to the calling function */ 298 | /* @param - [Output] : pStatus = The result of the certificate removal */ 299 | /* @return - success : 0 */ 300 | /* - failure : Any other integer */ 301 | /* */ 302 | static int remove_cert_from_store(const char* storePath, 303 | const char* searchThumb, const char* keyPath, const char* password, 304 | char** pMessage, enum AgentApiResultStatus* pStatus) 305 | { 306 | int ret = 0; 307 | 308 | if (!ssl_remove_cert_from_store(storePath, searchThumb, keyPath, password)) 309 | { 310 | log_error("%s::%s(%d) : Unable to remove cert from store at %s", 311 | LOG_INF, storePath); 312 | append_linef(pMessage, "Unable to remove cert from store at %s", 313 | storePath); 314 | *pStatus = STAT_ERR; 315 | } 316 | return ret; 317 | } /* remove_cert_from_store */ 318 | 319 | /******************************************************************************/ 320 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 321 | /******************************************************************************/ 322 | /** */ 323 | /* The management job flow: */ 324 | /* 1.) Ask the platform for the details on the managment job (get config)*/ 325 | /* 2.) Based on the response: */ 326 | /* a.) If the job was canceled, you finish */ 327 | /* b.) If the job was an ADD, run the ADD workflow */ 328 | /* c.) If the job was a REMOVE, run the REMOVE workflow */ 329 | /* 3.) Respond to the platform as to the job's success */ 330 | /* */ 331 | /* @param - [Input] : jobInfo = the details of the job from the platform */ 332 | /* @param - [Input] : sessionToken = the GUID for the curl session */ 333 | /* @param - [Input] : chainJob = any additional jobs required to run after */ 334 | /* this one -- typically an Inventory job */ 335 | /* @return - job was run : 0 */ 336 | /* - job was canceled : 1 */ 337 | /* */ 338 | int cms_job_manage(struct SessionJob* jobInfo, char* sessionToken, 339 | char** chainJob) 340 | { 341 | int res = 0; 342 | struct ManagementConfigResp* manConf = NULL; 343 | char* statusMessage = strdup(""); 344 | enum AgentApiResultStatus status = STAT_UNK; 345 | int returnable = 0; 346 | log_info("%s::%s(%d) : Starting management job %s", LOG_INF, 347 | jobInfo->JobId); 348 | 349 | res = get_management_config(sessionToken, jobInfo->JobId, 350 | jobInfo->ConfigurationEndpoint, &manConf); 351 | 352 | /* Validate data */ 353 | if (manConf) 354 | { 355 | bool failed = false; 356 | if (manConf->Job.StorePath) 357 | { 358 | /* Is the target store a directory and not a file? */ 359 | if (is_directory(manConf->Job.StorePath)) 360 | { 361 | log_error("%s::%s(%d) : The store path must be a file and " 362 | "not a directory.", LOG_INF); 363 | append_linef(&statusMessage, "The store path must be a file " 364 | "and not a directory."); 365 | failed = true; 366 | } 367 | /* Is the target store the Agent store? */ 368 | if (0 == strcasecmp(ConfigData->AgentCert, manConf->Job.StorePath)) 369 | { 370 | log_warn("%s::%s(%d) : Attempting a Management job on the " 371 | "agent cert store is not allowed.", LOG_INF); 372 | append_linef(&statusMessage, "Attempting a Management job the" 373 | " agent cert store is not allowed."); 374 | failed = true; 375 | } 376 | /* Verify the target store exists */ 377 | if (!file_exists(manConf->Job.StorePath)) 378 | { 379 | log_warn("%s::%s(%d) : Attempting to manage a certificate" 380 | " store that does not exist yet.", LOG_INF); 381 | append_linef(&statusMessage, "Attempting to manage a " 382 | "certificate store that does not exist yet."); 383 | failed = true; 384 | } 385 | } 386 | else 387 | { 388 | log_error("%s::%s(%d) : Job doesn't contain a target store " 389 | "to manage.", LOG_INF); 390 | append_linef(&statusMessage, "Job doesn't contain a target " 391 | "store to manage."); 392 | failed = true; 393 | } 394 | /* if any test failed, then let the platform know about it. */ 395 | if (failed) 396 | { 397 | struct ManagementCompleteResp* manComp = NULL; 398 | send_management_job_complete(sessionToken, jobInfo->JobId, 399 | jobInfo->CompletionEndpoint, STAT_ERR, manConf->AuditId, 400 | statusMessage, &manComp); 401 | ManagementCompleteResp_free(manComp); 402 | goto exit; 403 | } 404 | } 405 | else 406 | { 407 | log_error("%s::%s(%d) : No managment configuration was returned" 408 | " from the platform.", LOG_INF); 409 | goto exit; 410 | } 411 | 412 | /* Data ok, process job */ 413 | if(res == 0 && AgentApiResult_log(manConf->Result, &statusMessage, &status)) 414 | { 415 | if(manConf->JobCancelled) 416 | { 417 | returnable = 1; 418 | log_info("%s::%s(%d) : Job has been cancelled and will not be run", 419 | LOG_INF); 420 | } 421 | else 422 | { 423 | long auditId = manConf->AuditId; 424 | log_verbose("%s::%s(%d) : Audit Id: %ld", LOG_INF, auditId); 425 | 426 | int opType = manConf->Job.OperationType; 427 | switch(opType) 428 | { 429 | case OP_ADD: 430 | log_verbose("%s::%s(%d) : Add certificate operation", LOG_INF); 431 | 432 | if(manConf->Job.PrivateKeyEntry) 433 | { 434 | const char* msg = "Adding a PFX is not supported at" 435 | " this time"; 436 | log_info("%s::%s(%d) : %s", LOG_INF, msg); 437 | status = STAT_ERR; 438 | append_line(&statusMessage, msg); 439 | } 440 | else 441 | { 442 | log_info("%s::%s(%d) : Attempting to add certificate" 443 | " to the store:\n%s", LOG_INF, 444 | manConf->Job.EntryContents); 445 | res = add_cert_to_store(manConf->Job.StorePath, 446 | manConf->Job.EntryContents, &statusMessage, &status); 447 | } 448 | break; 449 | case OP_REM: 450 | log_verbose("%s::%s(%d) : Remove certificate operation", 451 | LOG_INF); 452 | res = remove_cert_from_store(manConf->Job.StorePath, 453 | manConf->Job.Alias, manConf->Job.PrivateKeyPath, 454 | manConf->Job.StorePassword, &statusMessage, &status); 455 | break; 456 | default: 457 | log_error("%s::%s(%d) : Unsupported operation type: %d", 458 | LOG_INF, opType); 459 | append_linef(&statusMessage, "Unsupported operation type: %d", 460 | opType); 461 | status = STAT_ERR; 462 | break; 463 | } 464 | 465 | struct ManagementCompleteResp* manComp = NULL; 466 | res = send_management_job_complete(sessionToken, 467 | jobInfo->JobId, jobInfo->CompletionEndpoint, status+1, 468 | auditId, statusMessage, &manComp); 469 | /* NOTE: Removed chain job due to inventory job running twice */ 470 | 471 | if(status >= STAT_ERR) 472 | { 473 | log_error("%s::%s(%d) : Management job %s failed with " 474 | "error: %s", LOG_INF, jobInfo->JobId, statusMessage); 475 | } 476 | else if(status == STAT_WARN) 477 | { 478 | log_warn("%s::%s(%d) : Management job %s completed" 479 | " with warning: %s",LOG_INF, jobInfo->JobId, statusMessage); 480 | } 481 | else 482 | { 483 | log_info("%s::%s(%d) : Management job %s completed" 484 | " successfully", LOG_INF, jobInfo->JobId); 485 | } 486 | 487 | ManagementCompleteResp_free(manComp); 488 | } 489 | } 490 | 491 | exit: 492 | ManagementConfigResp_free(manConf); 493 | free(statusMessage); 494 | 495 | return returnable; 496 | } /* cms_job_manage */ 497 | /******************************************************************************/ 498 | /******************************* END OF FILE **********************************/ 499 | /******************************************************************************/ -------------------------------------------------------------------------------- /schedule.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include "schedule.h" 14 | #include "logging.h" 15 | #include "agent.h" 16 | #include 17 | #include 18 | #include 19 | 20 | /******************************************************************************/ 21 | /***************************** LOCAL DEFINES *********************************/ 22 | /******************************************************************************/ 23 | 24 | /******************************************************************************/ 25 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 26 | /******************************************************************************/ 27 | struct ScheduledJob* currentJob; /* Defined in schedule.c */ 28 | 29 | /******************************************************************************/ 30 | /************************* LOCAL GLOBAL VARIABLES *****************************/ 31 | /******************************************************************************/ 32 | 33 | /******************************************************************************/ 34 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 35 | /******************************************************************************/ 36 | /** */ 37 | /* Retrieve the Local offset from UTC. Negative if local time is "behind" UTC */ 38 | /* */ 39 | /* @param - none */ 40 | /* @return - success : The difference between time(NULL) & UTC time(NULL) */ 41 | /* failure : n/a */ 42 | /* */ 43 | static time_t get_utc_offset(void) 44 | { 45 | time_t start = time(NULL); 46 | struct tm tmp; 47 | gmtime_r(&start, &tmp); 48 | tmp.tm_isdst = 0; 49 | time_t rt = mktime(&tmp); 50 | 51 | return (start - rt); 52 | } /* get_utc_offset */ 53 | 54 | /** */ 55 | /* Add the number of minutes to a time. */ 56 | /* */ 57 | /* @param - [Input] prev = The time to be added to */ 58 | /* @param - [Input] intSch = a string representing the # of minutes to add */ 59 | /* @return - success : the time intSch from prev */ 60 | /* failure : prev */ 61 | /* */ 62 | static time_t next_interval(char* intSch, time_t prev) 63 | { 64 | int mins = atoi(intSch); 65 | if(mins > 0) 66 | { 67 | return (mins * 60) + prev; 68 | } 69 | else 70 | { 71 | log_error("%s::%s(%d) : Invalid interval: %s", LOG_INF, intSch); 72 | return prev; 73 | } 74 | } /* next_interval */ 75 | 76 | /** */ 77 | /* Add the number of days to a time */ 78 | /* */ 79 | /* @param - [Input] prev = The time to be added to */ 80 | /* @param - [Input] dailySch = a string representing the # of days to add */ 81 | /* @return - success : the time dailySch from prev */ 82 | /* failure : prev */ 83 | /* */ 84 | static time_t next_daily(char* dailySch, time_t prev) 85 | { 86 | int hrs, mins; 87 | if((2 == sscanf(dailySch, "%d:%d", &hrs, &mins)) 88 | && hrs >= 0 && hrs <=23 && mins >= 0 && mins <= 59) 89 | { 90 | struct tm prevStruct; 91 | gmtime_r(&prev, &prevStruct); 92 | 93 | prevStruct.tm_hour = hrs; 94 | prevStruct.tm_min = mins; 95 | prevStruct.tm_sec = 0; 96 | /* Should guarantee this is before prev (up to 37 hrs of skew from */ 97 | /* setting hour, etc. and TZ)*/ 98 | prevStruct.tm_mday-=2; 99 | prevStruct.tm_isdst = 0; 100 | 101 | time_t rtTime = mktime(&prevStruct); 102 | rtTime += get_utc_offset(); 103 | 104 | while(rtTime <= prev) 105 | { 106 | rtTime += 60 * 60 * 24; /* Step forward 1 day */ 107 | } 108 | 109 | return rtTime; 110 | } 111 | else 112 | { 113 | log_error("%s::%s(%d) : Invalid daily: %s", LOG_INF, dailySch); 114 | return prev; 115 | } 116 | } /* next_daily */ 117 | 118 | /** */ 119 | /* Add the number of weeks to a time */ 120 | /* */ 121 | /* @param - [Input] prev = The time to be added to */ 122 | /* @param - [Input] weeklySch = a string representing the # of weeks to add */ 123 | /* @return - success : the time weeklySch from prev */ 124 | /* failure : prev */ 125 | /* */ 126 | static time_t next_weekly(char* weeklySch, time_t prev) 127 | { 128 | int dows; 129 | int hrs, mins; 130 | if((3 == sscanf(weeklySch, "%d_%d:%d", &dows, &hrs, &mins)) 131 | && hrs >= 0 && hrs <=23 && mins >= 0 && mins <= 59) 132 | { 133 | struct tm prevStruct; 134 | gmtime_r(&prev, &prevStruct); 135 | 136 | if( 137 | hrs < prevStruct.tm_hour || \ 138 | (hrs == prevStruct.tm_hour && mins <= prevStruct.tm_min) ) 139 | { 140 | /* This gets us to the first time after prev with the right hrs */ 141 | /* and mins Either later in the same day, or earlier in the next*/ 142 | prevStruct.tm_mday++; 143 | prevStruct.tm_wday++; 144 | } 145 | prevStruct.tm_hour = hrs; 146 | prevStruct.tm_min = mins; 147 | prevStruct.tm_sec = 0; 148 | prevStruct.tm_isdst = 0; 149 | 150 | int smallest = 10; 151 | while(dows > 0) 152 | { 153 | int dow = dows % 10; 154 | /* Add 7 to guarantee positive. */ 155 | /* Add 1 to account for 0-based prevStruct */ 156 | int offset = ((dow + 7) - (prevStruct.tm_wday + 1)) % 7; 157 | smallest = (smallest > offset) ? offset : smallest; 158 | 159 | dows /= 10; 160 | } 161 | 162 | /*/ Number of days (0 to 6) to advance to get to the appropriate day */ 163 | prevStruct.tm_mday += smallest; 164 | 165 | time_t rtTime = mktime(&prevStruct); 166 | rtTime += get_utc_offset(); 167 | 168 | return rtTime; 169 | } 170 | else 171 | { 172 | log_error("%s::%s(%d) : Invalid weekly: %s", LOG_INF, weeklySch); 173 | return prev; 174 | } 175 | } /* next_weekly */ 176 | 177 | /** */ 178 | /* Add the number of months to a time */ 179 | /* */ 180 | /* @param - [Input] prev = The time to be added to */ 181 | /* @param - [Input] monthSch = a string representing the # of months to add */ 182 | /* @return - success : the time monthSch from prev */ 183 | /* failure : prev */ 184 | /* */ 185 | static time_t next_monthly(char* monthSch, time_t prev) 186 | { 187 | int dom, hrs, mins; 188 | if( 189 | (3 == sscanf(monthSch, "%d_%d:%d", &dom, &hrs, &mins)) && 190 | hrs >= 0 && hrs <=23 && 191 | mins >= 0 && mins <= 59 && 192 | dom >=1 && dom <= 31 ) 193 | { 194 | struct tm prevStruct; 195 | gmtime_r(&prev, &prevStruct); 196 | 197 | prevStruct.tm_hour = hrs; 198 | prevStruct.tm_min = mins; 199 | prevStruct.tm_sec = 0; 200 | prevStruct.tm_mday = dom; 201 | prevStruct.tm_mon--; /* Make sure we are before prev */ 202 | prevStruct.tm_isdst = 0; 203 | 204 | time_t rtTime; 205 | do 206 | { 207 | rtTime = mktime(&prevStruct); 208 | rtTime += get_utc_offset(); 209 | prevStruct.tm_mon++; /* For the next time around the loop */ 210 | } 211 | while(rtTime <= prev); 212 | 213 | return rtTime; 214 | } 215 | else if((2 == sscanf(monthSch, "L_%d:%d", &hrs, &mins)) 216 | && hrs >= 0 && hrs <=23 && mins >= 0 && mins <= 59) 217 | { 218 | struct tm prevStruct; 219 | gmtime_r(&prev, &prevStruct); 220 | 221 | prevStruct.tm_hour = hrs; 222 | prevStruct.tm_min = mins; 223 | prevStruct.tm_sec = 0; 224 | prevStruct.tm_mday = 0; 225 | prevStruct.tm_mon--; /* Make sure we are before prev */ 226 | prevStruct.tm_isdst = 0; 227 | 228 | int curMon = prevStruct.tm_mon; 229 | time_t rtTime; 230 | do 231 | { 232 | rtTime = mktime(&prevStruct); 233 | rtTime += get_utc_offset(); 234 | /* For the next time around the loop, if needed */ 235 | prevStruct.tm_mon = ++curMon; 236 | prevStruct.tm_mday = 0; 237 | } 238 | while(rtTime <= prev); 239 | 240 | return rtTime; 241 | } 242 | else 243 | { 244 | log_error("%s::%s(%d) : Invalid monthly: %s", LOG_INF, monthSch); 245 | return prev; 246 | } 247 | } /* next_monthly */ 248 | 249 | /** */ 250 | /* Decode a scheduled datetime into a time_t structure. */ 251 | /* NOTE: This does not verify that the date is a future date. */ 252 | /* */ 253 | /* @param - [Input] oneTimeSch is a string designating a date time in the */ 254 | /* format of : YYYY-MM-DDTHH:mm where */ 255 | /* YYYY = Year */ 256 | /* MM = Month [01..12] */ 257 | /* DD = Day of Month [01..31] */ 258 | /* HH = Hours [00..23] */ 259 | /* mm = Minutes [00..59] */ 260 | /* @return - success : the formatted string as a time_t structure */ 261 | /* failure : time(NULL) */ 262 | /* */ 263 | static time_t next_one_time(char* oneTimeSch) 264 | { 265 | int year, mon, day, hrs, mins; 266 | if( 267 | (5 == sscanf(oneTimeSch, "%d-%d-%dT%d:%d", &year, 268 | &mon, &day, &hrs, &mins)) && 269 | hrs >= 0 && hrs <=23 && 270 | mins >= 0 && mins <= 59 && 271 | mon >=1 && mon <= 12 && 272 | day >= 1 && day <=31) 273 | { 274 | struct tm tStruct; 275 | 276 | tStruct.tm_hour = hrs; 277 | tStruct.tm_min = mins; 278 | tStruct.tm_sec = 0; 279 | tStruct.tm_mday = day; 280 | tStruct.tm_mon = mon - 1; // Months go from 0 to 11 281 | tStruct.tm_year = year - 1900; // Years since 1900 282 | tStruct.tm_isdst = 0; 283 | 284 | time_t rtTime = mktime(&tStruct); 285 | rtTime += get_utc_offset(); 286 | 287 | return rtTime; 288 | } 289 | else 290 | { 291 | log_error("%s::%s(%d) : Invalid one-time: %s", LOG_INF, oneTimeSch); 292 | return time(NULL); 293 | } 294 | } /* next_one_time */ 295 | 296 | /******************************************************************************/ 297 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 298 | /******************************************************************************/ 299 | /** */ 300 | /* Get the time of the next execution of a job based on the previous job */ 301 | /* execution and the schedule string. */ 302 | /* */ 303 | /* @param - [Input] sch = The schedule string in the form of */ 304 | /* T_X where: */ 305 | /* T = job type. Which is one of: */ 306 | /* D = Daily */ 307 | /* M = Monthly */ 308 | /* O = One-time execution */ 309 | /* I = Interval (aka Minutes) */ 310 | /* W = Weekly */ 311 | /* X = Integer or substring indicating the time delay */ 312 | /* (e.g., I_5 = 5 minutes, D_2 = every other day, */ 313 | /* M_1_3:0 = 1st day hour 3 minute 0 of each month */ 314 | /* O_2020-10-31T13:30 = One time job @ 31-Oct-2020 & 1:30pm)*/ 315 | /* @param - [Input] prev the last time the job ran */ 316 | /* (set to time(NULL) if never) */ 317 | /* @return - success : time_t = the next time the job runs */ 318 | /* failure : -1 */ 319 | /* */ 320 | time_t next_execution(char* sch, time_t prev) 321 | { 322 | time_t next = -1; 323 | 324 | if(!sch || strlen(sch) < 3) 325 | { 326 | log_verbose("%s::%s(%d) : Schedule is not provided, indicating " 327 | "job should be run immediately", LOG_INF); 328 | return next = prev; 329 | } 330 | 331 | switch(sch[0]) 332 | { 333 | case 'D': 334 | log_verbose("%s::%s(%d) : Daily schedule: %s", LOG_INF, sch); 335 | next = next_daily(&sch[2], prev); 336 | break; 337 | case 'M': 338 | log_verbose("%s::%s(%d) : Monthly schedule: %s", LOG_INF, sch); 339 | next = next_monthly(&sch[2], prev); 340 | break; 341 | case 'O': 342 | log_verbose("%s::%s(%d) : One-time schedule: %s", LOG_INF, sch); 343 | next = next_one_time(&sch[2]); 344 | break; 345 | case 'I': 346 | log_verbose("%s::%s(%d) : Interval schedule: %s", LOG_INF, sch); 347 | next = next_interval(&sch[2], prev); 348 | break; 349 | case 'W': 350 | log_verbose("%s::%s(%d) : Weekly schedule: %s", LOG_INF, sch); 351 | next = next_weekly(&sch[2], prev); 352 | break; 353 | default: 354 | log_error("%s::%s(%d) : Unknown schedule: %s", LOG_INF, sch); 355 | break; 356 | } 357 | 358 | return next; 359 | } /* next_execution */ 360 | 361 | /** */ 362 | /* Go through the job list and see if it is time to run a job. */ 363 | /* If so, return it. If not, return null. */ 364 | /* */ 365 | /* @param - [Input] pList: Linked list of ScheduledJobs */ 366 | /* @param - [Input] now: current time */ 367 | /* @return - NULL if no jobs are runnable */ 368 | /* - The SessionJob* to the job to execute */ 369 | /* */ 370 | struct SessionJob* get_runnable_job(struct ScheduledJob** pList, time_t now) 371 | { 372 | struct ScheduledJob* current = *pList; 373 | 374 | while(current) 375 | { 376 | log_trace("%s::%s(%d) : Checking job %s NextExecution = %ld Now = %ld", 377 | LOG_INF, current->Job->JobId,current->NextExecution,now); 378 | if(current->NextExecution <= now) 379 | { 380 | return current->Job; 381 | } 382 | 383 | current = current->NextJob; 384 | } 385 | 386 | log_verbose("%s::%s(%d) : No jobs to run", LOG_INF); 387 | return NULL; 388 | } 389 | 390 | /** */ 391 | /* Loop through the job list & retrieve the job structure based on jobId */ 392 | /* */ 393 | /* @param - [Input] jobId = the jobId who's details to retrieve */ 394 | /* @param - [Input] pList = the list of scheduled jobs */ 395 | /* @return - success : a pointer to the found job */ 396 | /* failure : NULL */ 397 | /* */ 398 | struct SessionJob* get_job_by_id(struct ScheduledJob** pList, const char* jobId) 399 | { 400 | struct ScheduledJob* current = *pList; 401 | 402 | while(current) 403 | { 404 | if(strcasecmp(current->Job->JobId, jobId) == 0) 405 | { 406 | return current->Job; 407 | } 408 | 409 | current = current->NextJob; 410 | } 411 | 412 | log_verbose("%s::%s(%d) : -Job %s not found", LOG_INF, jobId); 413 | return NULL; 414 | } /* get_job_by_id */ 415 | 416 | /** */ 417 | /* Clear all scheduled jobs (also free data structures) */ 418 | /* */ 419 | /* @param - [Input/Ouput] pList = A list of scheduled jobs */ 420 | /* @return - none */ 421 | /* */ 422 | void clear_job_schedules(struct ScheduledJob** pList) 423 | { 424 | struct ScheduledJob* current = *pList; 425 | 426 | while(current) 427 | { 428 | struct ScheduledJob* temp = current->NextJob; 429 | 430 | if ( current->Job ) 431 | { 432 | log_info("%s::%s(%d) : Freeing job # %s", LOG_INF, 433 | current->Job->JobId); 434 | SessionJob_free(current->Job); 435 | current->Job = NULL; 436 | current->NextJob = NULL; 437 | } 438 | free(current); 439 | current = temp; 440 | } 441 | 442 | } /* clear_job_schedules */ 443 | 444 | /** */ 445 | /* Add a job to a scheduled job list */ 446 | /* */ 447 | /* @param - [Output] pList = a list of scheduled jobs to add a new job into */ 448 | /* @param - [Input] job = a filled job session to add to the scheduled list */ 449 | /* @param - [Input] prev = the timestamp of the previous job run */ 450 | /* @return - none */ 451 | /* */ 452 | void schedule_job(struct ScheduledJob** pList, struct SessionJob* job, 453 | time_t prev) 454 | { 455 | struct ScheduledJob* newSchJob = calloc(1, sizeof(struct ScheduledJob)); 456 | if ( !newSchJob ) 457 | { 458 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 459 | return; 460 | } 461 | newSchJob->Job = job; 462 | 463 | newSchJob->NextExecution = next_execution(job->Schedule, prev); 464 | 465 | if(!(*pList)) 466 | { 467 | *pList = newSchJob; 468 | } 469 | else 470 | { 471 | struct ScheduledJob* prev = NULL; 472 | struct ScheduledJob* current = *pList; 473 | 474 | /* Go through the list of jobs & update that job if it is already */ 475 | /* In the list of jobs, if not, add the job to the end of the list */ 476 | while(current) 477 | { 478 | if(strcasecmp(current->Job->JobId, job->JobId) == 0) 479 | { 480 | log_verbose("%s::%s(%d) : Rescheduling job %s", 481 | LOG_INF, job->JobId); 482 | 483 | if( 484 | current->NextExecution > 0 && \ 485 | (!job->Schedule || job->Schedule[0] == 'O') ) 486 | { 487 | log_verbose("%s::%s(%d) : Job %s is a one-time job, " 488 | "and will not be rescheduled", LOG_INF, job->JobId); 489 | 490 | if(prev) 491 | { 492 | prev->NextJob = current->NextJob; 493 | } 494 | else /* Removing first element */ 495 | { 496 | *pList = current->NextJob; 497 | } 498 | 499 | SessionJob_free(current->Job); 500 | free(current); 501 | } 502 | else 503 | { 504 | current->NextExecution = newSchJob->NextExecution; 505 | } 506 | /*Don't need the new struct, there is already one for this job*/ 507 | free(newSchJob); 508 | newSchJob = NULL; 509 | 510 | return; 511 | } 512 | 513 | prev = current; 514 | current = current->NextJob; 515 | } 516 | 517 | prev->NextJob = newSchJob; 518 | } 519 | } /* schedule_job */ 520 | /******************************************************************************/ 521 | /******************************* END OF FILE **********************************/ 522 | /******************************************************************************/ -------------------------------------------------------------------------------- /inventory.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************/ 2 | /* Copyright 2021 Keyfactor */ 3 | /* Licensed under the Apache License, Version 2.0 (the "License"); you may */ 4 | /* not use this file except in compliance with the License. You may obtain a */ 5 | /* copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless */ 6 | /* required by applicable law or agreed to in writing, software distributed */ 7 | /* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES */ 8 | /* OR CONDITIONS OF ANY KIND, either express or implied. See the License for */ 9 | /* thespecific language governing permissions and limitations under the */ 10 | /* License. */ 11 | /******************************************************************************/ 12 | 13 | #include "inventory.h" 14 | #include 15 | #include "httpclient.h" 16 | #include 17 | #include 18 | #include "constants.h" 19 | #include "lib/base64.h" 20 | #include "utils.h" 21 | #include "constants.h" 22 | #include "logging.h" 23 | #include "config.h" 24 | 25 | #ifdef __WOLF_SSL__ 26 | #include "wolfssl_wrapper/wolfssl_wrapper.h" 27 | #else 28 | #ifdef __OPEN_SSL__ 29 | #include "openssl_wrapper/openssl_wrapper.h" 30 | #else 31 | #ifdef __TPM__ 32 | #else 33 | #endif 34 | #endif 35 | #endif 36 | 37 | /******************************************************************************/ 38 | /***************************** LOCAL DEFINES *********************************/ 39 | /******************************************************************************/ 40 | 41 | /******************************************************************************/ 42 | /************************ LOCAL GLOBAL STRUCTURES *****************************/ 43 | /******************************************************************************/ 44 | 45 | /******************************************************************************/ 46 | /************************* LOCAL GLOBAL VARIABLES *****************************/ 47 | /******************************************************************************/ 48 | 49 | /******************************************************************************/ 50 | /************************ LOCAL FUNCTION DEFINITIONS **************************/ 51 | /******************************************************************************/ 52 | static int get_inventory_config(const char* sessionToken, const char* jobId, 53 | const char* endpoint, struct InventoryConfigResp** pInvConf) 54 | { 55 | char* url = NULL; 56 | 57 | log_verbose("%s::%s(%d) : Sending inventory config request: %s", 58 | LOG_INF, jobId); 59 | struct CommonConfigReq* req = CommonConfigReq_new(); 60 | if ( NULL == req ) 61 | { 62 | log_error("%s::%s(%d) : Out of memory in CommonConfigReq_new()", 63 | LOG_INF); 64 | return -1; 65 | } 66 | 67 | req->JobId = strdup(jobId); 68 | log_trace("%s::%s(%d) : Set job ID to %s", LOG_INF, jobId); 69 | req->SessionToken = strdup(sessionToken); 70 | log_trace("%s::%s(%d) : Set session token to %s", LOG_INF, sessionToken); 71 | 72 | char* jsonReq = CommonConfigReq_toJson(req); 73 | log_trace("%s::%s(%d) : Set Common Config Request to %s", LOG_INF, jsonReq); 74 | 75 | char* jsonResp = NULL; 76 | 77 | url = config_build_url(endpoint, true); 78 | log_trace("%s::%s(%d) : Attempting a POST to %s", LOG_INF, url); 79 | 80 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 81 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 82 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp 83 | ,ConfigData->httpRetries,ConfigData->retryInterval); 84 | 85 | if(res == 0) 86 | { 87 | *pInvConf = InventoryConfigResp_fromJson(jsonResp); 88 | } 89 | else 90 | { 91 | log_error("%s::%s(%d) : Config retrieval failed with error code %d", 92 | LOG_INF, res); 93 | } 94 | 95 | free(jsonReq); 96 | free(jsonResp); 97 | free(url); 98 | CommonConfigReq_free(req); 99 | 100 | return res; 101 | } /* get_inventory_config */ 102 | 103 | static int send_inventory_update(const char* sessionToken, const char* jobId, 104 | const char* endpoint, struct InventoryUpdateList* newInv, 105 | struct InventoryUpdateResp** pUpdResp) 106 | { 107 | char* url = NULL; 108 | 109 | log_verbose("%s::%s(%d) : Sending inventory update request: %s", LOG_INF, jobId); 110 | struct InventoryUpdateReq* updReq = calloc(1, sizeof(*updReq)); 111 | if (!updReq) { 112 | log_error("%s::%s(%d) : Error couldn't allocate update request structure", LOG_INF); 113 | return 999; 114 | } 115 | 116 | updReq->SessionToken = strdup(sessionToken); 117 | updReq->JobId = strdup(jobId); 118 | updReq->Inventory = *newInv; 119 | char* jsonReq = InventoryUpdateReq_toJson(updReq); 120 | char* jsonResp = NULL; 121 | url = config_build_url(endpoint, true); 122 | 123 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 124 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 125 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp 126 | ,ConfigData->httpRetries,ConfigData->retryInterval); 127 | 128 | if(res == 0) { 129 | *pUpdResp = InventoryUpdateResp_fromJson(jsonResp); 130 | } else { 131 | log_error("%s::%s(%d) : Update submission failed with error code %d", LOG_INF, res); 132 | } 133 | 134 | if (jsonReq) free(jsonReq); 135 | if (jsonResp) free(jsonResp); 136 | if (url) free(url); 137 | if (updReq) InventoryUpdateReq_free(updReq); 138 | 139 | return res; 140 | } /* send_inventory_update */ 141 | 142 | static int send_inventory_job_complete(const char* sessionToken, 143 | const char* jobId, const char* endpoint, int jobStatus, long auditId, 144 | const char* message, struct CommonCompleteResp** pInvComp) 145 | { 146 | char* url = NULL; 147 | 148 | log_verbose("%s::%s(%d) : Sending inventory complete request: %ld for" 149 | " session: %s", LOG_INF, auditId, sessionToken); 150 | 151 | struct CommonCompleteReq* req = CommonCompleteReq_new(); 152 | if (!req) { 153 | log_error("%s::%s(%d) : Error allocating request structure", LOG_INF); 154 | return 999; 155 | } 156 | 157 | req->SessionToken = strdup(sessionToken); 158 | req->JobId = strdup(jobId); 159 | req->Status = jobStatus; 160 | req->AuditId = auditId; 161 | req->Message = strdup(message); 162 | char* jsonReq = CommonCompleteReq_toJson(req); 163 | char* jsonResp = NULL; 164 | url = config_build_url(endpoint, true); 165 | 166 | int res = http_post_json(url, ConfigData->Username, ConfigData->Password, 167 | ConfigData->TrustStore, ConfigData->AgentCert, ConfigData->AgentKey, 168 | ConfigData->AgentKeyPassword, jsonReq, &jsonResp 169 | ,ConfigData->httpRetries,ConfigData->retryInterval); 170 | 171 | if(res == 0) { 172 | *pInvComp = CommonCompleteResp_fromJson(jsonResp); 173 | } else { 174 | log_error("%s::%s(%d) : Job completion failed with error code %d", LOG_INF, res); 175 | } 176 | 177 | if (jsonReq) free(jsonReq); 178 | if (jsonResp) free(jsonResp); 179 | if (url) free(url); 180 | if (req) CommonCompleteReq_free(req); 181 | 182 | return res; 183 | } /* send_inventory_job_complete */ 184 | 185 | static void InventoryUpdateList_add(struct InventoryUpdateList* list, 186 | struct InventoryUpdateItem* item) 187 | { 188 | if(list && item) 189 | { 190 | list->items = realloc(list->items, 191 | (list->count + 1) * sizeof(struct InventoryUpdateItem*)); 192 | if (list->items) 193 | { 194 | list->items[list->count] = item; 195 | list->count++; 196 | } 197 | else 198 | { 199 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 200 | } 201 | } 202 | return; 203 | } /* InventoryUpdateList_add */ 204 | 205 | /** */ 206 | /* Free an inventory item list */ 207 | /* */ 208 | /* @param - [Input] : list = the list to free */ 209 | /* @return - none */ 210 | /* */ 211 | static void InventoryUpdateList_free(struct InventoryUpdateList* list) 212 | { 213 | if (list) 214 | { 215 | for(int i = 0; i < list->count; i++) 216 | { 217 | if (list->items[i]) 218 | { 219 | if(list->items[i]->Alias) free (list->items[i]->Alias); 220 | if(list->items[i]->Certificates) 221 | { 222 | for(int j = 0; j < list->items[i]->Certificates_count; j++) 223 | { 224 | if (list->items[i]->Certificates[j]) 225 | { 226 | free(list->items[i]->Certificates[j]); 227 | } 228 | } 229 | } 230 | free(list->items[i]); 231 | } 232 | } 233 | if (list->items) free(list->items); 234 | free(list); 235 | } 236 | return; 237 | } /* InventoryUpdateList_free */ 238 | 239 | /** */ 240 | /* The the inventory job configuration returned a list of inventory items the */ 241 | /* platform knows about. The inventory item contains an "Alias" which is the */ 242 | /* sha1 hash (aka: thumbprint) of the certificate. It also passes this same */ 243 | /* information in the "Thumbprints" json structure. */ 244 | /* */ 245 | /* Loop through the list of thumbprints pulled from the PEM store & compare */ 246 | /* them to the thumbprints downloaded from the platform. */ 247 | /* If we find the thumbprint, then respond with a Status of "INV_STAT_UNCH". */ 248 | /* If the PEM file contains a new certificate, respond with a status of */ 249 | /* "INV_STAT_ADD" and include the ASCII version of the certificate in the */ 250 | /* response for that cert. */ 251 | /* */ 252 | /* The platform then adds that certificate to its list for this machine & */ 253 | /* this store. */ 254 | /* */ 255 | /* NOTE: updateList is allocated in this function and must be freed by the */ 256 | /* calling function */ 257 | /* */ 258 | /* @param - [Input] cmsItems - a list of all items the Platform knows about */ 259 | /* in this data store */ 260 | /* @param - [Input] cmsItemCount - The # of items in the list cmsItems */ 261 | /* @param - [Input] fileItemList - a list of all items the Agent knows about */ 262 | /* in this data store */ 263 | /* @param - [Output] updateList - The list of items to ADD or DELETE from */ 264 | /* the platform */ 265 | /* @return - success : 0 */ 266 | /* failure : 0 */ 267 | /* */ 268 | /* */ 269 | static int compute_inventory_update(struct InventoryCurrentItem** cmsItems, 270 | int cmsItemCount, struct PemInventoryList* fileItemList, 271 | struct InventoryUpdateList** updateList) 272 | { 273 | *updateList = calloc(1, sizeof(struct InventoryUpdateList)); 274 | struct InventoryUpdateItem* updateItem = NULL; 275 | PemInventoryItem* currentPem = NULL; 276 | bool inFile = false; 277 | bool inCms = false; 278 | 279 | for(int i = 0; i < fileItemList->item_count; ++i) 280 | { 281 | currentPem = fileItemList->items[i]; 282 | inCms = false; 283 | for(int j = 0; j < cmsItemCount; ++j) 284 | { 285 | /* if the thumbprints match, we don't have to do anything */ 286 | if(0 == strcasecmp(currentPem->thumbprint_string, cmsItems[j]->Alias)) { 287 | log_verbose("%s::%s(%d) : Alias %s is UNCHANGED", LOG_INF, currentPem->thumbprint_string); 288 | inCms = true; 289 | 290 | struct InventoryUpdateItem* updateItem = calloc(1, sizeof(*updateItem)); 291 | if (!updateItem) { 292 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 293 | break; 294 | } else { 295 | if (cmsItems[j]->Alias) { 296 | updateItem->Alias = strdup(cmsItems[j]->Alias); 297 | } else { 298 | updateItem->Alias = strdup(""); 299 | } 300 | updateItem->ItemStatus = INV_STAT_UNCH; 301 | updateItem->PrivateKeyEntry = cmsItems[j]->PrivateKeyEntry; 302 | updateItem->UseChainLevel = false; 303 | } 304 | 305 | InventoryUpdateList_add(*updateList, updateItem); 306 | break; 307 | } /* parasoft-suppress BD-RES-LEAKS "Freed by calling function" */ 308 | } 309 | 310 | if(!inCms) { 311 | log_verbose("%s::%s(%d) : Alias %s is ADDED", LOG_INF, currentPem->thumbprint_string); 312 | 313 | updateItem = calloc(1, sizeof(*updateItem)); 314 | if (!updateItem) { 315 | log_error("%s::%s(%d) : Out of memory", LOG_INF); 316 | break; 317 | } else { 318 | if (currentPem->thumbprint_string) { 319 | updateItem->Alias = strdup(currentPem->thumbprint_string); 320 | } else { 321 | updateItem->Alias = strdup(""); 322 | } 323 | updateItem->ItemStatus = INV_STAT_ADD; 324 | updateItem->PrivateKeyEntry = currentPem->has_private_key; 325 | updateItem->UseChainLevel = false; 326 | updateItem->Certificates = calloc(1, sizeof(char *)); 327 | updateItem->Certificates[0] = strdup(currentPem->cert); 328 | updateItem->Certificates_count = 1; 329 | 330 | InventoryUpdateList_add(*updateList, updateItem); 331 | } 332 | } 333 | } 334 | 335 | for(int m = 0; m < cmsItemCount; ++m) 336 | { 337 | inFile = false; 338 | for(int n = 0; n < fileItemList->item_count; ++n) { 339 | currentPem = fileItemList->items[n]; 340 | 341 | if(0 == strcasecmp(currentPem->thumbprint_string, cmsItems[m]->Alias)) { 342 | inFile = true; 343 | break; 344 | } 345 | } 346 | 347 | if(!inFile) { 348 | log_verbose("%s::%s(%d) : Alias %s is DELETED", LOG_INF, cmsItems[m]->Alias); 349 | 350 | updateItem = calloc(1, sizeof(struct InventoryUpdateItem)); /* parasoft-suppress BD-RES-LEAKS "Freed by calling function" */ 351 | if (!updateItem) { 352 | log_error("%s::%s(%d) : Out of Memory", LOG_INF); 353 | break; 354 | } else { 355 | updateItem->Alias = strdup(cmsItems[m]->Alias); 356 | updateItem->ItemStatus = INV_STAT_REM; 357 | updateItem->PrivateKeyEntry = false; 358 | updateItem->UseChainLevel = false; 359 | 360 | InventoryUpdateList_add(*updateList, updateItem); 361 | } 362 | } 363 | } 364 | 365 | return 0; 366 | } /* compute_inventory_update */ /* parasoft-suppress BD-RES-LEAKS "Freed by calling function" */ 367 | 368 | /******************************************************************************/ 369 | /*********************** GLOBAL FUNCTION DEFINITIONS **************************/ 370 | /******************************************************************************/ 371 | /** */ 372 | /* Run the control flow for an inventory job. */ 373 | /* The control flow is: */ 374 | /* 1.) Ask the platform for the Inventory Job details (aka config) */ 375 | /* 2.) Read the PEM inventory from the store requested. */ 376 | /* 3.) Compare the thumbprints in #2 to the thumbprints downloaded in */ 377 | /* #1. */ 378 | /* 4.) Inform the platform if: */ 379 | /* a.) The thumbprint is not found (Platform Deletes its cert) */ 380 | /* b.) A new thumbprint is found (Platform adds the cert) */ 381 | /* For 4b) the cert is uploaded to the platform as a naked PEM */ 382 | /* */ 383 | /* @param - [Input] : sessionToken = the session token for the Platform */ 384 | /* @return - job not canceled by platform : 0 */ 385 | /* job was canceled by platform : 1 */ 386 | /* */ 387 | int cms_job_inventory(struct SessionJob* jobInfo, char* sessionToken) 388 | { 389 | int res = 0; 390 | struct InventoryConfigResp* invConf = NULL; 391 | char* statusMessage = strdup(""); 392 | enum AgentApiResultStatus status = STAT_UNK; 393 | 394 | int returnable = 0; 395 | log_info("%s::%s(%d) : Starting inventory job %s", LOG_INF, jobInfo->JobId); 396 | 397 | res = get_inventory_config(sessionToken, jobInfo->JobId, 398 | jobInfo->ConfigurationEndpoint, &invConf); 399 | 400 | /* Validate inputs */ 401 | if (invConf) 402 | { 403 | bool failed = false; 404 | if (invConf->Job.StorePath) 405 | { 406 | /* Verify the target store isn't a directory */ 407 | if (is_directory(invConf->Job.StorePath)) 408 | { 409 | log_error("%s::%s(%d) : The store path must be a file and " 410 | "not a directory.", LOG_INF); 411 | append_linef(&statusMessage, "The store path must be a file " 412 | "and not a directory."); 413 | failed = true; 414 | } 415 | 416 | if (ConfigData->UseAgentCert) { 417 | /* Verify the target store isn't the Agent store */ 418 | if (0 == strcasecmp(ConfigData->AgentCert, invConf->Job.StorePath)) 419 | { 420 | 421 | log_warn("%s::%s(%d) : Attempting to inventory the agent" 422 | " cert store is not allowed.", LOG_INF); 423 | append_linef(&statusMessage, "Attempting to inventory the " 424 | "agent cert store is not allowed."); 425 | failed = true; 426 | } 427 | } 428 | 429 | /* Verify the target store exists */ 430 | if (!file_exists(invConf->Job.StorePath)) 431 | { 432 | log_warn("%s::%s(%d) : Attempting to inventory a certificate" 433 | " store that does not exist yet.", LOG_INF); 434 | append_linef(&statusMessage, "Attempting to inventory a " 435 | "certificate store that does not exist yet."); 436 | failed = true; 437 | } 438 | } 439 | else 440 | { 441 | log_error("%s::%s(%d) : Job doesn't contain a store to inventory.", LOG_INF); 442 | append_linef(&statusMessage, "Job doesn't contain a store to inventory."); 443 | failed = true; 444 | } 445 | /* If we failed any test above, then let the platform know about it */ 446 | if(failed) 447 | { 448 | struct CommonCompleteResp* invComp = NULL; 449 | send_inventory_job_complete(sessionToken, jobInfo->JobId, 450 | jobInfo->CompletionEndpoint, STAT_ERR, invConf->AuditId, 451 | statusMessage, &invComp); 452 | CommonCompleteResp_free(invComp); 453 | goto exit; 454 | } 455 | } 456 | else 457 | { 458 | log_error("%s::%s(%d) : No inventory configuration was returned " 459 | "from the platform", LOG_INF); 460 | goto exit; 461 | } 462 | 463 | if( (res == 0) && 464 | AgentApiResult_log(invConf->Result, &statusMessage, &status) ) 465 | { 466 | if(invConf->JobCancelled) 467 | { 468 | log_info("%s::%s(%d) : Job has been cancelled and will " 469 | "not be run", LOG_INF); 470 | returnable = 1; 471 | } 472 | else 473 | { 474 | long auditId = invConf->AuditId; 475 | log_verbose("%s::%s(%d) : Audit Id: %ld", LOG_INF, auditId); 476 | 477 | PemInventoryList* pemList = NULL; 478 | log_trace("%s::%s(%d) : Reading inventory store located at %s", 479 | LOG_INF, invConf->Job.StorePath); 480 | res = ssl_read_store_inventory(invConf->Job.StorePath, 481 | invConf->Job.StorePassword, &pemList); 482 | 483 | if(res == 0) 484 | { 485 | struct InventoryUpdateList* updateList = NULL; 486 | compute_inventory_update(invConf->Job.Inventory, 487 | invConf->Job.Inventory_count, pemList, &updateList); 488 | 489 | struct InventoryUpdateResp* updResp = NULL; 490 | res = send_inventory_update(sessionToken, jobInfo->JobId, 491 | invConf->InventoryEndpoint, updateList, &updResp); 492 | if(res == 0 && updResp) 493 | { 494 | AgentApiResult_log(updResp->Result, &statusMessage, &status); 495 | } 496 | 497 | if (updateList) 498 | { 499 | InventoryUpdateList_free(updateList); 500 | } 501 | if (updResp) 502 | { 503 | InventoryUpdateResp_free(updResp); 504 | } 505 | } 506 | else 507 | { 508 | status = STAT_ERR; 509 | append_line(&statusMessage, strerror(res)); 510 | } 511 | 512 | if (pemList) 513 | { 514 | log_trace("%s::%s(%d) : Freeing pemList containing %d items", 515 | LOG_INF, pemList->item_count); 516 | PemInventoryList_free(pemList); 517 | } 518 | 519 | struct CommonCompleteResp* invComp = NULL; 520 | res = send_inventory_job_complete(sessionToken, jobInfo->JobId, 521 | jobInfo->CompletionEndpoint, (status + 1), auditId, 522 | statusMessage, &invComp); 523 | if(res == 0 && invComp) 524 | { 525 | AgentApiResult_log(invComp->Result, NULL, NULL); 526 | } 527 | 528 | if(status >= STAT_ERR) 529 | { 530 | log_error("%s::%s(%d) : Inventory job %s failed with error: %s", 531 | LOG_INF, jobInfo->JobId, statusMessage); 532 | } 533 | else if(status == STAT_WARN) 534 | { 535 | log_warn("%s::%s(%d) : Inventory job %s completed with " 536 | "warning: %s", LOG_INF, jobInfo->JobId, statusMessage); 537 | } 538 | else 539 | { 540 | log_info("%s::%s(%d) : Inventory job %s completed successfully", 541 | LOG_INF, jobInfo->JobId); 542 | } 543 | 544 | CommonCompleteResp_free(invComp); 545 | } 546 | } 547 | 548 | exit: 549 | InventoryConfigResp_free(invConf); 550 | free(statusMessage); 551 | 552 | return returnable; 553 | } /* cms_job_inventory */ 554 | /******************************************************************************/ 555 | /******************************* END OF FILE **********************************/ 556 | /******************************************************************************/ --------------------------------------------------------------------------------