Subject: [kitten] PGSSAPI, take two

From: Nico Williams <[email-masked]>
Date: Thu, 24 Mar 2011 03:08:11 -0500

A few years ago there was a proposal for a "PGSSAPI" where GSS
functions were augmented with an additional caller context argument.
I then proposed overloading the minor_status argument, but no one
liked that.  Later I proposed a GSS-APIv3, but that won't happen
anytime soon.  Then Love proposed just adding an additional context
argument to a small handful of new functions.

I'm starting to think that we need so badly to add such this feature
(caller contexts) to the API real soon.  Here's an idea: if apps
define a C macro called, say, GSS_C_APPLICATION_PROFILE, then
GSS_C_NO_NAME and GSS_C_NO_CREDENTIAL could expand into function
calls.  For convenience we could also add new a version of
gss_import_name() that takes a profile name, plus a way to call it to
get a gss_name_t that is equivalent to GSS_C_NO_NAME.  The rest would
follow.

I.e., something like this in the GSS C header:

#ifdnef GSS_C_APPLICATION_PROFILE
#define GSS_C_NO_NAME ((gss_name_t)NULL)
#define GSS_C_NO_CREDENTIAL((gss_cred_id_t)NULL)
#else
#define GSS_C_NO_NAME (__gss_c_no_name(GSS_C_APPLICATION_PROFILE))
#define GSS_C_NO_CREDENTIAL (__gss_c_no_credential(GSS_C_APPLICATION_PROFILE))
#endif

then have those __gss_c_no_*() functions use thread-local storage to
store thread-local name/cred object handles.

Apps would:

#define GSS_C_APPLICATION_PROFILE "imap-client"
#include <gssapi/gssapi.h>

No other application changes would be needed, so this would be quite
an unintrusive change.

For completeness we might want to define a macro to indicate support
for this feature.

The mechglue SPI would be extended with entry points corresponding to
these new functions (__gss_c_no_name() and so on), of course.

What the mechglue and mechanisms do with the profile would be up to
them.  But quite a bit of policy that various folks have been needing
to profile could be referred to in this way.  If we need something
more complex than a profile name, well, we could do something about
that too (or we could do that later).

Nico
--

---

From: Martin Rex <[email-masked]>
Date: Fri, 25 Mar 2011 02:52:56 +0100 (MET)

> functions were augmented with an additional caller context argument.

Admittedly, my memories about this discussion are quite weak.

> #define GSS_C_NO_CREDENTIAL (__gss_c_no_credential(GSS_C_APPLICATION_PROFILE))

I completely fail to understand the motivation behind this.
Do you have any specific example about the purpose or what exactly
you're trying to achieve.  The use of these particular constants 
is limited to very specific purposes, so if you desire is to get
an additional context handle, this would leave all other usage
scenarios of the gss-api calls out in the cold.

To me, this particular example looks quite incompatible to the
GSS-API v2 architecture and design.

GSS_C_NO_NAME and GSS_C_NO_CREDENTIAL are really only shortcuts
to represent the _absence_ of a parameter -- on both, input to
a GSS-API call as well as output of a GSS-API call.

When used as input, the absence of a credentials parameter is
supposed to indicate a request for a "default credential",
a concept described here:

  http://tools.ietf.org/html/rfc2743#page-8

GSS_C_NO_NAME is somewhat different, but related to GSS_C_NO_CREDENTIALS.
There is no such thing as a "default name" in GSS-API, but in order to
be able to acquire an explicit credentials handle to a credential
with "default credential behaviour", gss_acquire_cred() was modified
to allow not passing a name on input (dubbed "GSS_C_NO_NAME") to
obtain an explicit credentials handle to a credential that exhibits
default credential behaviour.

GSS_C_NO_NAME is not allowed for other gss-api calls with a
gss_name_t input parameter (such as gss_compare_name,
gss_canonicalize_name or gss_export_name), because of this.

GSS-API functions are allowed to fail, and still return output
parameters, so it is really important to be able to recognize
whether an output parameter is present (which implies it must
be released by the caller later on) or absent.

e.g. gss_init_sec_context() and gss_acccept_sec_context() should
always return a mech_OID when either of the calls fails and returns
a minor_status value, so that gss_display_status() has a clue
which mechanism needs to translate the minor_status value into
readable text.

