One document matched: draft-kaduk-kitten-gss-loop-01.xml


<?xml version="1.0"?>
<!DOCTYPE rfc SYSTEM "rfc2629.dtd" [
<!ENTITY RFC2743 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2743.xml">
<!ENTITY RFC2744 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2744.xml">
<!ENTITY RFC5801 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.5801.xml">
<!ENTITY RFC3645 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.3645.xml">
<!ENTITY RFC2203 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.2203.xml">
<!ENTITY RFC4462 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4462.xml">
<!ENTITY RFC4752 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4752.xml">
<!ENTITY RFC4401 SYSTEM "http://xml.resource.org/public/rfc/bibxml/reference.RFC.4401.xml">
]>
<rfc category="info" ipr="trust200902" docName="draft-kaduk-kitten-gss-loop-01" submissionType="IETF">
  <front>
    <title>Structure of the GSS Negotiation Loop</title>
    <author initials="B." surname="Kaduk" fullname="Benjamin Kaduk">
      <organization abbrev="MIT">MIT Kerberos Consortium</organization>
      <address>
        <email>kaduk@mit.edu</email>
      </address>
    </author>
    <date month="November" year="2013"/>
    <abstract>
      <t>This document specifies the generic structure of the negotiation
	loop to establish a GSS security context between initiator and
	acceptor.  The control flow of the loop is indicated for both
	parties, including error conditions, and indications are given for
	where application-specific behavior must be specified.
      </t>
    </abstract>
  </front>
  <middle>
    <section anchor="intro" title="Introduction">
      <t>The Generic Security Service Application Program Intervace
	version 2 <xref target="RFC2743" /> provides a generic interface
	for security services, in the form of an abstraction layer over
	the underlying security mechanisms that an application may use.
	A GSS initiator and acceptor exchange messages, called tokens,
	until a security context is established.  Such a security context
	allows for mutual authentication of the two parties, the passing of
	confidential or integrity-protected messages
	between the initiator and acceptor, the generation of identical
	pseudo-random bit strings by both
	participants <xref target="RFC4401" />, and more.  The number of
	tokens which must be exchanged between initiator and acceptor in
	order to establish the security context is dependent on the
	underlying mechanism as well as the desired properties of the
	security context, and is in general not known to the application.
	Accordingly, the application's control flow must include a loop
	within which GSS security context tokens are exchanged, which
	terminates upon successful establishment of a security context
	(or an error condition).
      </t>
      <t>The GSS-API C bindings <xref target="RFC2744" /> provide some
	example code for such a negotiation loop, but this code does not
	specify the application's behavior on unexpected or error conditions.
	As such, individual application protocol specifications have
	had to specify the structure of their GSS negotiation loops,
	including error handling, on a per-protocol basis.
	<xref target="RFC4462" />, <xref target="RFC3645" />,
	<xref target="RFC5801" />, <xref target="RFC4752" />,
	<xref target="RFC2203" />
	This represents a substantial duplication of effort, and the
	various specifications go into different levels of detail and
	describe different possible error conditions.  It is therefore
	preferable to have the structure of the GSS negotiation loop,
	including error conditions and token passing, described in a single
	specification, which can then be referred to from other documents
	in lieu of repeating the structure of the loop each time.
	This document will perform that role.
      </t>
      <t>The necessary requirements for correctly performing a GSS
	negotiation loop are essentially all included in
	<xref target="RFC2743" />, but they are scattered in many different
	places.  This document brings all the requirements together into
	one place for the convenience of implementors, even though the
	normative requirements remain in <xref target="RFC2743" />.
	In a few places, this document notes additional behavior which is
	useful for applications but is not mandated by <xref target="RFC2743" />.
      </t>
    </section>
    <section anchor="loop" title="Loop Structure">
      <t>The loop is begun by the appropriately named initiator,
	which calls GSS_Init_sec_context() with an empty (zero-length)
	input_token and a fixed set of input flags containing the
	desired attributes for the security context.
	The initiator should not change any of the
	input parameters to GSS_Init_sec_context() between calls
	to it during the loop, with the exception of the input_token
	parameter, which will contain a message from the acceptor
	after the initial call, and the input_context_handle, which
	must be the result returned in the output_context_handle of
	the previous call to GSS_Init_sec_context() (or GSS_C_NO_CONTEXT
	for the first call).  (In the C bindings, there is only a single
	read/modify context_handle argument.)  RFC 2743 only requires that
	the claimant_cred_handle argument remain constant over all calls in
	the loop, but the other non-excepted arguments should also remain
	fixed for reliable operation.
      </t>
      <t>The following subsections will describe the various steps of
	the loop, without special consideration to whether a call
	to GSS_Init_sec_context() or GSS_Accept_sec_context() is the
	first such call in the loop.  For the first call to each
	routine in the loop, the major status code from the previous
	call to GSS_Init_sec_context() or GSS_Accept_sec_context() should
	be taken as GSS_S_CONTINUE_NEEDED.
      </t>
      <section anchor="anon" title="Anonymous Initiators">
	<t>If the initiator is requesting anonymity by setting the
	  anon_req_flag input to GSS_Init_sec_context(), then on
	  non-error returns from GSS_Init_sec_context() (that is,
	  the major status is GSS_S_COMPLETE or GSS_S_CONTINUE_NEEDED),
	  the initiator must verify that the output value of anon_state
	  from GSS_Init_sec_context() is true before sending the
	  security context token to the acceptor.  Failing to perform
	  this check could cause the initiator to lose anonymity.
	</t>
      </section>
      <section anchor="initiator" title="GSS_Init_sec_context">
	<t>The initiator calls GSS_Init_sec_context(), using the
	  input_context_handle for the current proto-security-context
	  and its fixed set of input parameters, and the input_token
	  received from the acceptor (if not the first iteration of
	  the loop).  The presence of a nonempty output_token and the
	  value of the major status code are the indicators for how to
	  proceed:
	  <list style="hanging">
	    <t>If the major status code is GSS_S_COMPLETE and the
	      output_token is empty, then the context negotiation is
	      fully complete and ready for use by the initiator with no
	      further actions.</t>
	    <t>If the major status code is GSS_S_COMPLETE and the
	      output_token is nonempty, then the initiator's portion
	      of the security context negotiation is complete but the
	      acceptor's is not.  The initiator must send the output_token
	      to the acceptor so that the acceptor can establish its half
	      of the security context.</t>
	    <t>If the major status code is GSS_S_CONTINUE_NEEDED and the
	      output_token is nonempty, the context negotiation is
	      incomplete.  The initiator must send the output_token
	      to the acceptor and await another input_token from the
	      acceptor.</t>
	    <t>If the major status code is GSS_S_CONTINUE_NEEDED and the
	      output_token is empty, the mechanism has produced an
	      output which is not compliant with <xref target="RFC2743" />.
	      However, there are some known implementations of certain
	      mechanisms which do produce empty context negotiation
	      tokens.  For maximum interoperability, applications should
	      be prepared to accept such tokens, and should transmit them
	      to the acceptor if they are generated.</t>
	    <t>If the major status code is any other value, the context
	      negotiation has failed.  If the output_token is nonempty,
	      it is an error token, and the initiator should send it to
	      the acceptor.  If the output_token is empty, then the
	      initiator should indicate the failure to the acceptor if an
	      appropriate channel to do so is available.
	    </t>
	  </list>
	</t>
      </section>
      <section anchor="itoa" title="Sending from Initiator to Acceptor">
	<t>The establishment of a GSS security context between initiator
	  and acceptor requires some communication channel by which to
	  exchange the context negotiation tokens.  The nature of this
	  channel is not specified by the GSS specification -- it could
	  be a synchronous TCP channel, a UDP-based RPC protocol, or
	  any other sort of channel.  In many cases, the channel will
	  be multiplexed with non-GSS application data; the application
	  protocol must provide some means by which the GSS context tokens
	  can be identified and passed through to the mechanism accordingly.
	  It is in such cases where the application protocol has a means
	  to indicate error conditions that the initiator could indicate
	  a failure to the acceptor, as mentioned in some of the above
	  cases conditional on "an appropriate channel to do so".</t>
	<t>However, even the presence of a communication channel does not
	  necessarily indicate that it is appropriate for the initiator
	  to indicate such errors.  For example, if the acceptor is
	  a stateless or near-stateless UDP server, there is probably
	  no need for the initiator to explicitly indicate its failure
	  to the acceptor.  Conditions such as this can be treated in
	  individual application protocol specifications.</t>
	<t>If a regular security context output_token is produced by the
	  call to GSS_Init_sec_context(), the initiator must transmit
	  this token to the acceptor over the application's communication
	  channel.  If the call to GSS_Init_sec_context() returns an
	  error token as output_token, it is recommended that the intiator
	  transmit this token to the acceptor over the application's
	  communication channel.
	</t>
      </section>
      <section anchor="asan" title="Acceptor Sanity Checking">
	<t>The acceptor's half of the negotiation loop is triggered
	  by the receipt of a context token from the initiator.
	  Before calling GSS_Accept_sec_context(), the acceptor
	  may find it useful to perform some sanity checks on the
	  state of the negotiation loop.</t>
	<t>If the acceptor receives a context token but was not expecting
	  such a token (for example, if the acceptor's previous call to
	  GSS_Accept_sec_context() returned GSS_S_COMPLETE), this is probably
	  an error condition indicating that the initiator's state is invalid.
	  See <xref target="extra-token" /> for some exceptional cases.
	  It is likely appropriate for the acceptor to report this error
	  condition to the acceptor via the application's communication
	  channel.</t>
	<t>If the acceptor is expecting a context token (e.g., if the
	  previous call to GSS_Accept_sec_context() returned
	  GSS_S_CONTINUE_NEEDED), but does not receive such a token within
	  a reasonable amount of time after transmitting the previous
	  output_token to the initiator, the acceptor should assume that
	  the initiator's state is invalid and fail the GSS negotiation.
	  Again, it is likely appropriate for the acceptor to report
	  this error condition to the initiator via the application's
	  communication channel.</t>
	<t>[Are there other checks to perform here?]
	</t>
      </section>
      <section anchor="accept" title="GSS_Accept_sec_context">
	<t>The GSS acceptor responds to the actions of an initiator;
	  as such, there should always be a nonempty input_token
	  to calls to GSS_Accept_sec_context().  The input_context_handle
	  parameter will always be given as the output_context_handle
	  from the previous call to GSS_Accept_sec_context() in a
	  given negotiation loop (or GSS_C_NO_CONTEXT on the first
	  call), but the acceptor_cred_handle and chan_bindings arguments
	  should remain fixed over the course of a given GSS negotiation
	  loop.  <xref target="RFC2743" /> only requires that the
	  acceptor_cred_handle remain fixed throughout the loop, but
	  the chan_bindings argument should also remain fixed for
	  reliable operation.</t>
	<t>The GSS acceptor calls GSS_Accept_sec_context(), using the
	  input_context_handle for the current proto-security-context
	  and the input_token received from the initiator.
	  The presence of a nonempty output_token and the value of the
	  major status code are the indicators for how to proceed:
	  <list style="hanging">
	    <t>If the major status code is GSS_S_COMPLETE and the
	      output_token is empty, then the context negotiation is
	      fully complete and ready for use by the acceptor with no
	      further actions.</t>
	    <t>If the major status code is GSS_S_COMPLETE and the
	      output_token is nonempty, then the acceptor's portion
	      of the security context negotiation is complete but the
	      initiator's is not.  The acceptor must send the output_token
	      to the initiator so that the initiator can establish its half
	      of the security context.</t>
	    <t>If the major status code is GSS_S_CONTINUE_NEEDED and the
	      output_token is nonempty, the context negotiation is
	      incomplete.  The acceptor must send the output_token
	      to the initiator and await another input_token from the
	      initiator.</t>
	    <t>If the major status code is GSS_S_CONTINUE_NEEDED and the
	      output_token is empty, the mechanism has produced an
	      output which is not compliant with <xref target="RFC2743" />.
	      output which is not compliant with <xref target="RFC2743" />.
	      However, there are some known implementations of certain
	      mechanisms which do produce empty context negotiation
	      tokens.  For maximum interoperability, applications should
	      be prepared to accept such tokens, and should transmit them
	      to the initiator if they are generated.</t>
	    <t>If the major status code is any other value, the context
	      negotiation has failed.  If the output_token is nonempty,
	      it is an error token, and the acceptor should send it to
	      the initiator.  If the output_token is empty, then the
	      acceptor should indicate the failure to the initiator if an
	      appropriate channel to do so is available.
	    </t>
	  </list>
	</t>
      </section>
      <section anchor="atoi" title="Sending from Acceptor to Initiator">
	<t>The mechanism for sending the context token from acceptor to
	  initiator will depend on the nature of the communication channel
	  between the two parties.  For a synchronous bidirectional channel,
	  it can be just another piece of data sent over the link, but for
	  a stateless UDP RPC acceptor, the token will probably end up
	  being sent as an RPC output parameter.  Application protocol
	  specifications will need to specify the nature of this behavior.</t>
        <t>If the application protocol has the initiator driving the
	  application's control flow (with the acceptor just responding to
	  actions from the initiator), it is particularly helpful for
	  the acceptor to indicate a failure to the initiator, as mentioned
	  in some of the above cases conditional on "an appropriate channel
	  to do so".</t>
	<t>If a regular security context output_token is produced by the
	  call to GSS_Accept_sec_context(), the acceptor must transmit
	  this token to the initiator over the application's communication
	  channel.  If the call to GSS_Accept_sec_context() returns an
	  error token as output_token, it is recommended that the acceptor
	  transmit this token to the initiator over the application's
	  communication channel.
	</t>
      </section>
      <section anchor="isan" title="Initiator input validation">
	<t>The initiator's half of the negotiation loop is triggered
	  (after the first call) by receipt of a context token from the
	  acceptor.  Before calling GSS_Init_sec_context(), the initiator
	  may find it useful to perform some sanity checks on the state
	  of the negotiation loop.</t>
	<t>If the initiator receives a context token but was not expecting
	  such a token (for example, if the initiator's previous call to
	  GSS_Init_sec_context() returned GSS_S_COMPLETE), this is probably an
	  error condition indicating that the acceptor's state is invalid.
	  See <xref target="extra-token" /> for some exceptional cases.
	  It may be appropriate for the initiator to report this error
	  condition to the acceptor via the application's communication
	  channel.</t>
	<t>If the initiator is expecting a context token (that is, the
	  previous call to GSS_Init_sec_context() returned
	  GSS_S_CONTINUE_NEEDED), but does not receive such a token within
	  a reasonable amount of time after transmitting the previous
	  output_token to the acceptor, the initiator should assume that
	  the acceptor's state is invalid and fail the GSS negotiation.
	  Again, it may be appropriate for the initiator to report this
	  error condition to the acceptor via the application's communication
	  channel.</t>
	<t>[Are there other checks to perform here?]
	</t>
      </section>
      <section title="Continue the Loop">
	<t>If the loop is in neither a success or failure condition, then
	  the loop must continue.  Control flow returns to
	  <xref target="initiator" />.
	</t>
      </section>
    </section>
    <section anchor="post" title="After Security Context Negotiation">
      <t>Once a party has completed its half of the security context
	and fulfilled its obligations to the other party, the context
	is complete, but it is not necessarily ready and appropriate
	for use.  (In some cases the context may be ready for use
	earlier than this, see <xref target="partial" />.)
        In particular, the security context
	flags may not be appropriate for the given application's use.</t>
      <t>The initiator specifies as part of its fixed set of inputs
	to GSS_Init_sec_context() values for the following booleans:
	deleg_req_flag, mutual_req_flag, replay_det_req_flag,
	sequence_req_flag, conf_req_flag, and integ_req_flag.  Upon
	completion of security context negotiation, the initiator must
	verify the values of the deleg_state, mutual_state, replay_det_state,
	sequence_state, conf_avail, and integ_avail flags
	from the last call to GSS_Init_sec_context()
	corresponding to the requested flags.  If a flag was requested
	but is not available, and that feature is necessary for the
	appplication protocol, the initiator must destroy the security
	context and not use the security context for application traffic.</t>
      <t>Application protocol specifications citing this document
	should indicate which context flags are required for the application
	protocol.</t>
      <t>The acceptor receives as output the following booleans:
	deleg_state, mutual_state, replay_det_state, sequence_state,
	anon_state, trans_state, conf_avail, and integ_avail.
	The acceptor must verify that any flags necessary for the application
	protocol are set.  If a necessary flag is not set, the acceptor
	must destroy the security context and not use the security context
	for application traffic.
      </t>
      <section anchor="partial"
	       title="Using Partially Complete Security Contexts">
	<t>For mechanism/flag combinations that require multiple token
	  exchanges, an application protocol may find it desirable to
	  begin sending application data protected with GSS per-message
	  operations while continuing to exchange security context tokens
	  to complete the security context negotiation.  The
	  prot_ready_state output value from GSS_Init_sec_context() and
	  GSS_Accept_sec_context() indicates when per-message operations
	  are avaialble.</t>
	<t>Applications requiring confidentiality and/or integrity
	  protection from such messages must check the value of the
	  conf_avail and/or integ_avail output flags from
	  GSS_Init_sec_context()/GSS_Accept_sec_context() as well as the
	  conf_state output of GSS_Wrap() (if GSS_Wrap() is used).
	</t>
      </section>
      <section anchor="extra-token" title="Additional Context Tokens">
	<t>Under some (rare) conditions, a context token will be received
	  by a party to a security context negotiation after that party has 
	  completed the negotiation (i.e., after GSS_Init_sec_context()
	  or GSS_Accept_sec_context() has returned GSS_S_COMPLETE).
	  Such tokens must be passed to GSS_Process_context_token() for
	  processing.
	</t>
	<t>The most common cause of such tokens is security context deletion
	  tokens, emitted when the remote party called GSS_Delete_sec_context()
	  with a non-null output_context_token parameter.  With the GSS-API
	  version 2, it is not recommended to use security context deletion
	  tokens.
	</t>
	<t>Extra security context tokens can also be emitted if the selected
	  mechanism specifies some functionality (such as per-message
          confidentiality protection) as optional-to-implement, and the
	  acceptor's implementation does not implement the optional functionality,
	  but the functionality was requested by the initiator.  In this case,
	  the acceptor's GSS implementation is required to emit at least one
	  context token (even when one would not otherwise be needed to complete
	  the context negotiation), and this can result in an "extra" token.
	</t>
	<t>In the rare case when an application receives an extra context token,
	  GSS_Inquire_context() should be used after processing the extra token
	  to re-verify that the context does support the features necessary
	  for the application protocol.  This will also indicate whether the
	  token was a deletion token, in which case the major status will be
	  GSS_S_NO_CONTEXT.
	</t>
      </section>
    </section>
    <section anchor="samples" title="Sample Code">
      <t>This section gives sample code for the GSS negotiation loop, both for
	a regular application and for an application where the initiator
	wishes to remain anonymous.  Since the code for the two cases is
	very similar, the anonymous-specific additions are wrapped in
	preprocessor conditionals which may be ignored if anonymous processing
	is not needed.</t>
      <t> Since the communication channel
	between the initiator and acceptor is a matter for individual
	application protocols, it is inherently unspecified at the GSS-API
	level, which can lead to examples that are less satisfying than
	may be desired.  For example, the sample code in
	<xref target="RFC2744" /> uses an unspecified send_token_to_peer()
	routine.  In the interest of concreteness, this sample code
	uses pipes for communication between initiator and acceptor, so that
	explicit read() and write() may be used.</t>
      <t>This sample code is written in C, using the GSS-API C bindings
	<xref target="RFC2744" />.  It uses the macro GSS_ERROR() to
	help unpack the various sorts of information which can be stored in the
	major status field; supplementary information does not necessarily
	indicate an error.  Applications written in other languages will
	need to exercise care that checks against the major status value
	are written correctly.</t>
      <t>This sample code should be compilable as a standalone program,
	linked against a GSS-API library.  With most implementations, in
	order for it to successfully run, the initiator will need to
	specify an explicit target name for the acceptor (which must match
	the credentials available to the acceptor).  A skeleton for how
	this may be done is provided, in a disabled block of code.</t>
      <t>This sample code assumes v2 of the GSS-API.  Applications wishing
	to remain compatible with v1 of the GSS-API may need to perform
	additional checks in some locations.
      </t>
      <section title="GSS Application Sample Code">
	<t>
	  <figure>
