[pisa-src] r1240 - in trunk/community-operator: Makefile.am co_client.c

  • From: Jan Marten <jan.marten@xxxxxxxxxxxxxx>
  • To: pisa-src@xxxxxxxxxxxxx
  • Date: Mon, 19 Oct 2009 22:47:11 +0200

Author: marten
Date: Mon Oct 19 22:47:11 2009
New Revision: 1240

Log:
Community-Operator client stores received certificates to a directory and 
checks periodically if a new certificate is required

Modified:
   trunk/community-operator/Makefile.am
   trunk/community-operator/co_client.c

Modified: trunk/community-operator/Makefile.am
==============================================================================
--- trunk/community-operator/Makefile.am        Mon Oct 19 21:51:52 2009        
(r1239)
+++ trunk/community-operator/Makefile.am        Mon Oct 19 22:47:11 2009        
(r1240)
@@ -20,6 +20,7 @@
 INCLUDES += -I@PISA_HIPL_SRCDIR@/libhiptool
 INCLUDES += -I@PISA_HIPL_SRCDIR@/libdht
 INCLUDES += -I@PISA_HIPL_SRCDIR@/opendht
+INCLUDES += -I@PISA_HIPL_SRCDIR@/firewall
 INCLUDES += -I@PISA_HIPL_SRCDIR@/hipd
 INCLUDES += -I@PISA_HIPL_SRCDIR@/i3/i3_client
 INCLUDES += -I@PISA_HIPL_SRCDIR@/pjproject/pjnath/include
@@ -33,6 +34,13 @@
 endif
 co_server_SOURCES = co_server.c hipl.c
 
+if PISA_WITH_HIPL
+# TODO correct linking
+# pisa_split_cert function is needed in co_client.c
+# Do not know any better way so far.
+  co_client_LDADD = @PISA_HIPL_SRCDIR@/firewall/pisa_cert.o
+endif
+
 co_client_SOURCES = co_client.c
 
 include_HEADERS = hipl.h

Modified: trunk/community-operator/co_client.c
==============================================================================
--- trunk/community-operator/co_client.c        Mon Oct 19 21:51:52 2009        
(r1239)
+++ trunk/community-operator/co_client.c        Mon Oct 19 22:47:11 2009        
(r1240)
@@ -3,38 +3,60 @@
  * All rights reserved.
  */
 
-#include <errno.h>
-#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "global.h"
+
+#define __USE_XOPEN
+#include <time.h>
 
 #include "co_common_packets.h"
 #include "config.h"
 #include "debug.h"
 
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#ifdef CONFIG_PISA_WITH_HIPL
+# include "pisa_cert.h"        /* in firewall under the hipl source tree */
+#endif /* CONFIG_PISA_WITH_HIPL */
+
+#define CERT_FILENAME_FORMAT "%Y%m%d%H%M%S"
 
 #define PATH_BUFFER 1000
 
+static int check_certs_dir(void);
+static void co_client_check_certs(int signal);
 static void co_client_init(void);
+static void co_client_main(void);
+static void co_client_quit(int signal);
+static int copy_cert(const char * source, char * destination);
 static int create_client_socket(struct sockaddr_in6 *srv_addr);
+static int is_valid_cert(const char *filename);
 static void print_deny_reason(int reason);
 static int receive_certificate(char **cert);
+static int retrieve_new_cert(void);
 static int request_cert(void);
-static int write_cert_file(char *cert, const char *filename);
+static int write_cert_file(const char *cert, const char *filename);
+#ifdef CONFIG_PISA_WITH_HIPL
+static void save_cert_to_certs_dir(const char * cert);
+#endif /* CONFIG_PISA_WITH_HIPL*/
 
 typedef struct co_client_conf {
        int retries;
        char srv_hit[INET6_ADDRSTRLEN];
        int port;
        char cert_filename[PATH_BUFFER];
+       int check_interval;
+       char certs_dir[PATH_BUFFER];
+       int save_certs;
 } co_client_conf;
 
 co_client_conf co_client_config;
 static int sock;
+char last_used_cert[PATH_BUFFER];
 
 /**
  * Write a certificate to a file.
@@ -43,7 +65,7 @@
  * @param filename the filename (file will be overwritten)
  * @return 0 on success, -1 otherwise
  */
