 /* secure database
  *
  * This file is part of the CRYPTO BONE Project
  * File: cryptoboned.c (cryptobone daemon)
  * Version  : 2.2 (ALL-IN-ONE)
  * License  : BSD-3-Clause
  * Date     : 12 Aug 2026
  *
  * 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-2026
  *	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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <sys/mount.h>

#include <cryptlib/cryptlib.h>
#include <bsd/string.h> 
#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 PGP_MAXBUF  2*PGP_MAXINPUT 
#define MAXLINE 200

#define SOCK_PATH "/usr/lib/cryptobone/secrets.sock"
#define MAX 512

#define DATABASEFILE "/usr/lib/cryptobone/database"
#define DATABASEBACKUP "/usr/lib/cryptobone/database.back"
#define INTERNAL_BUFFER_SIZE MAXINPUT+1024
#define MAXBUF  2*MAXINPUT 
#define KEYLEN 101

#define cryptUser CRYPT_UNUSED
#define MAXBOOTSECONDS 120


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);
       
     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 = 0;
     int  num = 0;
     unsigned char *sptr ;
     FILE *fp;

/***************************************************************************************/
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
       */

      FILE *fd; 
      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 */
            fd = popen("/bin/touch /usr/lib/cryptobone/database", "r");
            if (fd != NULL ){
                 pclose(fd);
	    }
            		 
            fd = popen("/bin/cp /usr/lib/cryptobone/database /usr/lib/cryptobone/database.back", "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;
      }

      /* OLD
      status = base64decode(input, 2*MAXBUF, &num, output, strlen(output), CRYPT_CERTTYPE_NONE);
      if (DEBUG) syslog(LOG_NOTICE,"base64: %i bytes received in input (status:%i) ",num,status);
      */

      /* 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<strlen(plaintext); x++){
          if (begin == 1){
                if (plaintext[x] != ':'){
                     key[i] = plaintext[x];
		     i++;
	        }
	        else{
	             key[i] = ' ';
		     key[i+1] = '\0';
		     strlcat(values,key,MAXINPUT);
		     i=0;
		     begin = 0;
		     strlcpy(key,"",KEYLEN);
	        }
	  }
	  if (plaintext[x] == '\n')  begin = 1;
     }
}

/***************************************************************************************/
int has_key(char key[]){
     
     int x=0;
     int i=0;
     int begin = 1;
     char testkey[KEYLEN];
     
     if (DEBUG) syslog(LOG_NOTICE,"has_key:  %s ",key);
     for (x=0; x<strlen(plaintext); x++){
          if (begin == 1){
               if (plaintext[x] != ':'){
                    testkey[i] = plaintext[x];
	            i++;
	       }
	       else{
	            testkey[i] = '\0';
		    i=0;
		    begin = 0;
	       }
	  }
	  else {
               if (strcmp(testkey,key) == 0){
                    return 1;
	       }
	       if (plaintext[x] == '\n'){
                    begin = 1;
	       }
          }
     }
     return 0;
}

/***************************************************************************************/
int get_element(char *value, char pattern[]){
     
     int x=0;
     int i=0;
     int begin = 1;
     char key[KEYLEN];
     
     for (x=0; x<strlen(plaintext); x++){
          if (begin == 1){
               if (plaintext[x] != ':'){
                    key[i] = plaintext[x];
	            i++;
	       }
	       else{
	            key[i] = '\0';
	            i=0;
	            begin = 0;
	       }
	  }
	  else {
               if (strcmp(key,pattern) == 0){
                    /* all values end with a newline */
	            if (plaintext[x] != '\n' ) {
		         value[i] = plaintext[x];
		         i++;
	            }
	            else {
	                 value[i] = '\0';
                         if (DEBUG) syslog(LOG_NOTICE,"element:%s found last byte at %i (valuelen: %i)",pattern,x, (int) strlen(value));
		         return 0;
	            }
               }
	       if (plaintext[x] == '\n'){
                    begin = 1;
	       }
          }		
     }
     value[0] = '\0';
     return 1;
}

/***************************************************************************************/
int replace_element(char newkey[], char new[]){
     
     int x=0;
     int i=0;
     int start=0;
     int begin = 1;
     char key[KEYLEN];
     
     if (DEBUG) syslog(LOG_NOTICE,"replace:  %s ",newkey);
     for (x=0; x<strlen(plaintext); x++){
          if (begin == 1){
                if (plaintext[x] != ':'){
                     key[i] = plaintext[x];
		     i++;
	        }
	        else{
	             key[i] = '\0';
		     i = 0;
		     begin = 0;
		     start = x;
	        }
	  }
	  else  {
                if (strcmp(key,newkey) == 0){
	             if (plaintext[x] == '\n'){
		          output[0] = '\0';
                          strlcat(output, plaintext, start+1);
		          /* insert new value */
			  strlcat(output, ":", MAXBUF );
			  strlcat(output, new, MAXBUF);
			  /* add from x to end of plaintext  */
			  input[0] = '\0';
			  i = 0;
			  while (x < strlen(plaintext)){
                                input[i] =  plaintext[x];
				x++;
				i++;
			  }
			  input[i] = '\0';
			  strlcat(output, input, MAXBUF);
			  if (strlen(output) < MAXINPUT) {
			       /* copy result to plaintext */
			       strlcpy(plaintext, output, MAXINPUT);
                               if (DEBUG) syslog(LOG_NOTICE,"replace: new plaintext %i bytes.", (int) strlen(plaintext));
			       aes_encrypt();
			       return 0;
			  }
		     }
		}
	        if (plaintext[x] == '\n'){
                     begin = 1;
	        }
          }		
     }
     return 1;
}


