Explorar el Código

Merge branch 'fix/posix' into feature/wiener

Michele Orrù hace 11 años
padre
commit
d0d546b6ed
Se han modificado 10 ficheros con 295 adiciones y 175 borrados
  1. 34 54
      src/cmdline.c
  2. 7 3
      src/include/qa.h
  3. 5 1
      src/include/qa_sock.h
  4. 34 104
      src/qa.c
  5. 157 5
      src/qa_sock.c
  6. 1 1
      src/questions/allquestions.c
  7. 14 4
      src/questions/example.c
  8. 2 2
      src/questions/questions.h
  9. 3 1
      src/questions/qwiener.h
  10. 38 0
      src/tests/test_qa.c

+ 34 - 54
src/cmdline.c

@@ -16,44 +16,6 @@
 
 #include "qa.h"
 
-#define QA_DEFAULT_PORT "443"
-
-
-/**
- * \brief Converts a uri into a tuple {host, service}.
- *
- * Parses an input string containing a host and (maybe) a service/port.
- * Valid options are:
- *  - service://hostname
- *  - hostname
- *  - hostname:port
- * The resulting tuple will be stored in params \ref host and \ref service.
- *
- * \param[in]  uri     The input uri.
- * \param[out] host    Place where to store parsed host.
- * \param[out] service Place where to store parsed service.
- *
- * \note \ref uri might be modified during parsing.
- */
-static void host_port(char* uri, char** host, char** service)
-{
-  char* c;
-
-  if (!(c = strchr(uri, ':')))
-    *host = uri;
-  else {
-    *c = '\0';
-    if (c[1] != '/' && c[2] == '/') {
-      *service = uri;
-      *host = c+3;
-    } else {
-      *service = c+1;
-      *host = uri;
-    }
-  }
-}
-
-
 /**
  * \brief Prints the usage message, then exit.
  *
@@ -63,14 +25,19 @@ static void host_port(char* uri, char** host, char** service)
 void usage(void)
 {
   static const char* help_message = "%s usage: %s"
-    " [-p PORT]"
-    " <target>"
+    " [-r HOST:port | -f FILE]"
     " \n";
   fprintf(stderr, help_message,
           program_invocation_short_name,
           program_invocation_name);
 }
 
+void conflicting_args(void)
+{
+  printf("Conflicting fuffa\n");
+  usage();
+  exit(EXIT_FAILURE);
+}
 
 int main(int argc, char** argv)
 {
@@ -79,15 +46,15 @@ int main(int argc, char** argv)
   size_t i;
 
   struct option long_options[] = {
-    {"help", required_argument, NULL, 'h'},
-    {"port", required_argument, NULL, 'p'},
+    {"help", no_argument, NULL, 'h'},
+    {"remote", required_argument, NULL, 'r'},
+    {"file", required_argument, NULL, 'f'},
     {0, 0, 0, 0}
   };
-  static const char* short_options = "h:p:";
+  static const char* short_options = "hr:f:";
 
   struct qa_conf conf = {
-    .host = NULL,
-    .port = NULL,
+    .src_type = NONE,
   };
 
   while ((opt=getopt_long(argc, argv,
@@ -98,8 +65,15 @@ int main(int argc, char** argv)
       usage();
       exit(EXIT_SUCCESS);
       break;
-    case 'p':
-      conf.port = optarg;
+    case 'f':
+      if (conf.src_type != NONE) conflicting_args();
+      conf.src_type = LOCAL;
+      conf.src = optarg;
+      break;
+    case 'r':
+      if (conf.src_type != NONE) conflicting_args();
+      conf.src_type = REMOTE;
+      conf.src = optarg;
       break;
     case '?':
     default:
@@ -107,14 +81,20 @@ int main(int argc, char** argv)
       exit(EXIT_FAILURE);
     }
 
-  if (optind < argc && !strcmp(argv[optind], "--")) optind++;
-  if (optind != argc-1) {
-    usage();
-    exit(EXIT_FAILURE);
+  if (conf.src_type == NONE)  {
+    conf.src_type = REMOTE;
+
+    if (optind == argc)
+      conf.src = "-";
+    else if (optind == argc-1)
+      conf.src = argv[optind];
+    else if (optind == argc-2 && !strcmp(argv[optind], "--"))
+      conf.src = argv[optind+1];
+    else {
+        usage();
+        exit(EXIT_FAILURE);
+      }
   }
 
-  host_port(argv[optind], &conf.host, &conf.port);
-  if (!conf.port) conf.port = QA_DEFAULT_PORT;
-
   return qa_init(&conf);
 }

+ 7 - 3
src/include/qa.h

@@ -4,14 +4,18 @@
 #include <openssl/bio.h>
 
 struct qa_conf {
-  char *host;
-  char *port;
+  enum sources {
+    NONE, LOCAL, REMOTE
+  } src_type;
+  char *src;
 };
 
 
-BIO* bio_out;
+extern BIO* bio_out;
+extern BIO* bio_err;
 
 int qa_init(const struct qa_conf* args);
 
+X509* get_local_cert(const char *src);
 
 #endif   /* _QA_H_ */