similarly, message unprotection call gss_unwrap()
may fail and still return unprotected data in a buffer.  It is at
the discretion of the GSS-API mechanism to fail with

e.g.
  (GSS_S_FAILURE|GSS_S_GAP_TOKEN) and no unprotected data buffer
or
  (GSS_S_COMPLETE|GSS_S_GAP_TOKEN) and unprotected data

similar for other message sequencing related status codes
  DUPLICATE, OLD, UNSEQ

also a gss-api mechanism may or may not return unprotected data
from gss_unwrap() that failes with GSS_S_CONTEXT_EXPIRED.

> #include <gssapi/gssapi.h>

YUCK!

An approach that provides binary plug-n-play capabilities would
be so much more useful.  Having to completely recompile everything
from scratch every time something is changed in a gssapi mechanism
would be extremely annoying.

Also, expecting applications to #include a mechanism-specific
header file during compilation of the application is a huge problem
by itself.  A design that limits an application to access only
a single gssapi mechanism per application binary seems awkward to me.

You really want your application binary to be independent from
any particular gssapi mechanism (or worse, a very specific version
of that mechanism).  Binary plug-n-play means all gssapi header
files must be 100% binary compatible to each other, and at that
point, the app simply compiles with its own gssapi header file
exclusively.

For us, binary plug-n-play of GSS-API mechanisms has been working
extremely well throughout the last 15 years with more than a
dozen independent products.
(Except maybe for the hosed Apples Kerberos 5 on Mac OS X 64-bit,
 which has been compiled with a #pragma pack(push,2) misalignment,
 defying binary interop with mechanism-independent apps and
 more reasonably compiled gssapi mechanism for that platform).

Some background info why the use of #pragma pack(push,2)
for a public API might not be a terribly good idea:
http://blogs.msdn.com/ce_base/archive/2008/06/27/when-not-to-pack-structures.aspx

-Martin

---

From: Nico Williams <[email-masked]>
Date: Fri, 25 Mar 2011 12:42:39 -0500

On Thu, Mar 24, 2011 at 8:52 PM, Martin Rex <[email-masked]> wrote:
> [...]

See my follow-up to Jeff.  I should have included that background
information when I posted this.

> a GSS-API call as well as output of a GSS-API call.

In the C bindings they are also implementation-specific (since the
gss_name_t and gss_cred_id_t types are also implementation-specific).
Thus it seems perfectly fair for these values to be macros that expand
into function calls (not too unlike what happened to 'errno' in the
world of pthreads).

I'd worry more about comparisons of GSS_C_NO_{NAME, CREDENTIAL} to
values of gss_name_t/gss_cred_id_t output by inquiry functions (and
deleg_cred in the gss_accept_sec_context() case).  But the
implementation can easily arrange for that to work out.  (This means
that these values cannot be thread-specific, but must be
process-global.  It also means that they leak until the GSS-API
library de-initializes, which only really happens implicitly when the
shared object is unloaded, which mostly only happens when the process
exits.  I'm willing to live with this.)

> gss_canonicalize_name or gss_export_name), because of this.

GSS_C_NO_NAME is allowed as an input to
gss_acquire_cred/gss_add_cred().  A credential whose desired_name is
GSS_C_NO_NAME is akin to GSS_C_NO_CREDENTIAL (cred-usage,
desired_mechs, and so on can be different from those of
GSS_C_NO_CREDENTIAL).

In any case, no one passes GSS_C_NO_NAME to gss_compare_name() and so
on, but it's perfectly possible for the implementor to check input
values of gss_name_t to see that they are not any known value of
GSS_C_NO_NAME.  For example, an implementation whose gss_name_t is a
pointer type could use values 1-4095 to encode values of GSS_C_NO_NAME
corresponding to specific profiles, or it could use the two least
significant bits of the pointer value to do the same.  Similarly for
GSS_C_NO_CREDENTIAL.  (Technically such tricks are non-portable, in
practice they are perfectly portable to all the major OSes and C
compilers I know of, and should even be portable to strange
architectures such as the Burrows, none of which really exist today,
and even if not, implementors on such architectures should be able to
find workable ways to encode these things.)

