]> granicus.if.org Git - pdns/commitdiff
Add implementation for GSS-TSIG feature
authorAki Tuomi <cmouse@cmouse.fi>
Thu, 21 May 2015 19:15:28 +0000 (22:15 +0300)
committerAki Tuomi <cmouse@cmouse.fi>
Thu, 28 May 2015 14:42:17 +0000 (17:42 +0300)
pdns/gss_context.cc [new file with mode: 0644]
pdns/gss_context.hh [new file with mode: 0644]

diff --git a/pdns/gss_context.cc b/pdns/gss_context.cc
new file mode 100644 (file)
index 0000000..5b8f306
--- /dev/null
@@ -0,0 +1,475 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <map>
+#include <string>
+#include "namespaces.hh"
+#include "dns.hh"
+#include "dnsparser.hh"
+#include "dnspacket.hh"
+#include "dnsrecords.hh"
+#include "logger.hh"
+#include "lock.hh"
+#include "arguments.hh"
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/make_shared.hpp>
+#include "gss_context.hh"
+
+#ifndef ENABLE_GSS_TSIG
+
+bool GssContext::supported() { return false; }
+GssContext::GssContext() {};
+GssContext::GssContext(const std::string& label) {};
+void GssContext::setLocalPrincipal(const std::string& name) {}
+bool GssContext::getLocalPrincipal(std::string& name) { return false; }
+void GssContext::setPeerPrincipal(const std::string& name) {}
+bool GssContext::getPeerPrincipal(std::string& name) { return false; }
+void GssContext::generateLabel(const std::string& suffix) {}
+void GssContext::setLabel(const std::string& label) {}
+bool GssContext::init(const std::string &input, std::string& output) { return false; }
+bool GssContext::accept(const std::string &input, std::string& output) { return false; }
+bool GssContext::destroy() { return false; }
+bool GssContext::expired() { return false; }
+bool GssContext::valid() { return false; }
+bool GssContext::sign(const std::string &input, std::string& output) { return false; }
+bool GssContext::verify(const std::string &input, const std::string &signature) { return false; }
+GssContextError GssContext::getError() { return GSS_CONTEXT_UNSUPPORTED; }
+
+#else
+
+class GssCredential : boost::noncopyable {
+public:
+  GssCredential(const std::string& name, const gss_cred_usage_t usage) {
+    gss_buffer_desc buffer;
+    d_name = GSS_C_NO_NAME;
+    d_nameS = name;
+    d_cred = GSS_C_NO_CREDENTIAL;
+
+    d_usage = usage;
+    d_valid = false;
+    
+    if (name.empty() == false) {
+      buffer.length = name.size();
+      buffer.value = (void*)name.c_str();
+      d_maj = gss_import_name(&d_min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &d_name);
+      if (d_maj != GSS_S_COMPLETE) {
+        d_valid = false;
+        return;       
+      }
+    }
+
+    renew();
+  };
+
+  ~GssCredential() {
+    OM_uint32 tmp_maj  __attribute__((unused)), tmp_min __attribute__((unused));
+    if (d_cred != GSS_C_NO_CREDENTIAL) 
+      tmp_maj = gss_release_cred(&tmp_min, &d_cred);
+    if (d_name != GSS_C_NO_NAME) 
+      tmp_maj = gss_release_name(&tmp_min, &d_name);
+  };
+
+  bool expired() const {
+    if (d_expires == -1) return false;
+    return time((time_t*)NULL)>d_expires;
+  }
+
+  bool renew() {
+    OM_uint32 time_rec, tmp_maj __attribute__((unused)), tmp_min __attribute__((unused));
+    d_maj = gss_acquire_cred(&d_min, d_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, d_usage, &d_cred, NULL, &time_rec);
+
+    if (d_maj != GSS_S_COMPLETE) {
+      d_valid = false;
+      tmp_maj = gss_release_name(&tmp_min, &d_name);
+      d_name = GSS_C_NO_NAME;
+      return false;
+    }
+
+    d_valid = true;
+
+    if (time_rec > GSS_C_INDEFINITE) {
+      d_expires = time((time_t*)NULL)+time_rec;
+    } else {
+      d_expires = -1;
+    }
+
+    return true;
+  }
+
+  bool valid() {
+    return d_valid && !expired();
+  }
+
+  OM_uint32 d_maj,d_min;
+
+  bool d_valid;
+  int64_t d_expires;
+  std::string d_nameS;
+  gss_name_t d_name;
+  gss_cred_id_t d_cred;
+  gss_cred_usage_t d_usage;
+};
+
+std::map<std::string, boost::shared_ptr<GssCredential> > s_gss_accept_creds;
+std::map<std::string, boost::shared_ptr<GssCredential> > s_gss_init_creds;
+
+class GssSecContext : boost::noncopyable {
+public:
+  GssSecContext(const std::string& label, boost::shared_ptr<GssCredential> cred) {    
+    if (cred->valid() == false) throw PDNSException("Invalid credential " + cred->d_nameS);
+    d_cred = cred;
+    d_state = GssStateInitial;
+    d_ctx = GSS_C_NO_CONTEXT;
+    d_expires = 0;
+    d_maj = d_min = 0;
+    d_peer_name = GSS_C_NO_NAME;
+    d_type = GSS_CONTEXT_NONE;
+  }
+
+  ~GssSecContext() {
+    OM_uint32 tmp_maj  __attribute__((unused)), tmp_min __attribute__((unused));
+    if (d_ctx != GSS_C_NO_CONTEXT) {
+      tmp_maj = gss_delete_sec_context(&tmp_min, &d_ctx, GSS_C_NO_BUFFER);
+    }
+    if (d_peer_name != GSS_C_NO_NAME) {
+      tmp_maj = gss_release_name(&tmp_min, &(d_peer_name));
+    }
+  }
+
+  GssContextType d_type;
+  gss_ctx_id_t d_ctx;
+  gss_name_t d_peer_name;
+  int64_t d_expires;
+  boost::shared_ptr<GssCredential> d_cred;
+  OM_uint32 d_maj,d_min;
+
+  enum {
+    GssStateInitial,
+    GssStateNegotiate,
+    GssStateComplete,
+    GssStateError
+  } d_state;
+
+};
+
+std::map<std::string, boost::shared_ptr<GssSecContext> > s_gss_sec_context;
+
+bool GssContext::supported() { return true; }
+
+void GssContext::initialize() {
+  d_label = "";
+  d_peerPrincipal = "";
+  d_localPrincipal = "";
+  d_error = GSS_CONTEXT_NO_ERROR;
+  d_type = GSS_CONTEXT_NONE;
+} 
+
+GssContext::GssContext() {
+  initialize();
+  generateLabel("pdns.tsig");
+}
+
+GssContext::GssContext(const std::string& label) {
+  initialize();
+  setLabel(toLowerCanonic(label));
+}
+
+void GssContext::generateLabel(const std::string& suffix) {
+  std::ostringstream oss;
+  oss << std::hex << time((time_t*)NULL) << "." << suffix;
+  setLabel(oss.str());
+}
+
+void GssContext::setLabel(const std::string& label) {
+  d_label = label;
+  if (s_gss_sec_context.find(d_label) != s_gss_sec_context.end()) {
+    d_ctx = s_gss_sec_context[d_label];
+    d_type = d_ctx->d_type;
+  }
+}
+
+bool GssContext::expired() {
+  return (!d_ctx || (d_ctx->d_expires > -1 && d_ctx->d_expires < time((time_t*)NULL)));
+}
+
+bool GssContext::valid() {
+  return (d_ctx && !expired() && d_ctx->d_state == GssSecContext::GssStateComplete);
+}
+
+bool GssContext::init(const std::string &input, std::string& output) {
+  OM_uint32 tmp_maj  __attribute__((unused)), tmp_min __attribute__((unused));
+  OM_uint32 maj,min;
+  gss_buffer_desc recv_tok, send_tok, buffer;
+  OM_uint32 flags;
+  OM_uint32 expires;
+
+  boost::shared_ptr<GssCredential> cred;
+  if (d_label.empty()) {
+    d_error = GSS_CONTEXT_INVALID;
+    return false;
+  }
+
+  d_type = GSS_CONTEXT_INIT;
+
+  if (s_gss_init_creds.find(d_localPrincipal) != s_gss_init_creds.end()) {
+    cred = s_gss_init_creds[d_localPrincipal];
+  } else {
+    s_gss_init_creds[d_localPrincipal] = boost::make_shared<GssCredential>(d_localPrincipal, GSS_C_INITIATE);
+    cred = s_gss_init_creds[d_localPrincipal];
+  }
+
+  // see if we can find a context in non-completed state
+  if (d_ctx) {
+    if (d_ctx->d_state != GssSecContext::GssStateNegotiate) {
+      d_error = GSS_CONTEXT_INVALID;
+      return false;
+    }
+  } else {
+    // make context
+    s_gss_sec_context[d_label] = boost::make_shared<GssSecContext>(d_label, cred);
+    s_gss_sec_context[d_label]->d_type = d_type;
+    d_ctx = s_gss_sec_context[d_label];
+    d_ctx->d_state = GssSecContext::GssStateNegotiate;
+  }
+
+  recv_tok.length = input.size();
+  recv_tok.value = (void*)input.c_str();
+
+  if (d_peerPrincipal.empty() == false) {
+    buffer.value = (void*)d_peerPrincipal.c_str();
+    buffer.length = d_peerPrincipal.size();
+    maj = gss_import_name(&min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &(d_ctx->d_peer_name));
+    if (maj != GSS_S_COMPLETE) {
+      processError("gss_import_name", maj, min);
+      return false;
+    }
+  }
+
+  maj = gss_init_sec_context(&min, cred->d_cred, &(d_ctx->d_ctx), d_ctx->d_peer_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &recv_tok, NULL, &send_tok, &flags, &expires);
+
+  if (send_tok.length>0) {
+    output.assign((const char*)send_tok.value, send_tok.length);
+    tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
+  }
+
+  if (maj == GSS_S_COMPLETE) {
+    if (expires > GSS_C_INDEFINITE) {
+      d_ctx->d_expires = time((time_t*)NULL) + expires;
+    } else {
+      d_ctx->d_expires = -1;
+    }
+    d_ctx->d_state = GssSecContext::GssStateComplete;
+    return true;
+  } else if (maj != GSS_S_CONTINUE_NEEDED) {
+    processError("gss_init_sec_context", maj,min);
+  }
+
+  return (maj == GSS_S_CONTINUE_NEEDED);
+}
+
+bool GssContext::accept(const std::string &input, std::string& output) {
+  OM_uint32 tmp_maj  __attribute__((unused)), tmp_min __attribute__((unused));
+  OM_uint32 maj,min;
+  gss_buffer_desc recv_tok, send_tok;
+  OM_uint32 flags;
+  OM_uint32 expires;
+
+  boost::shared_ptr<GssCredential> cred;
+  if (d_label.empty()) {
+    d_error = GSS_CONTEXT_INVALID;
+    return false;
+  }
+
+  d_type = GSS_CONTEXT_ACCEPT;
+
+  if (s_gss_accept_creds.find(d_localPrincipal) != s_gss_accept_creds.end()) {
+    cred = s_gss_accept_creds[d_localPrincipal];
+  } else {
+    s_gss_accept_creds[d_localPrincipal] = boost::make_shared<GssCredential>(d_localPrincipal, GSS_C_ACCEPT);
+    cred = s_gss_accept_creds[d_localPrincipal];
+  }
+
+  // see if we can find a context in non-completed state
+  if (d_ctx) {
+    if (d_ctx->d_state != GssSecContext::GssStateNegotiate) {
+      d_error = GSS_CONTEXT_INVALID;
+      return false;
+    } 
+  } else {
+    // make context
+    s_gss_sec_context[d_label] = boost::make_shared<GssSecContext>(d_label, cred);
+    s_gss_sec_context[d_label]->d_type = d_type;
+    d_ctx = s_gss_sec_context[d_label];
+    d_ctx->d_state = GssSecContext::GssStateNegotiate;
+  }
+
+  recv_tok.length = input.size();
+  recv_tok.value = (void*)input.c_str();
+
+  maj = gss_accept_sec_context(&min, &(d_ctx->d_ctx), cred->d_cred, &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &(d_ctx->d_peer_name), NULL, &send_tok, &flags, &expires, NULL);
+
+  if (send_tok.length>0) {
+    output.assign((const char*)send_tok.value, send_tok.length);
+    tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
+  }
+
+  if (maj == GSS_S_COMPLETE) {
+    if (expires > GSS_C_INDEFINITE) {
+      d_ctx->d_expires = time((time_t*)NULL) + expires;
+    } else {
+      d_ctx->d_expires = -1;
+    }
+    d_ctx->d_state = GssSecContext::GssStateComplete;
+    return true;
+  } else if (maj != GSS_S_CONTINUE_NEEDED) {
+    processError("gss_accept_sec_context", maj,min);
+  }
+  return (maj == GSS_S_CONTINUE_NEEDED);
+};
+
+bool GssContext::sign(const std::string& input, std::string& output) {
+  OM_uint32 tmp_maj  __attribute__((unused)), tmp_min __attribute__((unused));
+  OM_uint32 maj,min;
+
+  gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
+
+  recv_tok.length = input.size();
+  recv_tok.value = (void*)input.c_str();
+
+  maj = gss_get_mic(&min, d_ctx->d_ctx, GSS_C_QOP_DEFAULT, &recv_tok, &send_tok);
+
+  if (send_tok.length>0) {
+    output.assign((const char*)send_tok.value, send_tok.length);
+    tmp_maj = gss_release_buffer(&tmp_min, &send_tok);
+  }
+
+  if (maj != GSS_S_COMPLETE) {
+    processError("gss_get_mic", maj,min);
+  }
+
+  return (maj == GSS_S_COMPLETE);
+}
+
+bool GssContext::verify(const std::string& input, const std::string& signature) {
+  OM_uint32 maj,min;
+
+  gss_buffer_desc recv_tok = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc sign_tok = GSS_C_EMPTY_BUFFER;
+
+  recv_tok.length = input.size();
+  recv_tok.value = (void*)input.c_str();
+  sign_tok.length = signature.size();
+  sign_tok.value = (void*)signature.c_str();
+
+  maj = gss_verify_mic(&min, d_ctx->d_ctx, &recv_tok, &sign_tok, NULL);
+
+  if (maj != GSS_S_COMPLETE) {
+    processError("gss_get_mic", maj,min);
+  }
+
+  return (maj == GSS_S_COMPLETE);
+}
+
+bool GssContext::destroy() {
+  return false;
+}
+
+void GssContext::setLocalPrincipal(const std::string& name) {
+  d_localPrincipal = name;
+}
+
+bool GssContext::getLocalPrincipal(std::string& name) {
+  name = d_localPrincipal;
+  return name.size()>0;
+}
+
+void GssContext::setPeerPrincipal(const std::string& name) {
+  d_peerPrincipal = name;
+}
+
+bool GssContext::getPeerPrincipal(std::string& name) {
+  gss_buffer_desc value;
+  OM_uint32 maj,min;
+
+  if (d_ctx->d_peer_name != GSS_C_NO_NAME) {
+    maj = gss_display_name(&min, d_ctx->d_peer_name, &value, NULL);
+    if (maj == GSS_S_COMPLETE && value.length > 0) {
+      name.assign((const char*)value.value, value.length);
+      maj = gss_release_buffer(&min, &value);
+      return true;
+    } else {
+      return false;
+    }
+  } else { 
+    return false;
+  }
+}
+
+void GssContext::processError(const std::string& method, OM_uint32 maj, OM_uint32 min) {
+   OM_uint32 tmp_min;
+   gss_buffer_desc msg;
+   OM_uint32 msg_ctx;
+
+   msg_ctx = 0;
+   while (1) {
+      ostringstream oss;
+      gss_display_status(&tmp_min, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
+      oss << method << ": " << (char*)msg.value;
+      d_gss_errors.push_back(oss.str());
+      if (!msg_ctx) break;
+   }
+   msg_ctx = 0;
+   while (1) {
+      ostringstream oss;
+      gss_display_status(&tmp_min, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &msg);
+      oss << method << ": " << (char*)msg.value;
+      d_gss_errors.push_back(oss.str());
+      if (!msg_ctx) break;
+   }
+}
+
+#endif
+
+bool gss_add_signature(const std::string& context, const std::string& message, std::string& mac) {
+  string tmp_mac;
+  GssContext gssctx(context);
+  if (!gssctx.valid()) {
+    L<<Logger::Error<<"GSS context '"<<context<<"' is not valid"<<endl;
+    BOOST_FOREACH(const string& error, gssctx.getErrorStrings()) {
+       L<<Logger::Error<<"GSS error: "<<error<<endl;;
+    }
+    return false;
+  }
+
+  if (!gssctx.sign(message, tmp_mac)) {
+    L<<Logger::Error<<"Could not sign message using GSS context '"<<context<<"'"<<endl;
+    BOOST_FOREACH(const string& error, gssctx.getErrorStrings()) {
+       L<<Logger::Error<<"GSS error: "<<error<<endl;;
+    }
+    return false;
+  }
+  mac = tmp_mac;
+  return true;
+}
+
+bool gss_verify_signature(const std::string& context, const std::string& message, const std::string& mac) {
+  GssContext gssctx(context);
+  if (!gssctx.valid()) {
+    L<<Logger::Error<<"GSS context '"<<context<<"' is not valid"<<endl;
+    BOOST_FOREACH(const string& error, gssctx.getErrorStrings()) {
+       L<<Logger::Error<<"GSS error: "<<error<<endl;;
+    }
+    return false;
+  }
+
+  if (!gssctx.verify(message, mac)) {
+    L<<Logger::Error<<"Could not verify message using GSS context '"<<context<<"'"<<endl;
+    BOOST_FOREACH(const string& error, gssctx.getErrorStrings()) {
+       L<<Logger::Error<<"GSS error: "<<error<<endl;;
+    }
+    return false;
+  }
+  return true;
+}
diff --git a/pdns/gss_context.hh b/pdns/gss_context.hh
new file mode 100644 (file)
index 0000000..1e48d25
--- /dev/null
@@ -0,0 +1,163 @@
+#ifndef GSS_CONTEXT_HH
+#define GSS_CONTEXT_HH
+#pragma once
+
+#ifdef ENABLE_GSS_TSIG
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_ext.h>
+#endif
+
+//! Generic errors
+enum GssContextError {
+  GSS_CONTEXT_NO_ERROR,
+  GSS_CONTEXT_UNSUPPORTED,
+  GSS_CONTEXT_NOT_FOUND,
+  GSS_CONTEXT_NOT_INITIALIZED,
+  GSS_CONTEXT_INVALID,
+  GSS_CONTEXT_EXPIRED,
+  GSS_CONTEXT_ALREADY_INITIALIZED
+};
+
+//! GSS context types
+enum GssContextType {
+  GSS_CONTEXT_NONE,
+  GSS_CONTEXT_INIT,
+  GSS_CONTEXT_ACCEPT
+};
+
+class GssSecContext;
+
+/*! Class for representing GSS names, such as host/host.domain.com@REALM.
+*/
+class GssName {
+public:
+  //! Initialize to empty name
+  GssName() {
+    setName("");
+  };
+
+  //! Initilize using specific name
+  GssName(const std::string& name) {
+    setName(name);
+  };
+
+  //! Parse name into native representation
+  bool setName(const std::string& name) {
+#ifdef ENABLE_GSS_TSIG
+    gss_buffer_desc buffer;
+    d_name = GSS_C_NO_NAME;
+
+    if (!name.empty()) {
+      buffer.length = name.size();
+      buffer.value = (void*)name.c_str();
+      d_maj = gss_import_name(&d_min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &d_name);
+      return d_maj == GSS_S_COMPLETE;
+    }
+
+    return true;
+#endif
+    return false;
+  };
+
+  ~GssName() {
+#ifdef ENABLE_GSS_TSIG
+     if (d_name != GSS_C_NO_NAME)
+       gss_release_name(&d_min, &d_name);
+#endif
+  };
+
+  //! Compare two Gss Names, if no gss support is compiled in, returns false always
+  //! This is not necessarely same as string comparison between two non-parsed names
+  bool operator==(const GssName& rhs) {
+#ifdef ENABLE_GSS_TSIG
+    OM_uint32 maj,min;
+    int result;
+    maj = gss_compare_name(&min, d_name, rhs.d_name, &result);
+    return (maj == GSS_S_COMPLETE && result != 0);
+#endif
+    return false;
+  }
+
+  //! Compare two Gss Names, if no gss support is compiled in, returns false always
+  //! This is not necessarely same as string comparison between two non-parsed names
+  bool match(const std::string& name) {
+#ifdef ENABLE_GSS_TSIG
+    OM_uint32 maj,min;
+    int result;
+    gss_name_t comp;
+    gss_buffer_desc buffer;
+    buffer.length = name.size();
+    buffer.value = (void*)name.c_str();
+    maj = gss_import_name(&min, &buffer, (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME, &comp);
+    if (maj != GSS_S_COMPLETE)
+      throw PDNSException("Could not import " + name + ": " + boost::lexical_cast<std::string>(maj) + string(",") + boost::lexical_cast<std::string>(min));
+    // do comparison
+    maj = gss_compare_name(&min, d_name, comp, &result);
+    gss_release_name(&min, &comp);
+    return (maj == GSS_S_COMPLETE && result != 0);
+#else
+   return false;
+#endif
+  };
+
+  //! Check if GSS name was parsed successfully.
+  bool valid() {
+#ifdef ENABLE_GSS_TSIG
+    return d_maj == GSS_S_COMPLETE;
+#else
+    return false;
+#endif
+  }
+private:
+#ifdef ENABLE_GSS_TSIG
+  OM_uint32 d_maj,d_min;
+  gss_name_t d_name;
+#endif
+};
+
+class GssContext {
+public:
+  static bool supported(); //<! Returns true if GSS is supported in the first place
+  GssContext(); //<! Construct new GSS context with random name
+  GssContext(const std::string& label); //<! Create or open existing named context
+
+  void setLocalPrincipal(const std::string& name); //<! Set our gss name
+  bool getLocalPrincipal(std::string& name); //<! Get our name
+  void setPeerPrincipal(const std::string& name); //<! Set remote name (do not use after negotiation)
+  bool getPeerPrincipal(std::string &name); //<! Return remote name, returns actual name after negotatioan
+
+  void generateLabel(const std::string& suffix); //<! Generate random context name using suffix (such as mydomain.com)
+  void setLabel(const std::string& label); //<! Set context name to this label
+  const std::string& getLabel() { return d_label; } //<! Return context name
+
+  bool init(const std::string &input, std::string& output); //<! Perform GSS Initiate Security Context handshake
+  bool accept(const std::string &input, std::string& output); //<! Perform GSS Acccept Security Context handshake
+  bool destroy(); //<! Release the cached context
+  bool expired(); //<! Check if context is expired
+  bool valid(); //<! Check if context is valid
+
+  bool sign(const std::string &input, std::string& output); //<! Sign something using gss
+  bool verify(const std::string &input, const std::string &signature); //<! Validate gss signature with something
+
+  GssContextError getError(); //<! Get error
+  const std::vector<std::string> getErrorStrings() { return d_gss_errors; } //<! Get native error texts
+ private:
+  void release(); //<! Release context
+  void initialize(); //<! Initialize context
+#ifdef ENABLE_GSS_TSIG
+  void processError(const string& method, OM_uint32 maj, OM_uint32 min); //<! Process and fill error text vector
+#endif
+  std::string d_label; //<! Context name
+  std::string d_peerPrincipal; //<! Remote name
+  std::string d_localPrincipal; //<! Our name
+  GssContextError d_error; //<! Context error
+  GssContextType d_type; //<! Context type
+  std::vector<std::string> d_gss_errors; //<! Native error string(s)
+  boost::shared_ptr<GssSecContext> d_ctx; //<! Attached security context
+};
+
+bool gss_add_signature(const std::string& context, const std::string& message, std::string& mac); //<! Create signature
+bool gss_verify_signature(const std::string& context, const std::string& message, const std::string& mac); //<! Validate signature
+
+#endif