+ 5 - 1
src/include/qa_sock.h

@@ -5,4 +5,8 @@
 
 int init_client(const struct qa_conf *options);
 
-#endif
+int host_port(char *uri, char **host, char **service);
+
+X509* get_remote_cert(char *address);
+
+#endif   /* _QA_SOCK_H_ */

+ 34 - 104
src/qa.c

@@ -1,51 +1,24 @@
 #include <assert.h>
+#include <error.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>
 #include <unistd.h>
-#include <netdb.h>
 
-#include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
 #include <openssl/x509.h>
-#include <openssl/evp.h>
-#include <openssl/bio.h>
 
 #include "qa.h"
 #include "questions.h"
 #include "qa_sock.h"
 
-/**
- * \brief Connection informations.
- */
-struct qa_connection {
-  int socket;   /**< socket file descriptor. */
-  SSL* ssl;     /**< ssl handler for this connection. */
-  SSL_CTX* ctx; /**< ssl context used in this connection. */
-};
-
-
-/**
- * \brief Destructor for a \ref qa_connection.
- *
- * Closes the socket, shuts down the connection, and frees all memory used for
- * holding the connection.
- * \note Input might be partial (ex. a socket exists, but not no ssl session).
- *
- * \param c   The connection to be freed.
- */
-static void qa_connection_free(struct qa_connection* c)
-{
-  if (c->socket)
-    close(c->socket);
-  if (c->ssl) {
-    SSL_shutdown(c->ssl);
-    SSL_free(c->ssl);
-  }
-  if (c->ctx)
-    SSL_CTX_free(c->ctx);
+/** BIO wrapper around stdout */
+BIO* bio_out;
+/** BIO wrapper around srderr */
+BIO* bio_err;
 
-  free(c);
-}
 
 void qa_abort(const char *reason)
 {
@@ -53,99 +26,56 @@ void qa_abort(const char *reason)
   exit(EXIT_FAILURE);
 }
 