> be released by the caller later on) or absent.

See above!

> would be extremely annoying.

Why would this not be binary compatible with prior versions of the
same implementation? Of course it is!  Older object code will be using
the original GSS_C_NO_{NAME, CREDENTIAL} value (almost everywhere
NULL, or 0), which the new implementation should gladly accept and
treat as intended.  Look ma!  ABI compatible!

> a single gssapi mechanism per application binary seems awkward to me.

Who said anything about a mechanism-specific header?!  I didn't.

> point, the app simply compiles with its own gssapi header file

Remember, the header in RFC2744 is NOT complete.  RFC2744 does NOT
specify an ABI.  (Some of us wish it had, but that's another story.)

Nico
--

---

From: Martin Rex <[email-masked]>
Date: Fri, 25 Mar 2011 19:58:01 +0100 (MET)

Nico Williams wrote:
> > of that mechanism). Â Binary plug-n-play means all gssapi header

Are you refering to these 4 definitions:

   typedef <platform-specific> gss_ctx_id_t;
   typedef <platform-specific> gss_cred_id_t;
   typedef <platform-specific> gss_name_t;

   typedef <platform-specific> gss_uint32;

that portable apps (that started out with 16-bit Windows support in 1995)
defined like this:

   typedef void FAR * gss_ctx_id_t;
   typedef void FAR * gss_cred_id_t;
   typedef void FAR * gss_name_t;

   #include <limits.h>
   #if UINT_MAX >= 0x10000ul
      /* Win 16-bit with 16-bit ints */
     typedef  unsigned int  gss_uint32;
   #else
     typedef  unsigned long gss_uint32;
   #endif

Nope, the interop problem I've encountered didn't affect any of those,
but instead this one:

typedef struct gss_OID_desc_struct {
      OM_uint32       length;
      void      FAR * elements;
} gss_OID_desc, FAR * gss_OID;

Which requires a padding between "length" and "elements" on 64-bit platforms,
and there had a differing definition been used by X/Open (which caused
very brief interop problems on HP-UX 11.0, but HP was quick to fix this),
and reliably causes bus errors on Mac OS X due to the #pragma pack(push,2)
in Apple's weird gssapi.h header.

-Martin

---

From: Nico Williams <[email-masked]>
Date: Fri, 25 Mar 2011 14:53:04 -0500

>   typedef void FAR * gss_name_t;

Yes, though implementations nowadays define them like this instead:

typedef struct gss_ctx_id  *gss_ctx_id_t;
typedef struct gss_cred_id *gss_cred_id_t;
typedef struct gss_name *gss_name_t;

That's from Solaris.  MIT krb5 does the same thing, with more lines:

struct gss_name_struct;
typedef struct gss_name_struct * gss_name_t;

struct gss_cred_id_struct;
typedef struct gss_cred_id_struct * gss_cred_id_t;

struct gss_ctx_id_struct;
typedef struct gss_ctx_id_struct * gss_ctx_id_t;

This is *much* better than void * because you actually get static type
safety this way.  With void * if you pass in a gss_cred_id_t value
where a gss_name_t was expected, the compiler does not complain,
whereas with pointers to incomplete structs the compiler does.

>     typedef  unsigned long gss_uint32;

This is different and unrelated.  (With C99 we could use
uint_least_32_t or whatever it is, except that I'd rather go whole hog
for 64 bits for such things in version 3 of the API, but that's
another story, for another day.)

> in Apple's weird gssapi.h header.

Right.  Exposing structs' fields and sizes in APIs is a recipe for
disaster.  One of the biggest issues in writing a pluggable mechglue
with a public plugin interface is memory management, and that's the
result of the API having a gss_buffer_t type that is not extensible.
Buffers (and OIDs, and OID sets) should have been opaque, with
function accessors.  Sound heavy-weight?  But this is a library whose
plugins do _crypto_ operations, and I/O too (network I/O in some
cases, no less, with no async version of the relevant functions), so,
no, opaque-everything-plus-accessor-functions is not to heavy-weight.
Of course, it would have been possible to design public structs that
didn't create these memory management problems, but it'd have been
easier to just go the opaque route.

Nico
--

---

From: Nico Williams <[email-masked]>
Date: Fri, 25 Mar 2011 16:44:08 -0500