/***************************************************************************************/
int remove_element(char key[]){
     
     int x=0;
     int i=0;
     int start=0;
     int begin = 1;
     char testkey[KEYLEN];
     
     if (DEBUG) syslog(LOG_NOTICE,"remove:  %s ",key);
     for (x=0; x<strlen(plaintext); x++){
          if (begin == 1){
                if (plaintext[x] != ':'){
                     testkey[i] = plaintext[x];
		     i++;
	        }
	        else{
	             testkey[i] = '\0';
		     i = 0;
		     begin = 0;
		     start = x;
	        }
	  }
	  else  {
                if (strcmp(testkey,key) == 0){
		     if (plaintext[x] == '\n'){
		          output[0] = '\0';
		          start = start - strlen(key);
                          strlcat(output, plaintext, start+1);
			  /* add from x to end of plaintext  */
			  input[0] = '\0';
			  i = 0;
                          /* skip newline */
			  x++;
			  while (x < strlen(plaintext)){
                                input[i] =  plaintext[x];
				x++;
				i++;
			  }
			  input[i] = '\0';
			  strlcat(output, input, MAXBUF);
			  /* copy result to plaintext */
			  strlcpy(plaintext, output, MAXINPUT);
                          if (DEBUG) syslog(LOG_NOTICE,"remove: new plaintext %i bytes.", (int) strlen(plaintext));
			  aes_encrypt();
                          if (DEBUG) syslog(LOG_NOTICE,"remove: database encrypted.");
			  return 0;
		     }
		}
	        if (plaintext[x] == '\n'){
                     begin = 1;
	        }
          }		
     }
     return 1;
}



/******************************************************************/
int checkparent(int p) {

     /* check if the parent process is a program stored in /usr/lib/cryptobone/
     * or /etc/init.d/cryptoboned
     * return 0 if ok and -1 if not
     */

     char pscommand[100];
     char line[100];
     int parentid = 0;

     snprintf(pscommand, 100, "/bin/ps -oppid %d", p);
     FILE* fd = popen(pscommand, "r");
     if ( fd != NULL ){
          while (fgets(line,sizeof(line),fd) != NULL) {
	      if (strstr(line,"PPID") != 0){
                    parentid = 1;
	      }
	      else {
	           if (parentid == 1){
	               parentid = atoi(line);
		   }    
              }		   
	  }
          pclose(fd);
	  
	  if (parentid > 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 = 0;
     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 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 */

               /* numbytes = read(connection, buffer, 4*MAXWORD); */
    
               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<bytesread; j++) {
                                   buffer[numbytes+j] = inbuf[j];
                              }
                              numbytes += bytesread;
                              count++;
                         }
                    }
                    else {
                         POLLING = 0;
                    }
               }

               if (DEBUG) syslog(LOG_NOTICE,"buffer returns (%i count ) numbytes: %i \n: ",count,numbytes);

               if (numbytes > 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 2.2\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 this_is_boottime(){
     
     char content[5];

     /* read bootswitch file */
     fp = fopen("/usr/lib/cryptobone/bootswitch","r");
     if (!fp){
           return -1;
     }
     else {
          sptr = fgets(content,5,fp);
          fclose(fp);
	  if ((sptr != NULL) && (atoi(content) < MAXBOOTSECONDS)) {
	        /* it's boot time */
	        return 0;
	  }	
     }
     return -1;
}

/*****MAIN***************************************************************/

        /* try to read the password if this is boot time */
        if (this_is_boottime() == 0) {
             fp = fopen("/usr/lib/cryptobone/keys/master.key","r");
             if (!fp){
                  printf ("Error: master key is not readable");
	          fclose(fp);
	          exit(1);
             }
             else {
                  sptr = fgets(password,41,fp);
	          password[41] = '\0';
                  fclose(fp);
             }
        }
	else {
	     /* illegal start after boot time */
             printf ("Error: boot time has elapsed. Terminating cryptoboned ...");
             exit(6);
	}

	/* 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 ("cryptobone_daemon", LOG_PID, LOG_DAEMON);

        /* log successful start of daemon */
	syslog(LOG_NOTICE,"cryptobone daemon started by user %d. Ready for connections.",getuid());

        /* prepare the encyption engine */ 
	status = cryptInit();
        
        if( status != CRYPT_OK ){
              /* cryptlib initialisation failed */;
              syslog(LOG_NOTICE,"CryptLib initialisation failed.");
	      exit(2);
        }
	
	/* 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;
}