-
-static int verify_callback(int ok, X509_STORE_CTX* ctx)
-{
-  return ok;
-}
-
-
-/**
- * \brief Set up a new ssl connection.
- *
- * Create a new \ref qa_connection, turning on OpenSSL (if not yet started), and
- * opening a socket with the target server over ssl.
- *
- * \param[in] conf  Configuration holding informations about the target.
- * \return          The new connection.
- */
-struct qa_connection* qa_connection_new(const struct qa_conf* conf)
+X509* get_local_cert(const char *src)
 {
-  struct qa_connection* c;
-  int err;
+  X509* crt;
+  FILE* fp;
 
-  c = malloc(sizeof(struct qa_connection));
-  if (!c) qa_abort("No Memory.");
+  if (!strcmp(src, "-")) fp = stdin;
+  else if (!(fp = fopen(src, "r")))
+    return NULL;
 
-  /* Initialize SSL Library by registering algorithms. */
-  SSL_library_init();
-
-
-  c->ctx = SSL_CTX_new(SSLv23_client_method());
-  if (!c->ctx) {
-    qa_connection_free(c);
-    qa_abort("Cannot create context");
-  }
-
-  /* is also the default. lol. */
-  SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, verify_callback);
-  c->ssl = SSL_new(c->ctx);
-  if (!c->ssl) {
-    qa_connection_free(c);
-    qa_abort("Cannot create ssl handle");
-  }
-
-  if (!(c->socket = init_client(conf))) {
-    qa_connection_free(c);
-    qa_abort("Cannot create socket.");
-  }
-
-  if (!SSL_set_fd(c->ssl, c->socket)) {
-    qa_connection_free(c);
-    qa_abort("Cannot bind socket to ssl session");
-  }
-
-  /* XXX. Handle errors appropriately using error codes from OpenSSL */
-  err = SSL_connect(c->ssl);
-  if (err != 1) {
-    qa_connection_free(c);
-    qa_abort("Cannot Connect");
-  }
-
-  SSL_set_connect_state(c->ssl);
-
-  return c;
+  crt = PEM_read_X509(fp, NULL, 0, NULL);
+  return crt;
 }
 
-
 /**
  * \brief Given an initial configuration, stuctures the program flow.
  *
  * \param[in] args   Initial configuration given from a frontend.
  */
