Subject: GSSAPI Conversation function proposal

From: [email-masked]
Date: Wed, 13 Jan 1999 02:44:35 -0500

At the Orlando IETF, in some private discussions with a number of CAT
working group members, I volunteer to write up a straw-man proposal for
a new interface to the GSSAPI library to allow mechanisms to perform I/O
with the user.  This would allow certain low-infrastructre-requiring
mechanisms to prompt the user for a username, password, SecureID
response, etc.  It might also be used by mechanisms such as kerberos
when the GSSAPI library wishs to obtain initial credentials itself,
instead of assuming that they are already present in the process.

Traditionally, this was ruled out of scope for the GSSAPI, but I think
we can make a strong case for this functionality, especially as we start
considering mechanisms that can't assume pre-existing cryptographic
credentials which were establishd at login time.

I'm told that Cybersafe has developd its own API for doing precisely
this.  If Cybersafe is willing to post its proposal, it should
definitely be considered, since my understanding is that they have
implemented it and are trying to use it.

Comments?

						- Ted

GSSAPI Conversation Function API

(Many ideas liberally stolen from the PAM specification, although things
have been changd to use the GSS "API style".)

OM_uint32
gss_register_conv_function(OM_uint32 *minor_status, struct gss_conv *conv);

This function registers a conversation function with the GSSAPI
library.  Once a conversation function has been registered, the GSSAPI
function calls gss_init_sec_context(), gss_acquire_cred(), and
gss_add_cred() may initiate a callback to the application-supplied
conversation function.

XXX This is does mean that we are tieing state to the GSSAPI library,
which isn't wonderful.  However, in practice, most GSSAPI
implementation have to have static state anyway.  An alternative is to
tie the converstaion function to the GSSAPI credentials --- but this
would require a new version of gss_acquire_cred (say,
gss_acquire_cred2) which included a new parameter for the gss_conv
structure, or other solutions which are even more awkward.

The gss_conv function shall have the following definition:

struct gss_conv {
	int (*conv)(OM_uint32 gss_num_msg,
		    const struct gss_conv_message *gss_msg,
		    struct gss_conv_message *gss_resp,
		    void *gss_appdata_ptr);
	void *gss_appdata_ptr;
};

The conversation function shall be passed two arrays of struct
gss_conv_message, which shall have the following type definition:

struct gss_conv_message {
	OMuint32     gss_msg_code;
	gss_buffer_t *gss_msg;
};

The first parameter, gss_num_msg specifies the number of messages to
be processed by the conversation function.  The GSSAPI library will
pass in two arrays of size gss_num_msg.  The first array, gss_msg,
contains a list of text messages which can be prompts or informational
text to be displayed to the user.  The second array, gss_resp, is
filled in by the conversation function.  For each entry in the gss_msg
which requires user input (i.e., GSS_CONV_ECHO_OFF or GSS_CONV_ECHO_ON
--- see below), the conversation function should fill in the user's
response in the corresponding gss_resp entry.

The first array, gss_msg, shall consist of the prompts and informational
text which should be displayed to the user.  The gss_msg_code field
shall have the following definitions in the gss_msg array:

#define GSS_CONV_ECHO_OFF	1
	/* Obtain a string without echoing any text */

#define GSS_CONV_ECHO_ON	2
	/* Obtain a string whilst echoing text */

#define GSS_CONV_ERROR_MSG	3
	/* Display an error */

#define GSS_CONV_TEXT_INFO	4
	/* Display some informational text. */

In the gss_resp array, the gss_msg_code field is reserved, and should
be set to zero by the conversation function.  

The conversation function returns the following integer codes to its
caller (the GSSAPI library):

#define GSS_CONV_SUCCESS	0	/* The user filled in the reqested
					   fields and all is well */
#define GSS_CONV_ABORT		1	/* The user requested an abort */

==========================================================================
This message was posted through the Stanford campus mailing list
server.  If you wish to unsubscribe from this mailing list, send the
message body of "unsubscribe ietf-cat-wg" to [email-masked]

---

From: "David S. Miller" <[email-masked]>
Date: Fri, 15 Jan 1999 17:12:35 -0800

Ted & CAT Group:

CyberSafe does enable conversation with the user in one of our extension
functions to GSS called csf_gss_acq_user().  This is a function to acquire
initial Kerberos V5 tickets.  The method we use however is particular to this
function, and not generalized, as the approach below is.  We have parameters for
the items seen in Ted's proposal:  prompts, labels, message status, responses.
One thing we do have that's not present in this proposal is a parameter to
identify which mechanism is doing the prompting.  This presumably would help in
a multimechanism implementation.  I'd recommend adding this.  Otherwise, my
feeling is that Ted's proposal below very much reflects the needs of the I/O
with the user.  CyberSafe is interested in participating in getting this API
extension solidified.