<artwork><![CDATA[
#include <unistd.h>
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gssapi/gssapi.h>

/*
 * Pipes for communication between initiator and acceptor.
 * We use a very simple communication protocol, that can only ever
 * send context negotiation tokens and no other application data.
 * The framing is that we write a 32-bit unsigned integer which is
 * the byte count of the following token, followed by the token.
 */
int pipefds_itoa[2];
int pipefds_atoi[2];

/*
 * This helper is used only on buffers that we allocate ourselves (e.g.,
 * from receive_buffer()).  Buffers allocated by GSS routines must use
 * gss_release_buffer().
 */
static void
release_buffer(gss_buffer_t buf)
{
    free(buf->value);
    buf->value = NULL;
    buf->length = 0;
}

/*
 * Helper to send a token on the specified fd, using our simple protocol.
 * We must warnx() instead of errx() because compliant GSS applications must
 * release resources allocated by the GSS library before exiting.  (These
 * resources may be non-local to the current process.)
 */
static int
send_token(int fd, gss_buffer_t token)
{
    int ret;
    OM_uint32 length;

    assert(sizeof(length) == 4);
    length = token->length;
    ret = write(fd, &length, 4);
    if (ret != 4) {
        warnx("send_token could not write length\n");
        return 1;
    }
    ret = write(fd, token->value, length);
    if (ret != length) {
        warnx("send_token could not write token\n");
        return 1;
    }
    return 0;
}

/*
 * Helper to receive a token on the specified fd, using our simple protocol.
 * We must warnx() instead of errx() because compliant GSS applications must
 * release resources allocated by the GSS library before exiting.  (These
 * resources may be non-local to the current process.)
 */
static int
receive_token(int fd, gss_buffer_t token)
{
    int ret;
    OM_uint32 length;

    assert(sizeof(length) == 4);
    ret = read(fd, &length, 4);
    if (ret != 4) {
        warnx("receive_token could not read length, ret %u\n", length);
        return 1;
    }
    /* Do a little sanity checking. */
    if (length > 64 * 1024*1024) {
        warnx("Attempting to receive token larger than 64M\n");
        return 1;
    }
    token->value = malloc(length);
    if (token->value == NULL) {
        warnx("Could not allocate memory to receive token\n");
        return 1;
    }
    ret = read(fd, token->value, length);
    if (ret != length) {
        warnx("Could not receive token\n");
        return 1;
    }
    token->length = length;
    return 0;
}

static void
do_initiator(int readfd, int writefd)
{
    int context_established = 0;
    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
    OM_uint32 major, minor, req_flags, ret_flags;
    gss_buffer_desc input_token, output_token;
    gss_name_t target_name = GSS_C_NO_NAME;
    OM_uint32 ret;

    memset(&input_token, 0, sizeof(input_token));
    memset(&output_token, 0, sizeof(output_token));

    /* Applications should set target_name to a real value. */
#if 0
    gss_buffer_desc name_buf;
    name_buf.value = "<service>@<hostname.domain>";
    name_buf.length = strlen(name_buf.value);
    major = gss_import_name(&minor, &name_buf, GSS_C_NT_HOSTBASED_SERVICE,
                            &target_name);
    /* target_name must be released with gss_release_name() at cleanup. */
#endif

    /* Mutual authentication will require a token from acceptor to initiator,
     * and thus a second call to gss_init_sec_context(). */
    req_flags = GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
#ifdef ANONYMOUS
    req_flags |= GSS_C_ANON_FLAG;
#endif

    while (!context_established) {
        /* The initiator_cred_handle, mech_type, time_req, input_chan_bindings,
         * actual_mech_type, and time_rec parameters are not needed in many
         * cases.  We pass GSS_C_NO_CREDENTIAL, GSS_C_NO_OID, 0, NULL, NULL,
         * and NULL for them, respectively. */
        major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ctx,
                                     target_name, GSS_C_NO_OID, req_flags, 0,
                                     NULL, &input_token, NULL, &output_token,
                                     &ret_flags, NULL);
        /* This memory is no longer needed. */
        release_buffer(&input_token);
#ifdef ANONYMOUS
        /* Initiators which wish to remain anonymous must check whether
         * their request has been honored before sending each context token. */
        if ((ret_flags & GSS_C_ANON_FLAG) != GSS_C_ANON_FLAG) {
            warnx("Anonymous processing not available\n");
            goto cleanup;
        }
#endif
        /* Always send a token if we are expecting another input token
         * (GSS_S_CONTINUE_NEEDED) or if it is nonempty. */
        if ((major & GSS_S_CONTINUE_NEEDED) != 0 ||
            output_token.length > 0) {
            ret = send_token(writefd, &output_token);
            if (ret != 0)
                goto cleanup;
        }
        /* Check for errors after sending the token so that we will send
         * error tokens. */
        if (GSS_ERROR(major)) {
            warnx("gss_init_sec_context() error major 0x%x\n", major);
            goto cleanup;
        }
        /* Having sent any output_token, release the storage for it. */
        (void)gss_release_buffer(&minor, &output_token);

        if ((major & GSS_S_CONTINUE_NEEDED) != 0) {
            ret = receive_token(readfd, &input_token);
            if (ret != 0)
                goto cleanup;
        } else if (major == GSS_S_COMPLETE) {
            context_established = 1;
        } else {
            /* This situation is forbidden by RFC 2743.  Bail out. */
            warnx("major not complete or continue-needed but not error\n");
            goto cleanup;
        }
    }	/* while(!context_established) */
    if ((ret_flags & req_flags) != req_flags) {
        warnx("Negotiated context does not support requested flags\n");
        goto cleanup;
    }
    printf("Initiator's context negotiation successful\n");
cleanup:
    /* It is safe to call gss_release_buffer twice on the same buffer. */
    (void)gss_release_buffer(&minor, &output_token);
    /* Do not request a context deletion token; pass NULL. */
    (void)gss_delete_sec_context(&minor, &ctx, NULL);
}

static void
do_acceptor(int readfd, int writefd)
{
    int context_established = 0, ret;
    gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
    OM_uint32 major, minor, ret_flags;
    gss_buffer_desc input_token, output_token;
    gss_name_t client_name;

    memset(&input_token, 0, sizeof(input_token));
    memset(&output_token, 0, sizeof(output_token));

    context_established = 0;
    major = GSS_S_CONTINUE_NEEDED;

    while(!context_established) {
        if ((major & GSS_S_CONTINUE_NEEDED) != 0) {
            ret = receive_token(readfd, &input_token);
            if (ret != 0)
                goto cleanup;
        } else if (major == GSS_S_COMPLETE) {
            context_established = 1;
            break;
        } else {
            /* This situation is forbidden by RFC 2743.  Bail out. */
            warnx("major not complete or continue-needed but not error\n");
            goto cleanup;
        }
        /* We can use the default behavior or do not need the returned
         * information for the parameters acceptor_cred_handle,
         * input_chan_bindings, mech_type, time_rec, and delegated_cred_handle
         * and pass the values GSS_C_NO_CREDENTIAL, NULL, NULL, NULL, and NULL,
         * respectively.  In some cases the src_name will not be needed, but
         * most likely it will be needed for some authorization or logging
         * functionality. */
        major = gss_accept_sec_context(&minor, &ctx, GSS_C_NO_CREDENTIAL,
                                       &input_token, NULL, &client_name, NULL,
                                       &output_token, &ret_flags, NULL, NULL);
        /* Release memory no longer needed. */
        release_buffer(&input_token);
        /* Always send a token if we are expecting another input token
         * (GSS_S_CONTINUE_NEEDED) or if it is nonempty. */
        if ((major & GSS_S_CONTINUE_NEEDED) != 0 ||
            output_token.length > 0) {
            ret = send_token(writefd, &output_token);
            if (ret != 0)
                goto cleanup;
        }
        /* Check for errors after sending the token so that we will send
         * error tokens. */
        if (GSS_ERROR(major)) {
            warnx("gss_accept_sec_context() error major 0x%x\n", major);
            goto cleanup;
        }
        /* Release the output token's storage; we don't need it anymore. */
        (void)gss_release_buffer(&minor, &output_token);
    }	/* while(!context_established) */
    if ((ret_flags & GSS_C_INTEG_FLAG) != GSS_C_INTEG_FLAG) {
        warnx("Negotiated context does not support integrity\n");
        goto cleanup;
    }
    printf("Acceptor's context negotiation successful\n");
cleanup:
    /* It is safe to call gss_release_buffer twice on the same buffer. */
    release_buffer(&input_token);
    /* Do not request a context deletion token, pass NULL. */
    (void)gss_delete_sec_context(&minor, &ctx, NULL);
    (void)gss_release_name(&minor, &client_name);
}

int main(int argc, char **argv)
{
    pid_t pid;

    if (pipe(pipefds_itoa) != 0)
        err(1, "pipe failed for itoa\n");
    if (pipe(pipefds_atoi) != 0)
        err(1, "pipe failed for atoi\n");
    pid = fork();
    if (pid == 0)
        do_initiator(pipefds_atoi[0], pipefds_itoa[1]);
    else if (pid > 0)
        do_acceptor(pipefds_itoa[0], pipefds_atoi[1]);
    else
        err(1, "fork() failed\n");
    exit(0);
}
]]></artwork>
	  </figure>
	</t>
      </section>
    </section>
    <section title="Security Considerations">
      <t>This document provides a (reasonably) concise description and
	example for correct construction of the GSS-API security context
	negotiation loop.  Since everything relating to the construction
	and use of a GSS security context is security-related, there
	are security-relevant considerations throughout the document.
	It is useul to call out a few things in this section, though.</t>
      <t>The GSS-API uses a request-and-check model for features.  An
	application using the GSS-API requests that certain features
	(confidentiality protection for messages, or anonynimity), but
	such a request does not require the GSS implementation to provide
	the feature.  The application must check the returned flags to
	verify whether a requested feature is present; if the feature
	was non-optional for the application, the application must generate
	an error.  Phrased differently, the GSS-API will not generate an
	error if it is unable to satisfy the features requested by the
	application.</t>
    </section>
  </middle>
  <back>
    <references title="Normative References">
      &RFC2743;
      &RFC2744;
    </references>
    <references title="Informational References">
      &RFC4462;
      &RFC4401;
      &RFC3645;
      &RFC5801;
      &RFC4752;
      &RFC2203;
    </references>
    <section title="Acknowledgements">
      <t>Thanks to Nico Williams and Jeff Hutzleman for prompting me to
	write this document.</t>
    </section>
  </back>
</rfc>

PAFTECH AB 2003-20262026-04-23 08:30:17