/* secure database * * This file is part of the CRYPTO BONE Project * File: cryptoboneexternd.c (external cryptobone daemon) * Version : 2.0 (EXTERNAL DEVICE) * License : BSD-3 Clause * Date : 21 Feb 2025 * * Summary: * This module implements the encrypted database for the Crypto Bone. * Each entry has the following form: * key:secretvalue\n * Keys are unique and the database must not be empty. * The maximum key length is 100 characters and the character : is not allowed * inside a key. * If the secret value contains newline characters, it must be base64-encoded * before it is handed over to the database. * * Copyright (c) 2015-2025 * Ralf Senderek, Ireland. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Ralf Senderek. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *****************************************************/ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "armor.h" #define DEBUG 0 #define MAXINPUT 250000 #define MAXWORD 66668 /* max number of bytes for a parameter through socket */ #define PGP_MAXINPUT 50001 #define MAX 512 #define BASEDIRECTORY "/usr/lib/cryptobone/ext" #define SOCK_PATH BASEDIRECTORY"/secrets.sock" #define DATABASEFILE BASEDIRECTORY"/database" #define DATABASEBACKUP DATABASEFILE".back" #define INTERNAL_BUFFER_SIZE MAXINPUT+1024 #define MAXBUF 2*MAXINPUT #define KEYLEN 101 #define cryptUser CRYPT_UNUSED #define MAXBOOTSECONDS 120 unsigned char plaintext[MAXINPUT] ; unsigned char input[MAXBUF]; unsigned char output[MAXBUF*4/3]; /* output holds base64 encoded data and exceeds input */ unsigned char password[KEYLEN]; int passwordLength = 0; int status; int num; unsigned char *sptr = NULL; FILE *fp; #define PGP_MAXBUF 2*PGP_MAXINPUT #define MAXLINE 200 unsigned char FileName[KEYLEN]; /***************************************************************************************/ int b64encode(BYTE *dest, int *destBytes , BYTE *src, const int len){ /* * the base64encode function does not handle padding. * one or two bytes have to be processed separately if (len % 3 != 0) . * The number of encoded bytes has to be updated accordingly. * We assume that len is greater than 10 characters */ unsigned char b64char(int position){ char CODES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if ((position >= 0) && (position < 64)){ return CODES[position]; } return '='; } unsigned char c1, c2, c3; unsigned char c4 = '='; unsigned int byte1, byte2 = 0; if ( len < 10 ) { return -1; } if ( (len % 3) != 0 ) { if ( (len % 3) == 2 ) { byte1 = src[len-2]; byte2 = src[len-1]; /* encode two extra bytes */ status = base64encode(dest, 2*PGP_MAXBUF, destBytes, src, len-2, CRYPT_CERTTYPE_NONE); c1 = b64char( (byte1 >> 2) & 0x3F ); c2 = b64char( ((byte1 << 4) | (byte2 >> 4)) & 0x3F ); c3 = b64char( (byte2 << 2) & 0x3F ); } if ( (len % 3) == 1 ) { /* encode one extra byte */ byte1 = src[len-1]; status = base64encode(dest, 2*PGP_MAXBUF, destBytes, src, len-1, CRYPT_CERTTYPE_NONE); c1 = b64char( (byte1 >> 2) & 0x3F ); c2 = b64char( (byte1 << 4) & 0x3F ); c3 = '='; } dest[*destBytes] = c1 ; *destBytes += 1; dest[*destBytes] = c2 ; *destBytes += 1; dest[*destBytes] = c3 ; *destBytes += 1; dest[*destBytes] = c4 ; *destBytes += 1; dest[*destBytes] = '\0'; } else { /* encoding without padding */ status = base64encode(dest, 2*PGP_MAXBUF, destBytes, src, len, CRYPT_CERTTYPE_NONE); dest[*destBytes] = '\0'; } if ( *destBytes == 0 ){ syslog(LOG_NOTICE,"Error: base64 encoding failed."); return 2; } return status; } /***************************************************************************************/ int b64decode(BYTE *dest, int *destBytes , BYTE *src, const int len){ /* * the base64decode function does not handle padding, so the last * four bytes have to be processed separately. * The number of decoded bytes has to be updated accordingly. * We assume that len is greater than 14 characters */ int position(char c){ char CODES[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; for (int i=0; i<64; i++){ if ( c == CODES[i] ) return i; } return -1; } unsigned int c1, c2, c3 = 0; if ( len < 14 ) { return -1; } if ( src[len-1] == '=' ) { c1 = position( src[len-4] ) ; c2 = position( src[len-3] ) ; c3 = position( src[len-2] ) ; /* decode the source minus the last 4 bytes */ status = base64decode(dest, 2*PGP_MAXBUF, destBytes, src, len-4, CRYPT_CERTTYPE_NONE); if ( src[len-2] == '=' ){ /* one byte */ dest[*destBytes] = ( ( ( c1 << 2 ) | ( c2 >> 4 ) ) & 0xFF ); *destBytes += 1; } else { /* two bytes */ dest[*destBytes] = ( ( ( c1 << 2 ) | ( c2 >> 4 ) ) & 0xFF ); *destBytes += 1; dest[*destBytes] = (unsigned char) ( ( ( c2 << 4 ) | ( c3 >> 2) ) & 0xFF ); *destBytes += 1; } dest[*destBytes] = '\0'; } else { /* decoding without padding */ status = base64decode(dest, 2*PGP_MAXBUF, destBytes, src, len, CRYPT_CERTTYPE_NONE); } if ( *destBytes == 0 ){ syslog(LOG_NOTICE,"Error: base64 decoding failed."); return 2; } return status; } /***************************************************************************************/ int get_masterkey(){ if (strlen(password) == 40) { return 0; } return -1; } /***************************************************************************************/ int aes_encrypt(){ /* * Reads from plaintext and writes the cryptogram to input and * base64-encoded to output. * output is stored in the database file */ int newfd; if (get_masterkey() != 0){ syslog(LOG_NOTICE,"Error: no key, encryption failed."); return 4; } if (strlen(plaintext) >= MAXINPUT){ return 2; } int bytesCopied = 0; int plaintextLength = strlen(plaintext); if (plaintextLength == 0){ syslog(LOG_NOTICE,"Error: plaintext is empty."); return 3; } CRYPT_ENVELOPE cryptEnvelope; status = cryptCreateEnvelope( &cryptEnvelope, cryptUser, CRYPT_FORMAT_CRYPTLIB ); if( status != CRYPT_OK ){ syslog(LOG_NOTICE,"Error: Cryptlib enveloping."); return 3; } /* set internal buffer size */ status = cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, INTERNAL_BUFFER_SIZE ); /* Add the password */ passwordLength = strlen(password); if (passwordLength < 20){ syslog(LOG_NOTICE,"Error: invalid key, encryption failed."); return 3; } if (passwordLength > 64){ syslog(LOG_NOTICE,"Error: invalid key, encryption failed."); return 3; } if (DEBUG) syslog(LOG_NOTICE,"Using password of length %i\n",passwordLength); status = cryptSetAttributeString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, password, passwordLength ); cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, plaintextLength ); status = cryptPushData( cryptEnvelope, plaintext, plaintextLength, &bytesCopied ); status = cryptFlushData( cryptEnvelope ); /* create cryptogram in buffer input */ status = cryptPopData( cryptEnvelope, input, MAXBUF, &bytesCopied ); if (DEBUG) syslog(LOG_NOTICE,"encrypted Buffer has %i bytes\n", bytesCopied); if (status == CRYPT_OK){ status = base64encode(output, 2*MAXBUF, &num, input, bytesCopied, CRYPT_CERTTYPE_NONE); if (DEBUG) syslog(LOG_NOTICE,"base64 input %i bytes output %i bytes \n", bytesCopied, num); /* set '\0' at pos num in output */ output[num] = '\0'; /* make backup of the database file */ FILE* fd = popen("/bin/cp "DATABASEFILE" "DATABASEBACKUP, "r"); if (fd != NULL ){ pclose(fd); if (DEBUG) syslog (LOG_NOTICE,"backup created"); newfd = open(DATABASEBACKUP,O_RDWR); fchmod(newfd,S_IRUSR | S_IWUSR); close(newfd); } /* write result to file */ fp = fopen(DATABASEFILE,"w"); if (!fp){ syslog(LOG_NOTICE,"Error: database is not available"); return 5; } else { fputs(output,fp); fclose(fp); /* change file permissions to 0600 */ newfd = open(DATABASEFILE,O_RDWR); fchmod(newfd,S_IRUSR | S_IWUSR); close(newfd); } } status = cryptDestroyEnvelope( cryptEnvelope ); if( status != CRYPT_OK ){ syslog(LOG_NOTICE,"Error: Envelope cannot be destroyed."); return 4; } return 0; } /***************************************************************************************/ int aes_decrypt(){ /* * reads base64 cryptogram from database file (output) decodes to input and * writes the decrypted message to plaintext */ if (get_masterkey() != 0){ syslog(LOG_NOTICE,"Error: no key, decryption failed."); return 4; } int bytesCopied = 0; CRYPT_ENVELOPE cryptEnvelope; /* read database file */ fp = fopen(DATABASEFILE,"r"); if (!fp){ syslog(LOG_NOTICE,"Error: database is not readable."); return 1; } else { sptr = fgets(output,MAXBUF*4/3,fp); fclose(fp); } /* decodes output and writes the result to input */ status = b64decode(input, &num, output, strlen(output)); if (DEBUG) syslog(LOG_NOTICE,"base64: %i bytes received in input (status: %i)",num, status); if ( num == 0 ){ if (DEBUG) syslog(LOG_NOTICE,"Error: base64 decoding failed."); return 1; } /* decrypt */ status = cryptCreateEnvelope( &cryptEnvelope, cryptUser, CRYPT_FORMAT_AUTO ); /* set internal buffer size */ status = cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, INTERNAL_BUFFER_SIZE ); status = cryptPushData( cryptEnvelope, input, MAXBUF, &bytesCopied ); /* Add the password */ passwordLength = strlen(password); if (passwordLength < 20){ syslog(LOG_NOTICE,"Error: invalid key, encryption failed."); return 3; } if (passwordLength > 64){ syslog(LOG_NOTICE,"Error: invalid key, encryption failed."); return 3; } if (DEBUG) syslog(LOG_NOTICE,"Using password of length %i\n",passwordLength); cryptSetAttributeString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, password, passwordLength ); status = cryptFlushData( cryptEnvelope ); status = cryptPopData( cryptEnvelope, plaintext, MAXBUF, &bytesCopied ); if (status != CRYPT_OK){ //puts("Error: decryption failed."); if (status == -32){ syslog(LOG_NOTICE,"Error: database is corrupt."); } syslog(LOG_NOTICE,"Error: decryption failed status %i ",status); status = cryptDestroyEnvelope( cryptEnvelope ); return 5; } status = cryptDestroyEnvelope( cryptEnvelope ); /* DEBUG write the database to /root/debug */ if (DEBUG) { fp = fopen("/root/debug","w"); if (!fp){ return 5; } else { fputs(plaintext,fp); fclose(fp); } } return 0; } /***************************************************************************************/ void get_keys(char *values){ int x=0; int i=0; int begin = 1; char key[KEYLEN]; strlcpy(values,"",KEYLEN); strlcpy(key,"",KEYLEN); if (DEBUG) syslog(LOG_NOTICE,"get_keys: plaintext %i bytes.", (int) strlen(plaintext)); for (x=0; x 1){ snprintf(pscommand, 100, "/bin/ps -ocmd %d", parentid); FILE* fd = popen(pscommand, "r"); if (fd != NULL ){ while (fgets(line,sizeof(line),fd) != NULL) { /* /usr/lib/cryptobone is now the only place to go */ if (strstr(line,"/usr/lib/cryptobone/") != 0){ pclose(fd); /* further checking omitted, is cbcontrol started via sudo? */ return 0; } if (strstr(line,"/usr/lib64/cryptobone/") != 0){ pclose(fd); /* further checking omitted, is cbcontrol started via sudo? */ return 0; } } pclose(fd); } } } return -1; } /*************************************************************************/ void write_sock(int conn, char *val) { /* max MAXINPUT Bytes output through socket */ int numbytes = 0; numbytes = strlen(val); if ((numbytes > 0) && (numbytes < MAXINPUT)){ status = write (conn, val, numbytes); } } /*************************************************************************/ int connection_handler( int connection ){ int ucred_length = sizeof(struct ucred); struct ucred credentials; int numbytes = 0; int ret = 0; int MAXBYTES = 8192; char inbuf[MAXBYTES+5000]; /* 33*MAXBYTES > 4*MAXWORD */ char buffer[4*MAXWORD+1024+MAXBYTES]; char value[MAXINPUT]; int i = 0; int j = 0; unsigned char words[4][MAXWORD+1]; unsigned int x = 0; int POLLING = 1; int count = 0; int bytesread = 0; /* check if connection is legitimate */ ret = getsockopt(connection, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length); if ( ret > -1 ) { if (DEBUG){ syslog(LOG_NOTICE,"PID: %d",credentials.pid); syslog(LOG_NOTICE,"UID: %d",credentials.uid); } /* exit if not a root process */ if ( credentials.uid != 0 ) { syslog(LOG_NOTICE,"Alert: UID: %d tried to use the external cryptobone daemon!",credentials.uid); return -1; } if (checkparent(credentials.pid) == 0){ /* syslog(LOG_NOTICE,"LEGITIMATE ACCESS BY cbcontrol"); */ /* now analyse the socket input and start secrets function */ /* init buffer */ for (j=0; j < (4*MAXWORD)+1; j++){ buffer[j] = '\0'; } j = 0; /* read max 4*MAXWORD bytes from socket */ bytesread = 0; numbytes = 0; POLLING = 1; while (POLLING > 0){ /* blocks until input is received */ if (numbytes < (4*MAXWORD - MAXBYTES)) { bytesread = read(connection, inbuf, MAXBYTES); if (DEBUG) syslog(LOG_NOTICE,"input buffer read (%i bytes) ",bytesread); if (bytesread <= 0) { POLLING = 0; } else { /* append inbuf to buffer */ if (DEBUG) syslog(LOG_NOTICE,"append input buffer (%i bytes) ",bytesread); for (j=0; j 0){ buffer[numbytes] = '\0'; } if (DEBUG) syslog(LOG_NOTICE,"buffer (length %i bytes) numbytes: %i \n: ", (int) strlen(buffer), numbytes); /* analyse buffer input from socket, split into words */ /* * since words are limited to 66668 bytes, no secret value can * be larger than the decoded value of 50001 bytes */ /* init words */ for (i=0; i<4; i++){ for (j=0; j <= MAXWORD; j++){ words[i][j] = '\0'; } } i = 0; j = 0; while ((x < numbytes) && ( i<4 )) { if ((buffer[x] != ' ') && (buffer[x] != ' ') && (buffer[x] != '\n')) { if (j < MAXWORD) { /* skipped : perform input validation A_Z@.a-z0-9 */ words[i][j] = buffer[x]; j++; } else { /* full, force end of word */ words[i][j] = '\0'; i++; j=0; /* skip rest of word until white space is hit */ while ( (x < numbytes) && ((buffer[x] != ' ') && (buffer[x] != ' ')) ) { x++; } } x++; } else { words[i][j] = '\0'; i++; j = 0; x++; /* skip white space */ while ( (x < numbytes) && ((buffer[x] == ' ') || (buffer[x] == ' ')) ) { x++; } } } if (DEBUG) syslog(LOG_NOTICE,"Words %i %i %i %i \n: ", (int) strlen(words[0]), (int) strlen(words[1]), (int) strlen(words[2]), (int) strlen(words[3])); /* now call SECRETS.c functions */ if (strcmp(words[0],"init") == 0) { /* do not overwrite an existing database file */ fp = fopen(DATABASEFILE,"r"); if (!fp){ strlcpy(plaintext, "cryptobone:version 1.6\n", MAXINPUT); aes_encrypt(); write_sock(connection,"\nThis is the database:\n"); write_sock(connection,plaintext); } else { syslog(LOG_NOTICE,"Error (init): database file exists."); fclose(fp); } } if (strstr(words[0],"all-keys") != 0) { get_keys(output); write_sock(connection,output); } if (strstr(words[0],"write") != 0){ if (strlen(words[2]) > 0){ /* store_element key:value\n */ /* does not overwrite, if key exists */ if (has_key(words[1]) == 0) { numbytes = strlen(plaintext) + 2 + strlen(words[1]) + strlen(words[2]); if (DEBUG) syslog(LOG_NOTICE,"try writing to database %d bytes (%s)\n",numbytes,words[1]); if (numbytes < MAXINPUT){ strlcat(plaintext, words[1], MAXINPUT); strlcat(plaintext, ":", MAXINPUT); strlcat(plaintext, words[2], MAXINPUT); strlcat(plaintext, "\n", MAXINPUT); aes_encrypt(); if (DEBUG) syslog(LOG_NOTICE,"%d bytes written (%s)\n",numbytes,words[1]); write_sock(connection,"OK"); } else write_sock(connection,"FAILED"); } else write_sock(connection,"SKIPPED"); } } if (strstr(words[0],"get-element") != 0){ if (strlen(words[1]) > 0){ /* _element(); */ get_element(value, words[1]); write_sock(connection,value); } } if (strstr(words[0],"replace") != 0){ if (strlen(words[2]) > 0){ /* key, newvalue */ /* if key does not exist, create entry */ if (has_key(words[1]) == 0) { numbytes = strlen(plaintext) + 2 + strlen(words[1]) + strlen(words[2]); if (DEBUG) syslog(LOG_NOTICE,"replace: try writing to database %d bytes (%s)\n",numbytes,words[1]); if (numbytes < MAXINPUT){ strlcat(plaintext, words[1], MAXINPUT); strlcat(plaintext, ":", MAXINPUT); strlcat(plaintext, words[2], MAXINPUT); strlcat(plaintext, "\n", MAXINPUT); aes_encrypt(); if (DEBUG) syslog(LOG_NOTICE,"replace NEW: %i bytes written (%s)\n",numbytes,words[1]); write_sock(connection,"OK"); } else { if (DEBUG) syslog(LOG_NOTICE,"ERROR:replace: writing %i bytes to %s\n",numbytes,words[1]); write_sock(connection,"FAILED"); } } else { if (DEBUG) syslog(LOG_NOTICE,"replace: calling function replace_element" ); ret = replace_element(words[1], words[2]); if (ret == 0) write_sock(connection,"OK"); else write_sock(connection,"FAILED"); } } } if (strstr(words[0],"remove") != 0){ if (strlen(words[1]) > 0){ /* key */ ret = remove_element(words[1]); if (ret == 0) write_sock(connection,"OK"); else write_sock(connection,"FAILED"); } } /* end SECRETS.c functions */ } } else { /* no credentials do nothing */ syslog(LOG_NOTICE,"no socket options available"); } return 0; } /*************************************************************************/ void cleanup(int sig){ syslog(LOG_NOTICE,"cleaning up ... signal %i shutdown.",sig); int i = 0; /* ZEROIZE password and plaintext */ for (i=0; i < KEYLEN; i++){ password[i] = '\0'; } for (i=0; i < MAXINPUT; i++){ plaintext[i] = '\0'; } syslog(LOG_NOTICE,"cleaning up ... zeroized memory."); unlink (SOCK_PATH); int status = cryptEnd(); if( status != CRYPT_OK ){ /* cryptlib shutdown failed */; syslog(LOG_NOTICE,"Cryptlib failed to shut down. Some sensible data may remain.\n"); } syslog(LOG_NOTICE,"cleaning up ... done."); closelog(); exit(0); } /*************************************************************************/ int hash_checked(unsigned char input[], char storedhashvalue[]){ /* returns 1, if stored hash matches sha256(input) */ int length = strlen(input); CRYPT_CONTEXT hashContext; unsigned char hash[CRYPT_MAX_HASHSIZE]; /* bytes */ unsigned char hashhexvalue[2*CRYPT_MAX_HASHSIZE]; /* hexstring */ int hashLength = 0; const char hex[16] = "0123456789abcdef"; unsigned int byte = 0; unsigned int j = 0; int match = 1; if (DEBUG) syslog(LOG_NOTICE,"in:%s [%i] hash:%s [%i]",input, (int) strlen(input),storedhashvalue, (int) strlen(storedhashvalue)); if ( strlen(input) > 0 ) { status = cryptCreateContext( &hashContext, cryptUser, CRYPT_ALGO_SHA256 ); if( status != CRYPT_OK ){ syslog(LOG_NOTICE,"error: crypt context"); return -1; } cryptEncrypt( hashContext, input, length ); cryptEncrypt( hashContext, "", 0 ); status = cryptGetAttributeString( hashContext, CRYPT_CTXINFO_HASHVALUE, hash, &hashLength ); if (DEBUG) syslog(LOG_NOTICE,"new hash len=%i ",hashLength); /*convert hash to hex string */ for(int i=0; i> 4) & 0x0F]; hashhexvalue[j+1] = hex[byte & 0x0F]; j += 2; } hashhexvalue[j] = '\0'; if (DEBUG) syslog(LOG_NOTICE,"hex hash %s [%i]",hashhexvalue, (int) strlen(hashhexvalue)); status = cryptDestroyContext( hashContext ); /* constant time compare */ match = 1; for (int i=0; i < 64; i++) { if (storedhashvalue[i] != hashhexvalue[i] ) { /* strings differ */ match = 0; } } return match; } return -1; } /*************************************************************************/ int key_connection_handler( int connection, char hashvalue[], int *success){ int ucred_length = sizeof(struct ucred); struct ucred credentials; int ret = 0; int MAXBYTES = 8192; char buffer[MAXBYTES]; int POLLING = 1; unsigned char value[MAX]; int keybytesread = 0; /* check if connection is legitimate */ ret = getsockopt(connection, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length); if ( ret > -1 ) { if (DEBUG){ syslog(LOG_NOTICE,"PID: %d",credentials.pid); syslog(LOG_NOTICE,"UID: %d",credentials.uid); } /* exit if not a root process */ if ( credentials.uid != 0 ) { syslog(LOG_NOTICE,"Alert: UID: %d tried to use the external cryptobone daemon!",credentials.uid); return -1; } /* syslog(LOG_NOTICE,"LEGITIMATE ACCESS BY cbcontrol"); */ /* read 64 bytes from socket */ POLLING = 1; while (POLLING > 0){ keybytesread = read(connection, buffer, MAX); if (DEBUG) syslog(LOG_NOTICE,"key read bytes: [%i]",keybytesread); if (keybytesread > 0) { /* copy first 64 bytes from buffer */ strlcpy(value,buffer,65); value[keybytesread-1] = '\0'; } if (keybytesread <= 0) { POLLING = 0; } } if (DEBUG) syslog(LOG_NOTICE,"key read value=%s [%i]",value, (int) strlen(value)); ret = hash_checked(value,hashvalue); if (ret == 1) { strlcpy(password,value,41); *success = 1; return 0; } } *success = 0; return 0; } /*************************************************************************/ int main(void) { struct sockaddr_un address, client; int sock = 0; /* my socket */ int connection = 0; /* client's socket */ int ret = 0; socklen_t address_length = sizeof(struct sockaddr_un); int keyavailable = 0; char storedhash[66]; /* signal processing */ signal(SIGTERM,cleanup); umask(0177); sock = socket(AF_UNIX, SOCK_STREAM, 0); if ( sock < 0 ) { /* error */ printf("Error: Cannot create socket.\n"); exit(1); } unlink (SOCK_PATH); memset(&address, 0, sizeof(struct sockaddr_un)); memset(&client, 0, sizeof(struct sockaddr_un)); address.sun_family = AF_UNIX; client.sun_family = AF_UNIX; snprintf(address.sun_path, strlen(SOCK_PATH)+1, SOCK_PATH); ret = bind(sock, (struct sockaddr *) &address, sizeof(struct sockaddr_un)); if ( ret != 0 ) { /* error */ printf( "Error: Cannot bind to socket.\n"); exit(1); } /* listen for exactly one connection */ ret = listen (sock, 1); if ( ret != 0 ) { /* error */ printf( "Error: Listening on socket failed.\n"); exit(1); } /* Our process ID and Session ID */ pid_t pid, sid; /* Fork off the parent process */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } /* Exit the parent process. */ if (pid > 0) { exit(EXIT_SUCCESS); } /* Open any logs here */ /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { /* Log the failure */ exit(EXIT_FAILURE); } if ((chdir("/")) < 0) { /* Log the failure */ exit(EXIT_FAILURE); } /* Close out the standard file descriptors */ close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); /* NO TERMINAL AFTER THIS POINT */ /* Daemon-specific initialization goes here */ openlog ("cryptoboneextern_daemon", LOG_PID, LOG_DAEMON); /* log successful start of daemon */ syslog(LOG_NOTICE,"external cryptobone daemon started by user %d. Ready for connections.",getuid()); /* read the master key's stored hash value */ fp = fopen(BASEDIRECTORY"/masterkey.hash","r"); if (!fp){ syslog(LOG_NOTICE,"Master key hash is missing."); exit(2); } else { sptr = fgets(storedhash,65,fp); fclose(fp); storedhash[64] = '\0'; if (DEBUG) syslog(LOG_NOTICE,"stored hash value=%s len=%i",storedhash, (int) strlen(storedhash)); } /* prepare the encryption engine */ status = cryptInit(); if( status != CRYPT_OK ){ /* cryptlib initialisation failed */; syslog(LOG_NOTICE,"CryptLib initialisation failed."); exit(2); } /* loop until the master password has arrived via the socket */ syslog(LOG_NOTICE,"waiting for a master key ..."); while ( keyavailable == 0 ) { /* wait for connection */ connection = accept(sock,(struct sockaddr *) &client, &address_length); if ( connection > -1){ if (DEBUG) syslog(LOG_NOTICE, "Master Key Connected."); key_connection_handler(connection, storedhash, &keyavailable); close(connection); } else { syslog(LOG_NOTICE,"failed socket connection [%i] while waiting ...",connection); syslog(LOG_NOTICE,"socket error: [%s]",strerror(errno)); } syslog(LOG_NOTICE,"master key available: %i",keyavailable); } syslog(LOG_NOTICE,"Master key is active"); /* check if the database exists, create a new one if not */ FILE *fptr = fopen(DATABASEFILE, "r"); if (! fptr) { syslog(LOG_NOTICE,"database INITIALISATION necessary!"); strlcpy(plaintext, "cryptobone:version 2.0\n", MAXINPUT); aes_encrypt(); } else { syslog(LOG_NOTICE,"database exists!"); fclose(fptr); } /* restore the database */ if (aes_decrypt() == 0){ syslog(LOG_NOTICE,"database decrypted successfully!"); } else { syslog(LOG_NOTICE,"Cannot decrypt cryptobone database!"); status = cryptEnd(); if( status != CRYPT_OK ){ /* cryptlib shutdown failed */; syslog(LOG_NOTICE,"Cryptlib failed to shut down. Some sensible data may remain.\n"); } closelog(); exit(2); } /* cryptAddRandom does NOT WORK in a daemon !!! * cryptAddRandom( NULL, CRYPT_RANDOM_SLOWPOLL ); */ /* The Big Loop */ while (1) { /* wait for connection */ connection = accept(sock,(struct sockaddr *) &client, &address_length); if ( connection > -1){ /* syslog(LOG_NOTICE, "Connected."); */ connection_handler(connection); close(connection); } } cleanup(9); return 0; }