I have some other concerns however that I'd like to discuss, and a suggestion
for an alternative approach.  First a couple of points:

   * Having this new extension function means that we'd have to advise users
     when to call it.  As it's likely to be used by the credential acquisition
     functions which operate early on in a GSS app, I'd pretty much see no
     alternative other than to advise that this function call needs to be the
     first GSS call an application makes.  This almost has to be true since the
     application doesn't know which routines will prompt, and even if they did,
     portable programming advice would lead the developer to putting it first.

   * Having this new extension function also means, obviously, that the
     application would need to be modified to take advantage of it.  This means
     legacy V1 apps could not benefit from new mechs that conversed.  Even if a
     default conversation function is provided by an implementation, some
     platforms may not be able to use it.  Also, I as an implementor can't
     change my implementation of functions that currently converse, by a
     different means, to this new method without breaking existing apps.

   * As Ted indicates, storing the conversation function pointer makes the
     implementation stateful.  This would better be avoided, as there are some
     implementation headaches to cure:

        o Assuming this is done using static storage, this would become a
          non-threadsafe section of the library.  This would be pretty easy to
          overcome, however, and I don't see it as a real big issue.  It could
          be simply be declared that this portion of the library is not
          threadsafe, nor does it need to be.
        o To make the implementation most portable, I'd want to keep a list of
          the conversation functions on a per-process, or perhaps per-thread,
          basis.  This would cover any platforms for which a copy of the static
          storage area is not available to each process.  (These may not be the
          mainstream platforms, but portability across the maximum number of
          possible platforms is something I'd like to strive for.)  Doable, but
          another implementation complication.  The main point is that the list
          would best be kept indexed by some kind of GSS handle, not by a
          process or thread identity.  But we don't have any such thing.
   * Given these facts, I'd like to propose a number of solutions that come to
     mind, and I'm sure this doesn't cover them all:

          1.  Ted's proposal as-is could be implemented.  Advise the caller to
          call the registration function nice and early in their app.  Assume
          that a static storage location for the registration pointer is unique
          per-process, so that just one of these needs to be stored, not a list.
          2.  Identify the functions that could ever be expected to converse.
          Make a new version of these functions that have the conversation
          function parameter added to their calls.

          3.  Identify the functions that could ever be expected to converse.
          Add a "GSS handle" parameter to them that could itself encapsulate the
          conversation function pointer.  Some "set" or other function would be
          necessary to do the encapsulation.

          4.  Add a "GSS handle" parameter to all GSS functions in case we
          missed any cases in the identification task in alternative 3.

          5.  Do 4, but generalize the function to register the conversation
          function to a more generalized "gss_start()" call.  (Yes, I remember
          gss_open()).  Consider also adding the companion function "gss_end()"
          gss_start() would also initialize the conversation function.  (Yes,
          more robbery from the PAM spec.)

CyberSafe really wants to avoid platform dependencies, and for that reason 1.)
alone sounds like it could cause a problem in the future.  Are there platforms
on which a per-process or per-thread list of function addresses would need to be
kept?  If that's the case, would you identify these by process or thread ID?
Any problems associated with that?  What about the problem of losing the state?
In general, we'd like to avoid storing state in the library.  The rest of the
alternatives do that.

Alternative 2 avoids lots of problems, except extensibility.  As soon as we
think there's nothing else we'd want to add to the API as long as we're taking
the pain to change the interface, we'll think of something.  That's why if we're
going to change the interface at all, we might as well add something more
extensible, like a "GSS handle."  That's implying alternative 3.

But with 3, you're to the point where the interface is changed, apps have to be
modified, and you're hoping that there are no other calls that could have also
benefited from the handle.  So this brings you to 4, where all functions have a
handle.

Finally, if you agree that the conversation function would have to be registered
early, like early enough to always be considered the GSS entry point, going to
step 5 is the conclusion.

I recommend going all the way to step 5.  I know this is a lot of change, but
I've encountered many implementation problems that needed to be solved (or
worse, functionality that couldn't be added properly) because GSS has no
extensible handles, nor an entry point.

Applications must be modified to use the conversation function no matter what.
Old V1 apps simply won't be able to take advantage of this facility.  Following
John Linn's advice, these kinds of changes could be kept in a separate draft
version.  And if people end up finding merit in any or all of this, it could
roll into GSS V2+ or V3 down the road.  I'm trying to make a case here for
enhancing some aspects of GSS that will better serve us in future versions.
Since this particular enhancement for conversation functions brings up issues
that seem to suggest some of these additions, why not address them now?
Especially since the changes will go into a separate draft, and so not
jeopardize any of the vast amount of work that went into standardizing V2.

