Socks 5: Continuing on Debian

In this installment, we will be starting an implementation of the same factory we started with SSPI, but using GSS-API. We’ll be doing this one a vanilla Debian-Lenny system, with one caveat: we’ll be using our own compiled version of the Boost libraries. I’ve created a branch in the vlinder-sdk repository for Debian (here) where you can download a ready-compiled version of Boost for Debian.

Most of the implementation of what we had so far can simply be copied to the Linux version. Changing SSPI to GSSAPI will fix most of the issues – except of course the getAvailableMechanisms function. Now, my first intention was to replace the call to EnumerateSecurityPackages with a call to gss_indicate_mechs, but things get a bit more complicated than that: gss_indicate_mechs gives us a set of OIDs, which are opaque. There is no standard way to extract the names from them, which is a bit of a pity. We’ll still use this function to know whether we can create an instance of a given mechanism, but we won’t be able to report the names of available mechanisms – at least until Debian upgrades to a more recent version of GNU GSS.

This gives me an occasion to show you how to allow different implementations of a factory to have different capabilities – and thus loosen up the definition of the abstract factory’s interface a bit. Let’s have a look how we’d do that.

The idea is to let the users of the mechanism factory know which optional features are implemented and which aren’t. In this case, the getAvailableMechanisms function is optional, and is indicated by has_get_available_mechanisms_.