To be fair, this proposal would break if there were libraries that
export interfaces that take or return gss_name_t or gss_cred_id_t
values and GSS_C_NO_NAME/CREDENTIAL can be used, but the break would
only happen if the application and the library use this new feature
and use different profiles, else there'd be no break.  I think that's
a reasonable limitation.

---

From: Sam Hartman <[email-masked]>
Date: Tue, 29 Mar 2011 15:47:38 -0400

I do not think this solves any of the need for context parameters I've
been running into lately.  I do think I want context parameters but I
don't think I want that so I can select between a compile-time set of
profiles.  Instead, I think I want it so I can have additional
configuration functions (I.E. setters) for the context.

So, at least for the use cases I know I have, this proposal would not
help.

I do share Martin's concern that you do not want to build more
mechglue-specific information into gssapi.h without a lot more thought.

---

From: Martin Rex <[email-masked]>
Date: Tue, 29 Mar 2011 23:16:18 +0200 (MEST)

> mechglue-specific information into gssapi.h without a lot more thought.

I had exchanged two more private EMails with Nico on this,
and as far as I understand, he is looking for a possibility to select
between different sets/profiles of default credentials.

I don't think that the "desired_name" parameter should be abused
for this purpose, and I also don't like the idea of a compile time
#define side effect rather than a programmatic feature.

Ideally, the additional requirements that have been piling up
(requesting specific attributes or selecting a profile, or
 offering a callback for password prompting a human user)
should be implemented with a new GSS-API call gss_acquire_cred_ex().

New API calls may incur a certain overhead for extremely layered
software stacks (including mechglue), so maybe Nico would prefer
to overload the semantics of the existing API call and parameters.

Personally, I would recommend to look at the gss_OID_set "desired_mechs"
parameter to gss_acquire_cred() or the "desired_mech" parameter
to gss_add_cred().

The ASN.1 OID space is virtually unlimited, and one might define OIDs
that aren't OIDs itself, but rather request for certain attributes
or profiles, so that this sequence of calls:

   def_cred = gss_acquire_cred( GSS_C_NO_NAME, GSS_C_NO_OID_SET)

   def_imap_cred = gss_add_cred( def_cred, IMAP_CRED_PROFILE_OID );

might be able to do what Nico is looking for (and the second call
would fail for mechanisms that don't recognize that non-mech-OID)

-Martin

I just noticed an (editorial) defect in rfc2743 at the top of page 38 
within 2.1.4:  GSS_Add_cred call, where it lists two output parameters
"cred_usage" and "mech_set" which probably should not be there.
"cred_usage" is already listed under input parameters an a pure input
parameter in the C-Bindings (rfc-2744), and "mech_set" appears to be
a duplicate of the "acutal_mechs" output parameter.

---

From: Martin Rex <[email-masked]>
Date: Tue, 29 Mar 2011 23:20:40 +0200 (MEST)

I'm sorry for the typo.
            s/that aren't OIDs itself/that aren't Mechanism-OIDs/
already applied in the quoted text.

Martin Rex wrote:
> that aren't Mechanism-OIDs, but rather request for certain attributes

---

From: Sam Hartman <[email-masked]>
Date: Thu, 31 Mar 2011 13:20:35 -0400

As an FYI, I don't think a callback for obtaining a password is a good
fit for use cases I care about.

There are a number of reasons.
One of the big ones has to do with common application design patterns.
Particularly because GSS-API blocks, it is desirable to run GSS-API in
separate threads from the user interface.
On several (all?) platforms, the user-interface typically runs in a
single thread with an event loop.
Having a callback rather than an asynchronous interface that wants UI is
problematic for these environments.

You *can* do it. Graphical login programs that interact with the PAM
stack are forced to do so.
However, it significantly complicates the application design.

Instead, I'd rather know that I could have initiated communication with
a particular service had passwords been supplied.

---

From: Volker Lendecke <[email-masked]>
Date: Thu, 31 Mar 2011 19:24:23 +0200

> a particular service had passwords been supplied.

While there -- some years ago I have had some chat with a
Kerberos developer about async non-blocking GSS-API. Is
there any work being done in that direction?

Thanks,

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen

---

From: Nico Williams <[email-masked]>
Date: Thu, 31 Mar 2011 12:38:19 -0500

> However, it significantly complicates the application design.

Well, PAM simply can't handle async anything.  Specifically it cannot
cancel current prompts (well, it might be possible to cancel the
thread that is running the PAM conversation function, but thread
cancellation in general seems like an iffy proposition).

Ideally the API would have async built-in, including async prompt cancellation.

What we need to do is: a) design portable and non-portable (well,
portablish, using a libevent-ABI-compatible set of function pointers
for hooking into the app's event loop) async GSS API, b) add in an
extension for interaction callbacks, c) add a function by which to
request cancellation of outstanding operations.