CyberSafe is willing to write up a modified version of the API that includes
these changes.

David

--
David S. Miller    [email-masked]
CyberSafe Corporation   425.391.6000

---

From: "Theodore Y. Ts'o" <[email-masked]>
Date: Fri, 15 Jan 1999 23:43:43 -0500 (EST)

David, 

	Thanks for your thoughtful note about some of the "bigger
picture" issues.

>    different means, to this new method without breaking existing apps.

Yes, this is absolutely true.  On the other hand, presumably the GSSAPI
library could take some appropriate fallback mechanisms if the
conversation function wasn't registered; for example, multi-mechanism
(and possibly negotiating) implementations could avoid the use of
mechanisms which require I/O with the user.  As another example, the
Kerberos mechanism could be enhanced to use the conversation function if
Kerberos credentials were not available; but if the conversation
function were not available (or if the user hit the cancel button), the
old behaviour of returning "credentials not available" could still be
used.

It's certainly the case, though, that any low-infrastructure
requiring mechanisms which require I/O with the user, won't be usable
with applications that don't know to register a conversation function.
There are two arguments that can be made here from this; and this
immediately leads to the question of whether we should make actual
*changes* to the GSSAPI interface, rather than just additions.  

On the one hand, it's true that GSSAPI hasn't exactly taken the world by
storm, so forcing applications to be modified might be acceptable.  On
the other hand, there are reasons why we might not want to make massive
changes to the API.  Programmers may be even less inclined to adopt
GSSAPI if we make significant changes now.  And for programmers who have
already adopted the use of GSSAPI, it's one thing to add support for a
new conversation function to an application; but it's quite another if
nearly every GSSPAI call in a program needs to be modified.  

Certainly if we were going to redesign the GSSAPI from scratch, there
are a large number of things which I would argue we should do
definitely; adding gss_start() and gss_end(), and adding a GSSAPI
library handle are certainly near the top of the list.  I'd also try to
simplify the interface some more, possibly using the concept of
parameter bundles, as proposed by the IDUP proposal.  On the hand, it's
not clear that the time is right to make such radical changes to the
GSSAPI.

>         threadsafe, nor does it need to be.

In general, the GSSAPI is silent on the issue of being thread-safe, and
a number of implementations of mechanisms simply aren't thread safe at
all.  A portable application would do well to put a subsystem lock
around any GSSAPI library calls in general.

Static storage in libraries is generally not a problem these days.  It's
true that depending on this is bad form, but as far as I know, the main
offender was Windows 3.1 DLL's.  Just about all modern shared library
implementations support static storage these days, so this may be an
acceptable requirement.

If we rally want to avoid this, we can also solve the problem --- by
tieing the conversation function to the credentials handle.  This would
be done by creating a version of gss_acquire_credentials which takes a
conversation function.  For example:

OM_uint32 gss_acquire_cred2(OM_uint32 * minor_status, 
		            gss_name_t desired_name,
		            OM_uint32 time_req,
		            gss_OID_set desired_mechs,
		            gss_cred_usage_t cred_usage,
			    gss_conv_t conv_struct,
		            gss_cred_id_t * output_cred_handle,
		            gss_OID_set * actual_mechs,
		            OM_uint32 *	time_rec);

This would allow gss_acquire_cred to use the conversation function, and
since a pointer to the conversation function could be saved in the
credentials handle, it could be used by gss_init_sec_context as well.
The advantage of such schemes is that we can keep the required
modifications to existing applications to a minimum.   

What do other folks think?  Is it worth it to do something like this to
avoid static storage in the library?  I confess I don't really care,
since our implementaiton is storing static information already.

						- Ted

---

From: Jacques Lebastard <[email-masked]>
Date: Mon, 18 Jan 1999 08:31:26 +0100

David S. Miller wrote:
> One thing we
> do have that's not present in this proposal is a parameter to identify
> which mechanism is doing the prompting.  This presumably would help in
> a multimechanism implementation.  I'd recommend adding this.

That'd be nice indeed. 

>           1.  Ted's proposal as-is could be implemented.  Advise the
>           caller to call the registration function nice and early in
>           their app.  Assume that a static storage location for the
>           registration pointer is unique per-process, so that just one
>           of these needs to be stored, not a list.

We could dynamically ask for the registration of the conversation
function
by adding a new major status (GSS_S_REGISTER_CONV) whenever the mech
implementation
is missing information from the user. That new major status might be
returned by eg. gss_acquire_cred, gss_add_cred,
gss_inquire_cred(_by_mech), 
gss_init_sec_context and gss_accept_sec_context ( ?). 