diff --git a/lib/security/MechanismFactory.h b/lib/security/MechanismFactory.h
index 8e9eb7d..ea1b1a5 100644
--- a/lib/security/MechanismFactory.h
+++ b/lib/security/MechanismFactory.h
@@ -10,12 +10,23 @@ namespace Vlinder { namespace Chausette { namespace Security {
        class VLINDER_CHAUSETTE_SECURITY_API MechanismFactory
        {
        public :
+               struct Capabilities
+               {
+                       Capabilities(bool has_get_available_mechanisms)
+                               : has_get_available_mechanisms_(has_get_available_mechanisms)
+                       { /* no-op */ }
+
+                       bool has_get_available_mechanisms_;
+               };
+
                static MechanismFactory & getInstance();
 
+               virtual Capabilities getCapabilities() const = 0;
+
                virtual Mechanism * getDefaultMechanism() const = 0;
                virtual void releaseMechanism(Mechanism * mechanism) = 0;
 
-               virtual std::vector< std::string > getAvailableMechanisms() const = 0;
+               virtual std::vector< std::string > getAvailableMechanisms() const;
 
        protected :
                MechanismFactory();

You’ll note, of course, that the = 0 is gone from behind the getAvailableMechanisms declaration, so we can have a default implementation and the function becomes optional. The default implementation, of course, looks like this:

diff --git a/lib/security/MechanismFactory.cpp b/lib/security/MechanismFactory.cpp
index 970d0da..c0c6a08 100644
--- a/lib/security/MechanismFactory.cpp
+++ b/lib/security/MechanismFactory.cpp
@@ -11,6 +11,11 @@ namespace Vlinder { namespace Chausette { namespace Security {
                else
                        throw std::logic_error("Factory not initialized");
        }
+
+       /*virtual */std::vector< std::string > MechanismFactory::getAvailableMechanisms() const
+       {
+               throw std::logic_error("Not implemented");
+       }
 
        MechanismFactory::MechanismFactory()
        { /* no-op */ }

Now, let’s continue our porting effort and implement an actual constructor for the Mechanism class in the GSSAPI version. We’ll start out with the code as taken from the documentation provided by Sun:

diff --git a/lib/gssapi/Mechanism.cpp b/lib/gssapi/Mechanism.cpp
index a5cde26..40dcdfc 100644
--- a/lib/gssapi/Mechanism.cpp
+++ b/lib/gssapi/Mechanism.cpp
@@ -1,8 +1,49 @@
 #include "Mechanism.h"
 
 namespace Vlinder { namespace Chausette { namespace GSSAPI {
-       Mechanism::Mechanism()
-       { /* no-op */ }
+       Mechanism::Mechanism(const char * mechanism)
+       {
+               char * mechstr = 0, cp;
+               gss_buffer_desc tok;
+               OM_uint32 maj_stat, min_stat;
+
+               if (mechanism && is_digit(mechanism[0]))
+               {
+                       mechstr = malloc(strlen(mechanism) + 5);
+                       if (!mechstr)
+                               throw std::bad_alloc();
+                       else
+                       { /* all is well */ }
+                       sprintf(mechstr, "{ %s }", mechanism);
+                       for (cp = mechstr; *cp; ++cp)
+                       {
+                               if (*cp == '.')
+                               {
+                                       *cp = ' ';
+                               }
+                               else
+                               { /* not a dot */ }
+                       }
+                       tok.value = mechstr;
+               }
+               else
+                       tok.value = mechanism;
+               tok.length = strlen(tok.value);
+               maj_stat = gss_str_to_oid(&min_stat, &tok, &oid_);
+               if (maj_stat != GSS_S_COMPLETE)
+               {
+                       display_status("str_to_oid", maj_stat, min_stat);
+                       throw std::runtime_error("str_to_oid failed");
+               }
+               else
+               { /* all is well */ }
+               if (mechstr)
+               {
+                       free(mechstr);
+               }
+               else
+               { /* didn't allocate */ }
+       }
 
        /*virtual */Mechanism::~Mechanism()
        { /* no-op */ }

If you’ve been paying attention these last few months, you’ll notice a few things that are wrong with this code, which we will correct in our next patch (this one is here, by the way). A few things that might also strike you as odd if you’ve been really paying attention, but which we’ll keep anyway, are these bits:

diff --git a/lib/gssapi/Mechanism.h b/lib/gssapi/Mechanism.h
index de903f9..4671d9d 100644
--- a/lib/gssapi/Mechanism.h
+++ b/lib/gssapi/Mechanism.h
@@ -3,19 +3,22 @@
 
 #include "../security/Mechanism.h"
 #include "Details/prologue.h"
+#include <gss/api.h>
 
 namespace Vlinder { namespace Chausette { namespace GSSAPI {
        class VLINDER_CHAUSETTE_GSSAPI_API Mechanism
                : public Security::Mechanism
        {
        public :
-               Mechanism();
+               Mechanism(const char * mechanism);
                virtual ~Mechanism();
 
        private :
                // neither CopyConstructible nor Assignable
                Mechanism(const Mechanism &);
                Mechanism & operator=(const Mechanism &);
+
+               gss_OID oid_;
        };
 }}}

Now, like I said, there are a few oddities about this, first of which it that it doesn’t actually compile because it uses extensions to the GSS API. Before we fix that, though, we will fix the C-vs-C++ issues:

diff --git a/lib/gssapi/Mechanism.cpp b/lib/gssapi/Mechanism.cpp
index 40dcdfc..72247a8 100644
--- a/lib/gssapi/Mechanism.cpp
+++ b/lib/gssapi/Mechanism.cpp
@@ -1,21 +1,22 @@
 #include "Mechanism.h"
+#include <vector>
+#include <boost/algorithm/string.hpp>
+#include <gss.h>
 
 namespace Vlinder { namespace Chausette { namespace GSSAPI {
-	Mechanism::Mechanism(const char * mechanism)
+	Mechanism::Mechanism(const std::string & mechanism)
 	{
-		char * mechstr = 0, cp;
-		gss_buffer_desc tok;
+		using namespace std;
+		using boost::algorithm::is_digit;
+
+		vector< char > buffer(mechanism.size() + 5 /* overhead for "{  }" */);
+		gss_buffer_desc token;
 		OM_uint32 maj_stat, min_stat;
 
-		if (mechanism && is_digit(mechanism[0]))
+		if (!mechanism.empty() && is_digit()(mechanism[0]))
 		{
-			mechstr = malloc(strlen(mechanism) + 5);
-			if (!mechstr)
-				throw std::bad_alloc();
-			else
-			{ /* all is well */ }
-			sprintf(mechstr, "{ %s }", mechanism);
-			for (cp = mechstr; *cp; ++cp)
+			sprintf(&buffer[0], "{ %s }", mechanism.c_str());
+			for (vector< char >::iterator cp = buffer.begin(); *cp; ++cp)
 			{
 				if (*cp == '.')
 				{
@@ -24,12 +25,12 @@ namespace Vlinder { namespace Chausette { namespace GSSAPI {
 				else
 				{ /* not a dot */ }
 			}
-			tok.value = mechstr;
+			token.value = &buffer[0];
 		}
 		else
-			tok.value = mechanism;
-		tok.length = strlen(tok.value);
-		maj_stat = gss_str_to_oid(&min_stat, &tok, &oid_);
+			token.value = const_cast< char* >(&mechanism[0]);
+		token.length = strlen((const char *)token.value);
+		maj_stat = gss_str_to_oid(&min_stat, &token, &oid_);
 		if (maj_stat != GSS_S_COMPLETE)
 		{
 			display_status("str_to_oid", maj_stat, min_stat);
@@ -37,12 +38,6 @@ namespace Vlinder { namespace Chausette { namespace GSSAPI {
 		}
 		else
 		{ /* all is well */ }
-		if (mechstr)
-		{
-			free(mechstr);
-		}
-		else
-		{ /* didn't allocate */ }
 	}
 
 	/*virtual */Mechanism::~Mechanism()
diff --git a/lib/gssapi/Mechanism.h b/lib/gssapi/Mechanism.h
index 4671d9d..298fac5 100644
--- a/lib/gssapi/Mechanism.h
+++ b/lib/gssapi/Mechanism.h
@@ -4,13 +4,14 @@
 #include "../security/Mechanism.h"
 #include "Details/prologue.h"
 #include <gss/api.h>
+#include <string>
 
 namespace Vlinder { namespace Chausette { namespace GSSAPI {
 	class VLINDER_CHAUSETTE_GSSAPI_API Mechanism
 		: public Security::Mechanism
 	{
 	public :
-		Mechanism(const char * mechanism);
+		Mechanism(const std::string & mechanism = std::string());
 		virtual ~Mechanism();
 
 	private :

By now, the difference should be obvious: we’re using RAII throughout by using the vector as a buffer, and we’re using a string because it handles its own memory as well. You may also note that the constructor takes a default value for its argument, allowing us to use an empty name for the token. While you’re noting stuff, note that we included the header for string in our header file: while we try to use forward declarations as much as possible, std::string is a typedef of std::basic_string< char > and unless we want to clutter our code with forward declarations of the string and its typedef, we’ll include it. Suffice it to say that this is a very common header, so including it here is less costly than it would be if we were including all kinds of implementation details that the user really doesn’t care about.

All the specific memory management code is also gone, because it has now become implicit by our use of RAII, so using RAII, once again, is not only safer: it also provides for cleaner, and easier to read, code.

Now then, we will change the meaning of the code a bit: we will not be able to provide a name for a mechanism to use, because GSS-API doesn’t support that. What it does support, though, is providing a set of mechanisms that can handle a given name:

diff --git a/lib/gssapi/Mechanism.cpp b/lib/gssapi/Mechanism.cpp
index 72247a8..c663b1a 100644
--- a/lib/gssapi/Mechanism.cpp
+++ b/lib/gssapi/Mechanism.cpp
@@ -1,43 +1,84 @@
 #include "Mechanism.h"
-#include <vector>
-#include <boost/algorithm/string.hpp>
-#include <gss.h>
+#include <stdexcept>
 
-namespace Vlinder { namespace Chausette { namespace GSSAPI {
-	Mechanism::Mechanism(const std::string & mechanism)
+namespace {
+	struct ScopedName
 	{
-		using namespace std;
-		using boost::algorithm::is_digit;
+		ScopedName()
+			: acquired_(false)
+		{ /* no-op */ }
 
-		vector< char > buffer(mechanism.size() + 5 /* overhead for "{  }" */);
-		gss_buffer_desc token;
-		OM_uint32 maj_stat, min_stat;
+		~ScopedName()
+		{
+			release();
+		}
 
-		if (!mechanism.empty() && is_digit()(mechanism[0]))
+		gss_name_t * operator&()
 		{
-			sprintf(&buffer[0], "{ %s }", mechanism.c_str());
-			for (vector< char >::iterator cp = buffer.begin(); *cp; ++cp)
+			return &name_;
+		}
+
+		void setAcquired()
+		{
+			acquired_ = true;
+		}
+
+		void release()
+		{
+			if (acquired_)
 			{
-				if (*cp == '.')
-				{
-					*cp = ' ';
-				}
-				else
-				{ /* not a dot */ }
+				OM_uint32 minor;
+				gss_release_name(&minor, &name_);
+				acquired_ = false;
 			}
-			token.value = &buffer[0];
 		}
-		else
-			token.value = const_cast< char* >(&mechanism[0]);
-		token.length = strlen((const char *)token.value);
-		maj_stat = gss_str_to_oid(&min_stat, &token, &oid_);
-		if (maj_stat != GSS_S_COMPLETE)
+
+		bool acquired_;
+		gss_name_t name_;
+
+	private :
+		ScopedName(const ScopedName &);
+		ScopedName & operator=(const ScopedName &);
+	};
+}
+
+namespace Vlinder { namespace Chausette { namespace GSSAPI {
+	Mechanism::Mechanism(const std::string & mechanism)
+		: allocated_(false)
+	{
+		if (!mechanism.empty())
 		{
-			display_status("str_to_oid", maj_stat, min_stat);
-			throw std::runtime_error("str_to_oid failed");
+			gss_buffer_desc token;
+			OM_uint32 maj_stat, min_stat;
+			token.value = const_cast< char* >(mechanism.c_str());
+			token.length = mechanism.size();
+			ScopedName name;
+			maj_stat = gss_import_name(
+						&min_stat,
+						&token,
+						GSS_C_NT_HOSTBASED_SERVICE,
+						&name);
+			if (GSS_ERROR(maj_stat))
+			{
+				throw std::runtime_error("import_name failed");
+			}
+			else
+			{ /* all is well */ }
+			name.setAcquired(); // for RAII
+			maj_stat = gss_inquire_mechs_for_name(
+							&min_stat,
+							name.name_,
+							&oids_);
+			if (GSS_ERROR(maj_stat))
+			{
+				throw std::runtime_error("inquire_mechs_for_name failed");
+			}
+			else
+			{ /* all is well */ }
+			allocated_ = true;
 		}
 		else
-		{ /* all is well */ }
+		{ /* nothing to do */ }
 	}
 
 	/*virtual */Mechanism::~Mechanism()
diff --git a/lib/gssapi/Mechanism.h b/lib/gssapi/Mechanism.h
index 298fac5..5c246b5 100644
--- a/lib/gssapi/Mechanism.h
+++ b/lib/gssapi/Mechanism.h
@@ -19,7 +19,8 @@ namespace Vlinder { namespace Chausette { namespace GSSAPI {
 		Mechanism(const Mechanism &);
 		Mechanism & operator=(const Mechanism &);
 
-		gss_OID oid_;
+		gss_OID_set oids_;
+		bool allocated_;
 	};
 }}}

Of course this changes the usage a bit, and users should be aware of that, so we’ll add a field to the Capabilities structure indicating what the name means:

diff --git a/lib/gssapi/MechanismFactory.cpp b/lib/gssapi/MechanismFactory.cpp
index 3394f96..8cd1e4b 100644
--- a/lib/gssapi/MechanismFactory.cpp
+++ b/lib/gssapi/MechanismFactory.cpp
@@ -66,7 +66,7 @@ namespace Vlinder { namespace Chausette { namespace GSSAPI {
 
 	/*virtual */MechanismFactory::Capabilities MechanismFactory::getCapabilities() const/* = 0*/
 	{
-		return Capabilities(false);
+		return Capabilities(false, Capabilities::query_mechanisms_supporting_name__);
 	}
 
 	/*virtual */Mechanism * MechanismFactory::getDefaultMechanism() const/* = 0*/
diff --git a/lib/security/MechanismFactory.h b/lib/security/MechanismFactory.h
index ea1b1a5..a74fbe0 100644
--- a/lib/security/MechanismFactory.h
+++ b/lib/security/MechanismFactory.h
@@ -12,11 +12,19 @@ namespace Vlinder { namespace Chausette { namespace Security {
 	public :
 		struct Capabilities
 		{
-			Capabilities(bool has_get_available_mechanisms)
+			enum MechanismNameMeaning {
+				name_of_mechanism__,
+				query_mechanisms_supporting_name__,
+			};
+			Capabilities(
+				bool has_get_available_mechanisms,
+				MechanismNameMeaning mechanism_name_meaning = name_of_mechanism__)
 				: has_get_available_mechanisms_(has_get_available_mechanisms)
+				, mechanism_name_meaning_(mechanism_name_meaning)
 			{ /* no-op */ }
 
 			bool has_get_available_mechanisms_;
+			MechanismNameMeaning mechanism_name_meaning_;
 		};
 
 		static MechanismFactory & getInstance();

This type of problem should be avoided as much as possible: if at all possible, factories should all implement the same functionality and the Capabilities structure – if one is there at all – should be kept to a struct minimum. In this case, however, that strict minimum is still a bit (annoyingly) stuffed with complexity, because SSPI and GSSAPI, though only two letters apart in their acronyms, are two worlds apart in their implementations.

About rlc

Software Analyst in embedded systems and C++, C and VHDL developer, I specialize in security, communications protocols and time synchronization, and am interested in concurrency, generic meta-programming and functional programming and their practical applications. I take a pragmatic approach to project management, focusing on the management of risk and scope. I have over two decades of experience as a software professional and a background in science.
This entry was posted in C++ for the self-taught and tagged , . Bookmark the permalink.

One Response to Socks 5: Continuing on Debian

  1. Pingback: Socks 5: Continuing on Debian | Debian-News.net - Your one stop for news about Debian

Comments are closed.