-int qa_init(const struct qa_conf* args)
+int qa_init(const struct qa_conf* conf)
 {
   X509 *crt;
-  struct qa_connection *c;
   struct qa_question *q;
 
-  /* bind stdout to a BIO shit to be used externally */
+  /* bind stdout/stderr to a BIO shit to be used externally */
   bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
+  bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
+
+  /* Initialize SSL Library by registering algorithms. */
+  SSL_library_init();
+
+
+  if (conf->src_type == REMOTE)
+    crt = get_remote_cert(conf->src);
+  else if (conf->src_type == LOCAL)
+    crt = get_local_cert(conf->src);
+  else
+    error(EXIT_FAILURE, 0, "iternal error: unable to determine source type.");
 
-  /* create a new connection, and download the certificate */
-  c = qa_connection_new(args);
-  crt = SSL_get_peer_certificate(c->ssl);
-  if (!crt) qa_abort("Cannot obtain certificate");
+  if (!crt)
+    error(EXIT_FAILURE, errno, "oops");
 
   register_all_questions();
-for (q=questions.lh_first; q; q = q->qs.le_next) {
+  for (q=questions.lh_first; q; q = q->qs.le_next) {
     q->setup();
     q->test(crt);
     q->ask(crt);
     q->teardown();
   }
 
-  qa_connection_free(c);
+  X509_free(crt);
 
   return 0;
 }

+ 157 - 5
src/qa_sock.c

@@ -1,3 +1,5 @@
+#define _POSIX_SOURCE
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -8,26 +10,69 @@
 #include <sys/socket.h>
 #include <netdb.h>
 
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+
 #include "qa.h"
 
 #define SOCKET_PROTOCOL 0
 #define INVALID_SOCKET  (-1)
 
-int init_client(const struct qa_conf *options)
+/**
+ * \brief Converts a uri into a tuple {host, service}.
+ *
+ * Parses an input string containing a host and (maybe) a service/port.
+ * Valid options are:
+ *  - service://hostname
+ *  - hostname
+ *  - hostname:port
+ * The resulting tuple will be stored in params \ref host and \ref service.
+ * \note \ref uri might be modified during parsing.
+ *
+ * \param[in]  uri     The input uri.
+ * \param[out] host    Place where to store parsed host.
+ * \param[out] service Place where to store parsed service.
+ *
+ * \return 0 if the string was not parsable, 1 otherwise.
+
+ */
+int host_port(char *uri, char **host, char **service)
+{
+  char* c;
+
+  if (!(c = strchr(uri, ':'))) {
+    *host = uri;
+    *service = NULL;
+  } else {
+    *c = '\0';
+    if (c[1] == '/' && c[2] == '/') {
+      *service = uri;
+      *host = c+3;
+    } else {
+      *service = c+1;
+      *host = uri;
+    }
+  }
+
+  return 1;
+}
+
+int init_client(const char *host, const char *port)
 {
   struct addrinfo hints;
   struct addrinfo *result, *rp;
   int s;
   int i;
 
+
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = 0;
   hints.ai_protocol = 0;
 
-  if ((i=getaddrinfo(options->host, options->port, NULL, &result))) {
-    fprintf(stderr, "Error: %s\n", gai_strerror(i));
-    exit(EXIT_FAILURE);
+  if ((i=getaddrinfo(host, port, NULL, &result))) {
+    BIO_printf(bio_err, "Error: %s\n", gai_strerror(i));
+    return -1;
   }
 
   for (rp=result; rp; rp = rp->ai_next)  {
@@ -44,7 +89,114 @@ int init_client(const struct qa_conf *options)
 
     if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) break;
   }
-  if (!rp) exit(EXIT_FAILURE);
+  if (!rp) return -1;
 
   return s;
 }
+
+/**
+ * \brief Connection informations.
+ */
+typedef struct qa_connection {
+  int socket;   /**< socket file descriptor. */
+  SSL* ssl;     /**< ssl handler for this connection. */
+  SSL_CTX* ctx; /**< ssl context used in this connection. */
+} qa_connection_t;
+
+
+/**
+ * \brief Destructor for a \ref qa_connection.
+ *
+ * Closes the socket, shuts down the connection, and frees all memory used for
+ * holding the connection.
+ * \note Input might be partial (ex. a socket exists, but not no ssl session).
+ *
+ * \param c   The connection to be freed.
+ */
+static void qa_connection_free(struct qa_connection* c)
+{
+  if (!c) return;
+
+  if (c->socket != -1)
+    close(c->socket);
+  if (c->ssl) {
+    SSL_shutdown(c->ssl);
+    SSL_free(c->ssl);
+  }
+  if (c->ctx)
+    SSL_CTX_free(c->ctx);
+
+  free(c);
+}
+
+
+static int verify_callback(int ok, X509_STORE_CTX* ctx)
+{
+  return ok;
+}
+
+
+/**
+ * \brief Set up a new ssl connection.
+ *
+ * Create a new \ref qa_connection, turning on OpenSSL (if not yet started), and
+ * opening a socket with the target server over ssl.
+ *
+ * \param[in] conf  Configuration holding informations about the target.
+ * \return          The new connection.
+ */
+struct qa_connection* qa_connection_new(char* address)
+{
+  struct qa_connection* c;
+  char *host, *port;
+  int err;
+
+  /* parse input address */
+  if (!host_port(address, &host, &port)) goto error;
+
+  c = malloc(sizeof(struct qa_connection));
+  if (!c) goto error;
+  /* set up context, and protocol versions */
+  c->ctx = SSL_CTX_new(SSLv23_client_method());
+  if (!c->ctx) goto error;
+  /* create the ssl session, disabling certificate verification */
+  SSL_CTX_set_verify(c->ctx, SSL_VERIFY_NONE, verify_callback);
+  c->ssl = SSL_new(c->ctx);
+  if (!c->ssl) goto error;
+  /* open the socket over ssl */
+  c->socket = init_client(host, port);
+  if (c->socket == -1)                goto error;
+  if (!SSL_set_fd(c->ssl, c->socket)) goto error;
+  if (SSL_connect(c->ssl) != 1)       goto error;
+
+  SSL_set_connect_state(c->ssl);
+  return c;
+
+ error:
+  /* XXX. add checks for errno, and the ssl error stack (ssl_get_error) */
+  qa_connection_free(c);
+  return NULL;
+}
+
+/**
+ * \brief Fetches the certificate opening a tcp connection to the given address.
+ *
+ * Attempts to open a new tcp connection to the address `address`, and return
+ * the X509 certificate presented by the server.
+ *
+ * \param address[in] the uri to which handshake a ssl connection.
+ * \return a valid pointer to the X509 certificate if the handshake succeeded,
+ *         NULL otherwise.
+ */
+X509* get_remote_cert(char *address)
+{
+  X509 *crt;
+  qa_connection_t *c;
+
+  c = qa_connection_new(address);
+  if (!c) return NULL;
+
+  crt = SSL_get_peer_certificate(c->ssl);
+  qa_connection_free(c);
+  return crt;
+}

+ 1 - 1
src/questions/allquestions.c

@@ -6,7 +6,7 @@
 #define REGISTER_QUESTION(q)                      \
   {                                               \
       extern struct qa_question q;                \
-      LIST_INSERT_HEAD(&questions, &q, qs);        \
+      LIST_INSERT_HEAD(&questions, &q, qs);       \
   }
 
 /**

+ 14 - 4
src/questions/example.c

@@ -1,7 +1,16 @@
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+
+
 #include "questions.h"
-#include "qa.h"
 
-int example_question_setup(void) { return 0; }
+static BIO* out;
+
+int example_question_setup(void) {
+  out = BIO_new_fp(stdout, BIO_NOCLOSE);
+  return 0;
+}
+
 int example_question_teardown(void) { return 0; }
 /* XXX. apparently openssl does not allow const X509* in get_pkey() func */
 int example_question_test(X509* cert) { return 1; }
@@ -10,12 +19,13 @@ int example_question_ask(X509* cert)
   EVP_PKEY* pkey;
 
   pkey = X509_get_pubkey(cert);
-  EVP_PKEY_print_public(bio_out, pkey, 3, NULL);
+  EVP_PKEY_print_public(out, pkey, 3, NULL);
+  return 1;
 }
 
 
 