-static int write_cert_file(char *cert,const char *filename)
+static int write_cert_file(const char *cert,const char *filename)
 {
        FILE *f;
 
@@ -191,6 +213,73 @@
 }
 
 /**
+ * Terminate community-operator client by receiving signal
+ * @param signal signal quit code
+ */
+static void co_client_quit(int signal)
+{
+       PISA_INFO("Shutting down Community-Operator client...\n");
+       pisa_cfg_cleanup();
+       exit(EXIT_SUCCESS);
+}
+
+/**
+ * Main loop
+ */
+static void co_client_main(void)
+{
+       select(0,NULL,NULL,NULL,NULL);
+}
+
+/**
+ * Copies cert file to destination.
+ * @param source Source filename
+ * @param destination Destination filename
+ * @return 1 -> success
+ * @return 0 -> error
+ */
+static int copy_cert(const char * source, char * destination)
+{
+       FILE *from, *to;
+       char ch;
+
+       if ((from = fopen(source, "r")) == NULL) {
+               perror("open");
+               return 0;
+       }
+
+       if ((to = fopen(destination, "w")) == NULL) {
+               perror("open");
+               return 0;
+       }
+
+       while (!feof(from)) {
+               ch = fgetc(from);
+               if (ferror(from)) {
+                       PISA_ERROR("Error reading source file.\n");
+                       return 0;
+               }
+               if (!feof(from))
+                       fputc(ch, to);
+               if (ferror(to)) {
+                       PISA_ERROR("Error writing destination file.\n");
+                       return 0;
+               }
+       }
+
+       if (fclose(from) == EOF) {
+               PISA_ERROR("Error closing source file.\n");
+               return 0;
+       }
+
+       if (fclose(to) == EOF) {
+               PISA_ERROR("Error closing destination file.\n");
+               return 0;
+       }
+       return 1;
+}
+
+/**
  * Reads the settings from the config file.
  */
 static void co_client_init(void)
@@ -202,6 +291,10 @@
        pisa_cfg_get_int_value("retry", &co_client_config.retries);
        pisa_cfg_get_string_value("filename", co_client_config.cert_filename,
                        PATH_BUFFER);
+       pisa_cfg_get_int_value("check_interval", 
&co_client_config.check_interval);
+       pisa_cfg_get_string_value("certs_dir", co_client_config.certs_dir,
+                       PATH_BUFFER);
+       pisa_cfg_get_bool_value("save_certs", &co_client_config.save_certs);
 
        /* sanity check configuration values */
        if (co_client_config.retries < 1)
@@ -209,12 +302,17 @@
        if (co_client_config.retries > 100) /* @todo find reasonable maximum */
                co_client_config.retries = 100;
 
+       if (co_client_config.check_interval <= 0)
+               co_client_config.check_interval = 60;
 }
 
-int main(int argc, char **argv)
+/**
+ * Retrieves new certificates from community-operator server
+ * @return 1 -> success
+ * @return 0 -> error
+ */
+static int retrieve_new_cert(void)
 {
-       co_client_init();
-
        struct sockaddr_in6 srv_addr = { 0 };
        char *cert = NULL;
        int count;
@@ -231,14 +329,203 @@
                        break;
        }
 
+       close(sock);
+
+       // Successfully received certificate
        if (cert) {
-               write_cert_file(cert, "test-cert");
+               PISA_INFO("Certificate was successfully retrieved.\n");
+#ifdef CONFIG_PISA_WITH_HIPL
+               save_cert_to_certs_dir(cert);
+#else
+               write_cert_file(cert, co_client_config.cert_filename);
+#endif /* CONFIG_PISA_WITH_HIPL */
                free(cert);
-               printf("Certificate was successfully retrieved.\n");
-       } else
-               printf("Certificate could not be obtained.\n");
+               return 1;
+       } else {
+               PISA_ERROR("Certificate could not be obtained.\n");
+               return 0;
+       }
 