>           2.  Identify the functions that could ever be expected to
>           converse.  Make a new version of these functions that have
>           the conversation function parameter added to their calls.

The list may be very small. I don't think modifying the existing GSS
calls
would facilitate backward compatibility ! On the other hand, we could 
specify the registration function to avoid the additional parameters
to existing calls. I reckon adding a credential handle (and/or a
context handle) to the registration function could provide what we need.
Registered with GSS_C_NO_CREDENTIAL (and GSS_C_NO_CONTEXT), the
conversation 
function should be used whenever needed. Registered with a specific
credential handle, that conversation functions should be used to acquire
credentials identified by the given credential handle.

[This is something we added to our GSS-API implementations : we needed
to
allow administrators to put application secret keys in separate files 
(instead of DEFAULT_KEYTAB_NAME) and/or to use their secret key
acquisition
functions. We therefore added a specific GSS-like call :
OM_uint32 gss_set_key_fetch_function (
    OM_uint32		FAR *minor_status,
    gss_cred_id_t	cred_handle,
    void		FAR *params,
    gss_buffer_t	(* pwd_function) (void *) );
Invoked with GSS_C_NO_CREDENTIAL indicates that params and pwd_function
should be used as default (ie instead of DEFAULT_KEYTAB_NAME). Invoked
with a
given credential_handle, then pwd_function and/or params should be used
to
acquire cred for that cred handle only.
]

> Applications must be modified to use the conversation function no
> matter what.  Old V1 apps simply won't be able to take advantage of
> this facility.  Following John Linn's advice, these kinds of changes

Just adding one GSS call without modifying any existing call makes
backward compatibility. Applications that don't call the registration
function (just because they don't need to :) will continue to run with
an upgrated GSS-API V2 library, without requiring to be rebuilt.

-- 
Jacques LEBASTARD                 mailto:[email-masked]
BullSoft - AccessMaster             http://www.openmaster.com/
Rue Jean Jaures - F3-2G-03           Tel: +33 1 30 80 77 86
F-78340 Les Clayes sous Bois         Fax: +33 1 30 80 77 99
==========================================================================
This message was posted through the Stanford campus mailing list
server.  If you wish to unsubscribe from this mailing list, send the
message body of "unsubscribe ietf-cat-wg" to [email-masked]

---

From: Jacques Lebastard <[email-masked]>
Date: Mon, 18 Jan 1999 09:07:59 +0100

Theodore Y. Ts'o wrote:
> And for programmers who have

I am indeed reluctant to modified all the GSSAPI applications writen
so far just because a new function (that I don't need except for 0.01 %
of applications) has been added !

> not clear that the time is right to make such radical changes to the

I don't think the time is right to redesign GSS-API from scratch.
GSS-API V2 is
now too mature to do this.

> around any GSSAPI library calls in general.

That's definitely a mechanism implementation issue.

> conversation function.  For example:

I'd rather add a credential handle to the conversation function
registration call (as said in my response to Joris' message).

> modifications to existing applications to a minimum.

Using a credential handle to the registration call does not imply any
modification to existing gss_acquire_cred/gss_init_sec_context calls.

> since our implementaiton is storing static information already.

Same here.

---

From: Martin Rex <[email-masked]>
Date: Mon, 18 Jan 1999 15:47:25 +0100 (MET)

Theodore Y. Ts'o wrote:
> 
> If we really want to avoid this, we can also solve the problem --- by

I prefer the gss_acquire_cred2() approach among the proposals.
However am not really comfortable about the gss-api mechanism being able
memorize and use the callback during later calls into gssapi.
Everything that is necessary could be done during gss_acquire_cred2()
in my opinion.

The multimechanism designers might want to decide which credentials
to create until after negotiation with the peer, but most application
designers will want to insert user popups at very specific places
within the application<->user interaction.  I don't really like to
idle a network connection before or during authentication -- I really
prefer a very short delay between connection setup and termination
of the security context establishment for the general case.

-Martin

---

From: "Theodore Y. Ts'o" <[email-masked]>
Date: Thu, 21 Jan 1999 17:48:59 -0500 (EST)

From: Martin Rex <[email-masked]>
   Date: Mon, 18 Jan 1999 15:47:25 +0100 (MET)

I think we need to allow the gss-api mechanism to use the callback
during init_sec_context, since some of the low-infrastructure schemes
may require user-input that is only available after the acceptor issues
the challenge.  For example, consider a challenge/response
authentication scheme where the DES key is stored in a hardware token,
and the user needs to type the challenge into the DES calculator in
order to get the response code, which then needs to be provided to
init_sec_context. 

It's true that in general the desire to want to minimize the cases where
user-input might be delaying the completion of an authentication
exchange, but don't think it can always be avoided in all cases.

						- Ted