-struct qa_question ExampleQuestion = {
+qa_question_t ExampleQuestion = {
   .name = "Example Question",
   .setup = example_question_setup,
   .teardown = example_question_teardown,

+ 2 - 2
src/questions/questions.h

@@ -6,7 +6,7 @@
 #include <openssl/x509.h>
 
 
-struct qa_question {
+typedef struct qa_question {
   const char* name;
   int (* setup) (void);
   int (* teardown) ();
@@ -14,7 +14,7 @@ struct qa_question {
   int (* ask) (X509* cert);
 
   LIST_ENTRY(qa_question) qs;
-};
+} qa_question_t;
 
 
 LIST_HEAD(listhead, qa_question) questions;

+ 3 - 1
src/questions/qwiener.h

@@ -12,6 +12,7 @@ typedef struct bigfraction {
   BIGNUM* k;
 } bigfraction_t;
 
+
 typedef struct cf {
   bigfraction_t fs[3];
   short i;
@@ -25,4 +26,5 @@ cf_t* cf_init(cf_t *f, BIGNUM *num, BIGNUM *b);
 bigfraction_t* cf_next(cf_t *f);
 
 extern struct qa_question WienerQuestion;
-#endif
+
+#endif /* _QA_WIENER_H_ */

+ 38 - 0
src/tests/test_qa.c

@@ -0,0 +1,38 @@
+#include <assert.h>
+#include <string.h>
+
+#include <openssl/x509.h>
+
+#include "qa.h"
+
+
+void test_get_local_cert(void)
+{
+  X509 *crt;
+  EVP_PKEY *pkey;
+  char path[64];
+
+  /* get_local_cert() shall return NULL if the file does not exist. */
+  strcpy(path, "/lifting/me/higher/keeps/me/lifting.crt");
+  assert(!get_local_cert(path));
+
+  strcpy(path, "/home/maker/dev/uni/thesis/src/dummy.crt");
+  crt = get_local_cert(path);
+  assert(crt);
+  pkey = X509_get_pubkey(crt);
+  assert(pkey);
+  /*
+   *  The certificate shall make accessible both the modulus and the public
+   *  exponent.
+   */
+  assert(pkey->pkey.rsa->n);
+  assert(pkey->pkey.rsa->e);
+
+}
+
+
+int main(int argc, char **argv)
+{
+  test_get_local_cert();
+  return 0;
+}