-       pisa_cfg_cleanup();
-       close(sock);
+       return 1;
+}
+
+#ifdef CONFIG_PISA_WITH_HIPL
+static void save_cert_to_certs_dir(const char * cert)
+{
+       // Save certificate to co_client_config.certs_dir
+       if(co_client_config.save_certs) {
+               struct pisa_cert pc;
+               struct tm *ts;
+               char buf_before[15];
+               char buf_after[15];
+               char path[100];
+               time_t now;
+               char *cert_start;
+
+               cert_start = strstr(cert,"(cert");
+               // Create pisa_struct from cert file
+               pisa_split_cert(cert_start, &pc);
+
+               // Convert dates to format yyyymmddhhmmss
+               PISA_DEBUG(PL_GENERIC,"Certificate is valid from: 
%s",ctime(&pc.not_before));
+               ts = localtime(&pc.not_before);
+               strftime(buf_before, sizeof(buf_before), CERT_FILENAME_FORMAT, 
ts);
+               ts = localtime(&pc.not_after);
+               PISA_DEBUG(PL_GENERIC,"Certificate expires at: 
%s",ctime(&pc.not_after));
+               strftime(buf_after, sizeof(buf_after), CERT_FILENAME_FORMAT, 
ts);
+
+               // Create filename notbefore-notaftercert 
(yyyymmddhhmmss-yyyymmddhhmmsscert)
+               // in co_client_config.certs_dir
+               snprintf((char 
*)path,sizeof(path),"%s/%s-%scert",co_client_config.certs_dir 
,buf_before,buf_after);
+               write_cert_file(cert, (const char *)path);
+               time(&now);
+
+               // Certificate is already valid
+               // Copy to final location
+               if(difftime(now,pc.not_before)>=0)
+               {
+                       PISA_DEBUG(PL_GENERIC,"Copying to cert location...\n");
+                       strncpy(last_used_cert,path,PATH_BUFFER);
+                       write_cert_file(cert,(const char 
*)co_client_config.cert_filename);
+               }
+               else
+               {
+                       PISA_DEBUG(PL_GENERIC,"Certificate not yet valid.\n");
+               }
+       }
+}
+#endif /* CONFIG_PISA_WITH_HIPL */
+
+/**
+ * Checks expiration dates of a cert filename
+ * @param filename filename to check
+ * @return 1  -> cert is valid
+ * @return 0  -> cert not yet valid
+ * @return -1 -> cert expired
+ */
+static int is_valid_cert(const char *filename)
+{
+       struct tm ts_before, ts_after;
+       char buf_before[15];
+       char buf_after[15];
+       time_t now, not_before, not_after;
+
+       // Read not-before date
+       strncpy(buf_before, filename, 14);
+       strptime(buf_before, CERT_FILENAME_FORMAT, &ts_before);
+
+       // Read not-after date
+       strncpy(buf_after, filename + 15, 14);
+       strptime(buf_after, CERT_FILENAME_FORMAT, &ts_after);
+
+       time(&now);
+       not_before = mktime(&ts_before);
+       not_after = mktime(&ts_after);
+
+       // Expired
+       if (difftime(now, not_after) > 0) {
+               PISA_DEBUG(PL_GENERIC, "Cert: %s expired.\n", filename);
+               return -1;
+       }
+
+       // Not yet valid
+       if (difftime(now, not_before) < 0) {
+               PISA_DEBUG(PL_GENERIC, "Cert: %s is not yet valid.\n", 
filename);
+               return 0;
+       }
+
+       PISA_DEBUG(PL_GENERIC, "Cert: %s is valid.\n", filename);
+       return 1;
+}
+
+/**
+ * Checks if there is a valid cert in co_client_config.certs_dir
+ * and copies the cert to co_client_config.filename
+ * @return 1 -> a valid cert file exists
+ * @return 0 -> no valid cert file found
+ */
+static int check_certs_dir(void)
+{
+       struct dirent **namelist;
+       int n, i, found, result;
+       char path[PATH_BUFFER];
+
+       found = 0;
+       n = scandir(co_client_config.certs_dir, &namelist, 0, alphasort);
+       if (n < 0) {
+               perror("scandir");
+       } else {
+               for (i = 0; i < n && !found; i++) {
+                       // Skip . and ..
+                       if (strcmp(namelist[i]->d_name, ".") != 0 &&
+                                       strcmp(namelist[i]->d_name, "..") != 0) 
{
+                               result = is_valid_cert(namelist[i]->d_name);
+                               snprintf((char *) path, sizeof(path), "%s/%s",
+                                               co_client_config.certs_dir, 
namelist[i]->d_name);
+
+                               // Certificate expired
+                               // Remove certificate
+                               if (result == -1) {
+                                       PISA_DEBUG(PL_GENERIC, "Removing 
cert...\n");
+                                       if (remove(path) != 0) {
+                                               perror("Removing cert failed");
+                                       }
+                               } else if (result) {
+                                       found = 1;
+                                       // It is a new certificate
+                                       // Copy to final destination
+                                       if (strcmp(path, last_used_cert) != 0) {
+                                               strncpy(last_used_cert, path, 
PATH_BUFFER);
+                                               PISA_DEBUG(PL_GENERIC, "Found 
valid cert.\n");
+                                               PISA_DEBUG(PL_GENERIC, "Copying 
to cert location...\n");
+                                               copy_cert(path, 
co_client_config.cert_filename);
+                                       }
+                               }
+                       }
+                       free(namelist[i]);
+               }
+               free(namelist);
+       }
+       return found;
+}
+
+/**
+ * Function is called periodically to retrieve new certs if old certs in
+ * co_client_config.certs_dir expired.
+ * @param signal signal code
+ */
+static void co_client_check_certs(int signal)
+{
+       if (co_client_config.save_certs) {
+               PISA_INFO("Checking existing certificates...\n");
+               if (!check_certs_dir()) {
+                       PISA_INFO("Did not find a valid cert\n");
+                       PISA_INFO("Requesting new cert...\n");
+                       retrieve_new_cert();
+               }
+               alarm(co_client_config.check_interval);
+       } else {
+               retrieve_new_cert();
+       }
+}
+
+int main(int argc, char **argv)
+{
+
+       signal(SIGTERM, co_client_quit);
+       signal(SIGINT, co_client_quit);
+       signal(SIGQUIT, co_client_quit);
+       signal(SIGALRM, co_client_check_certs);
+
+       co_client_init();
+
+       co_client_check_certs(SIGALRM);
+
+       // Only enter infinite loop if we periodically check for valid certs
+       if (co_client_config.save_certs) {
+               co_client_main();
+       }
+
+       co_client_quit(0);
        return 0;
 }

Other related posts: