Added a call extracting a PEM string of client's certificate chain
authorDaniel Kouřil <kouril@ics.muni.cz>
Tue, 19 Feb 2008 15:18:41 +0000 (15:18 +0000)
committerDaniel Kouřil <kouril@ics.muni.cz>
Tue, 19 Feb 2008 15:18:41 +0000 (15:18 +0000)
org.glite.security.gss/interface/glite_gss.h
org.glite.security.gss/src/glite_gss.c

index 4b62c17..ae3971c 100644 (file)
@@ -136,6 +136,11 @@ edg_wll_gss_get_client_conn(edg_wll_GssConnection *connection,
                            edg_wll_GssPrincipal *principal,
                            edg_wll_GssStatus* gss_code);
 
+int
+edg_wll_gss_get_client_pem(edg_wll_GssConnection *connection,
+                          const char *my_cert_file, const char *my_key_file,
+                           char **pem_string);
+
 void
 edg_wll_gss_free_princ(edg_wll_GssPrincipal principal);
 
index 165c2df..f325c14 100644 (file)
 #include <globus_common.h>
 #include <gssapi.h>
 #include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/bio.h>
 
 #include "glite_gss.h"
 
@@ -1213,6 +1218,189 @@ end:
    return ret;
 }
 
+/* Beware, this call manipulates with environment variables and is not
+   thread-safe */
+static int
+get_peer_cred(edg_wll_GssConnection *gss, const char *my_cert_file,
+              const char *my_key_file, STACK_OF(X509) **chain,
+              edg_wll_GssStatus* gss_code)
+{
+   OM_uint32 maj_stat, min_stat;
+   gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+   BIO *bio = NULL;
+   SSL_SESSION *session = NULL;
+   unsigned char int_buffer[4];
+   long length;
+   int ret, index;
+   STACK_OF(X509) *cert_chain = NULL;
+   X509 *p_cert;
+   char *orig_cert = NULL, *orig_key = NULL;
+
+   maj_stat = gss_export_sec_context(&min_stat, (gss_ctx_id_t *) &gss->context,
+                                    &buffer);
+   if (GSS_ERROR(maj_stat)) {
+      if (gss_code) {
+         gss_code->major_status = maj_stat;
+         gss_code->minor_status = min_stat;
+      }
+      return EDG_WLL_GSS_ERROR_GSS;
+   }
+
+   /* The GSSAPI specs requires gss_export_sec_context() to destroy the
+    * context after exporting. So we have to resurrect the context here by
+    * importing from just generated buffer. gss_import_sec_context() must be
+    * able to read valid credential before it loads the exported context
+    * so we set the environment temporarily to point to the ones used by
+    * the server.
+    * */
+
+   orig_cert = getenv("X509_USER_CERT");
+   orig_key = getenv("X509_USER_KEY");
+
+   if (my_cert_file)
+      setenv("X509_USER_CERT", my_cert_file, 1);
+   if (my_key_file)
+      setenv("X509_USER_KEY", my_key_file, 1);
+   
+   maj_stat = gss_import_sec_context(&min_stat, &buffer,
+                                    (gss_ctx_id_t *)&gss->context);
+
+   if (orig_cert)
+       setenv("X509_USER_CERT", orig_cert, 1);
+   else
+       unsetenv("X509_USER_CERT");
+
+   if (orig_key) 
+       setenv("X509_USER_KEY", orig_key, 1);
+   else 
+       unsetenv("X509_USER_KEY");
+
+   if (GSS_ERROR(maj_stat)) {
+      if (gss_code) {
+         gss_code->major_status = maj_stat;
+         gss_code->minor_status = min_stat;
+      }
+      ret = EDG_WLL_GSS_ERROR_GSS;
+      goto end;
+   }
+
+   bio = BIO_new(BIO_s_mem());
+   if (bio == NULL) {
+      ret = ENOMEM;
+      goto end;
+   }
+   
+   /* Store exported context to memory, skipping the version number and and cred_usage fields */
+   BIO_write(bio, buffer.value + 8 , buffer.length - 8);
+
+   /* decode the session data in order to skip at the start of the cert chain */
+   session = d2i_SSL_SESSION_bio(bio, NULL);
+   if (session == NULL) {
+      ret = EINVAL;
+      goto end;
+   }
+   SSL_SESSION_free(session);
+
+   cert_chain = sk_X509_new_null();
+
+   BIO_read(bio, (char *) int_buffer, 4);
+   length  = (((size_t) int_buffer[0]) << 24) & 0xffff;
+   length |= (((size_t) int_buffer[1]) << 16) & 0xffff;
+   length |= (((size_t) int_buffer[2]) <<  8) & 0xffff;
+   length |= (((size_t) int_buffer[3])      ) & 0xffff;
+
+   if (length == 0) {
+      ret = EINVAL;
+      goto end;
+   }
+
+   for(index = 0; index < length; index++) {
+      p_cert = d2i_X509_bio(bio, NULL);
+      if (p_cert == NULL) {
+        ret = EINVAL;
+        sk_X509_pop_free(cert_chain, X509_free);
+        goto end;
+      }
+
+      sk_X509_push(cert_chain, p_cert);
+   }
+
+   *chain = cert_chain;
+   ret = 0;
+
+end:
+   gss_release_buffer(&min_stat, &buffer);
+
+   return ret;
+}
+
+int
+edg_wll_gss_get_client_pem(edg_wll_GssConnection *connection,
+                           const char *my_cert_file, const char *my_key_file,
+                           char **pem_string)
+{
+   char *tmp = NULL;
+   STACK_OF(X509) *chain = NULL;
+   BIO *bio = NULL;
+   int ret, i;
+   size_t total_len, l;
+
+   ret = get_peer_cred(connection, my_cert_file, my_key_file, &chain, NULL);
+   if (ret)
+      return ret;
+
+   bio = BIO_new(BIO_s_mem());
+   if (bio == NULL) {
+      ret = ENOMEM;
+      goto end;
+   }
+
+   for (i = 0; i < sk_X509_num(chain); i++) {
+      ret = PEM_write_bio_X509(bio, sk_X509_value(chain, i));
+      if (ret <= 0) {
+        ret = EINVAL;
+        goto end;
+      }
+   }
+
+   total_len = BIO_pending(bio);
+   if (total_len <= 0) {
+      ret = EINVAL;
+      goto end;
+   }
+
+   tmp = malloc(total_len + 1);
+   if (tmp == NULL) {
+      ret = ENOMEM;
+      goto end;
+   }
+
+   l = 0;
+   while (l < total_len) {
+      ret = BIO_read(bio, tmp+l, total_len-l);
+      if (ret <= 0) {
+        ret = EINVAL;
+        goto end;
+      }
+      l += ret;
+   }
+
+   tmp[total_len] = '\0';
+   *pem_string = tmp;
+   tmp = NULL;
+
+   ret = 0;
+
+end:
+   sk_X509_pop_free(chain, X509_free);
+   if (bio)
+      BIO_free(bio);
+   if (tmp)
+      free(tmp);
+
+   return ret;
+}
+
 void
 edg_wll_gss_free_princ(edg_wll_GssPrincipal principal)
 {