> a particular service had passwords been supplied.

That pattern is not really available for all mechanisms.  However, we
could have a mechanism attribute that indicates whether the initial
gss_init_sec_context() call succeeds only if there are target-specific
credentials cached (I don't have time to check, but, IIRC, we do have
such an attribute).

Nico
--

---

From: Nico Williams <[email-masked]>
Date: Thu, 31 Mar 2011 12:50:12 -0500

> there any work being done in that direction?

If async krb5 APIs materialize then it wouldn't be too hard to build
async GSS interfaces.

Last time I talked to Love I convinced him, I think (Love, correct me
if I'm misremembering please!) that we could do something like this:

 - add a libevent function pointer set and event base argument to the
functions we want to be async

Apps that use libevent can be made to use such GSS async APIs trivially.

Apss that don't use libevent need to provide libevent-ABI-compatible
wrappers to their event loops.

I.e., let's standardize the libevent API/ABI without having to force
everyone to use libevent proper.

Given the widespread ports of libevent, and its popularity, this seems
like the best approach to me.

Nico
--

---

From: Love Hörnquist Åstrand <[email-masked]>
Date: Thu, 31 Mar 2011 18:16:01 +0000

I agree with this, a callback will simply not work for the use cases what I care about.

This is esp true for GUI apps.

Love

31 mar 2011 kl. 10.20 skrev Sam Hartman:

> You *can* do it. GrapAl

> hical login programs that interact with the PAM
> _______________________________________________
> Kitten mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/kitten

---

From: Love Hörnquist Åstrand <[email-masked]>
Date: Thu, 31 Mar 2011 18:19:01 +0000

> if I'm misremembering please!) that we could do something like this:

Standardizing on libevent is a non starter for me.

Love

---

From: Volker Lendecke <[email-masked]>
Date: Thu, 31 Mar 2011 21:39:59 +0200

> Standardizing on libevent is a non starter for me.

The Avahi API might be worth a look:

http://avahi.org/download/doxygen/struct_avahi_poll.html

It might need extensions, but for pure socket and timeout
operations something like that looks like a good start.

It was pretty easy to integrate this int Samba's event loop.
I could imagine that other event loops are similar in this
respect.

Volker

---

From: Nico Williams <[email-masked]>
Date: Thu, 31 Mar 2011 15:36:04 -0500

Also, note that I was a fan of a callback-only API as the standard,
and then Love told me that'd be a non-starter for Samba folks, yet
it's also what Love prefers.

So then let me try one more proposal:

 - A standard set of new GSS functions that simply take a completion
callback function and data pointers, with all output parameters passed
to the callback.

 - An optional to implement set of new GSS functions that take an
event loop as an argument, where the event loop consists of a vector
of libevent-like functions (same prototypes, semantics) and a data
pointer that corresponds to a libevent event base.

   - Maybe N more optional to implement variants using different event
loop APIs.

I think this is pretty much what my last offer had been when we last
talked about this.

This should be relatively simple to implement and use, assuming
underlying Kerberos (and other) async APIs.

Nico
--

---

From: Luke Howard <[email-masked]>
Date: Fri, 1 Apr 2011 09:25:44 +1100

Async krb5 apis shipped in MIT 1.9. I think they exist in Heimdal for the AS.

Von meinem iPhone gesendet

---

From: Tom Yu <[email-masked]>
Date: Thu, 31 Mar 2011 20:29:15 -0400

> Standardizing on libevent is a non starter for me.

So you want a non-callback approach that doesn't require libevent?
Would you please elaborate on what your constraints are?