Exemple #1
0
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_display_status(OM_uint32 *minor_status,
    OM_uint32 status_value,
    int status_type,
    const gss_OID mech_type,
    OM_uint32 *message_content,
    gss_buffer_t status_string)
{
	OM_uint32 major_status;

	_mg_buffer_zero(status_string);
	*message_content = 0;

	major_status = _gss_mg_get_error(mech_type, status_type,
					 status_value, status_string);
	if (major_status == GSS_S_COMPLETE) {

	    *message_content = 0;
	    *minor_status = 0;
	    return GSS_S_COMPLETE;
	}

	*minor_status = 0;
	switch (status_type) {
	case GSS_C_GSS_CODE: {
	    	char *buf = NULL;
		int e;

		if (GSS_SUPPLEMENTARY_INFO(status_value))
		    e = asprintf(&buf, "%s", supplementary_error(
		        GSS_SUPPLEMENTARY_INFO(status_value)));
		else
		    e = asprintf (&buf, "%s %s",
		        calling_error(GSS_CALLING_ERROR(status_value)),
			routine_error(GSS_ROUTINE_ERROR(status_value)));

		if (e < 0 || buf == NULL)
		    break;

		status_string->length = strlen(buf);
		status_string->value  = buf;

		return GSS_S_COMPLETE;
	}
	case GSS_C_MECH_CODE: {
		OM_uint32 maj_junk, min_junk;
		gss_buffer_desc oid;
		char *buf = NULL;
		int e;

		maj_junk = gss_oid_to_str(&min_junk, mech_type, &oid);
		if (maj_junk != GSS_S_COMPLETE) {
		    oid.value = rk_UNCONST("unknown");
		    oid.length = 7;
		}
		
		e = asprintf (&buf, "unknown mech-code %lu for mech %.*s",
			  (unsigned long)status_value,
			  (int)oid.length, (char *)oid.value);
		if (maj_junk == GSS_S_COMPLETE)
		    gss_release_buffer(&min_junk, &oid);

		if (e < 0 || buf == NULL)
		    break;

		status_string->length = strlen(buf);
		status_string->value  = buf;

		return GSS_S_COMPLETE;
	}
	}
	_mg_buffer_zero(status_string);
	return (GSS_S_BAD_STATUS);
}
Exemple #2
0
/**
 * @ingroup globus_gsi_gss_assist
 * Gets a token using the specific tokenizing functions,
 * and performs the GSS unwrap of that token
 *
 * @see gss_unwrap
 *
 * @param minor_status
 *        GSSAPI return code, @see gss_unwrap
 * @param context_handle
 *        the context 
 * @param data
 *        pointer to be set to the unwrapped application data. This must be
 *        freed by the caller.
 * @param length
 *        pointer to be set to the length of the @a data byte array.
 * @param token_status
 *        assist routine get/send token status 
 * @param gss_assist_get_token
 *        a detokenizing routine 
 * @param gss_assist_get_context
 *        first arg for above routine
 * @param fperr
 *        error stream to print to
 * 
 * @return
 *        GSS_S_COMPLETE on sucess
 *        Other gss errors on failure.  
 */
OM_uint32
globus_gss_assist_get_unwrap(
    OM_uint32 *                         minor_status,
    const gss_ctx_id_t                  context_handle,
    char **			        data,
    size_t *			        length,
    int *			        token_status,
    int (*gss_assist_get_token)(void *, void **, size_t *),
    void *                              gss_assist_get_context,
    FILE *                              fperr)
{

  OM_uint32                             major_status = GSS_S_COMPLETE;
  OM_uint32                             minor_status1 = 0;
  gss_buffer_desc                       input_token_desc  = GSS_C_EMPTY_BUFFER;
  gss_buffer_t                          input_token       = &input_token_desc;
  gss_buffer_desc                       output_token_desc = GSS_C_EMPTY_BUFFER;
  gss_buffer_t                          output_token      = &output_token_desc;

  static char *                         _function_name_ =
      "globus_gss_assist_get_unwrap";
  GLOBUS_I_GSI_GSS_ASSIST_DEBUG_ENTER;

  *token_status = (*gss_assist_get_token)(gss_assist_get_context,
                                          &input_token->value,
                                          &input_token->length);

  if (*token_status == 0) {
      
      major_status = gss_unwrap(minor_status,
                                context_handle,
                                input_token,
                                output_token,
                                NULL,
                                NULL);

      GLOBUS_I_GSI_GSS_ASSIST_DEBUG_FPRINTF(
          3, (globus_i_gsi_gss_assist_debug_fstream,
              _GASL("unwrap: maj: %8.8x min: %8.8x inlen: %u outlen: %u\n"),
              (unsigned int) major_status, 
              (unsigned int) *minor_status, 
              input_token->length,
              output_token->length));
      
      gss_release_buffer(&minor_status1,
                         input_token);
      
      *data = output_token->value;
      *length = output_token->length;
  }
  
  if (fperr && (major_status != GSS_S_COMPLETE || *token_status != 0)) {
      globus_gss_assist_display_status(stderr,
                                       _GASL("gss_assist_get_unwrap failure:"),
                                       major_status,
                                       *minor_status,
                                       *token_status);
  }

  *data = output_token->value;
  *length = output_token->length;
  
  if (*token_status) {
      major_status = GSS_S_FAILURE;
  }

  GLOBUS_I_GSI_GSS_ASSIST_DEBUG_EXIT;
  return major_status;
}
Exemple #3
0
CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
                                      struct connectdata *conn)
{
  struct SessionHandle *data = conn->data;
  curl_socket_t sock = conn->sock[sockindex];
  CURLcode code;
  ssize_t actualread;
  ssize_t written;
  int result;
  long timeout;
  OM_uint32 gss_major_status, gss_minor_status, gss_status;
  OM_uint32 gss_ret_flags;
  int gss_conf_state, gss_enc;
  gss_buffer_desc  service = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_send_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_recv_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc  gss_w_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc* gss_token = GSS_C_NO_BUFFER;
  gss_name_t       server = GSS_C_NO_NAME;
  gss_name_t       gss_client_name = GSS_C_NO_NAME;
  unsigned short   us_length;
  char             *user=NULL;
  unsigned char socksreq[4]; /* room for gssapi exchange header only */
  char *serviceptr = data->set.str[STRING_SOCKS5_GSSAPI_SERVICE];

  /* get timeout */
  timeout = Curl_timeleft(data, NULL, TRUE);

  /*   GSSAPI request looks like
   * +----+------+-----+----------------+
   * |VER | MTYP | LEN |     TOKEN      |
   * +----+------+----------------------+
   * | 1  |  1   |  2  | up to 2^16 - 1 |
   * +----+------+-----+----------------+
   */

  /* prepare service name */
  if (strchr(serviceptr,'/')) {
    service.value = malloc(strlen(serviceptr));
    if(!service.value)
      return CURLE_OUT_OF_MEMORY;
    service.length = strlen(serviceptr);
    memcpy(service.value, serviceptr, service.length);

    gss_major_status = gss_import_name(&gss_minor_status, &service,
                                       (gss_OID) GSS_C_NULL_OID, &server);
  }
  else {
    service.value = malloc(strlen(serviceptr) +strlen(conn->proxy.name)+2);
    if(!service.value)
      return CURLE_OUT_OF_MEMORY;
    service.length = strlen(serviceptr) +strlen(conn->proxy.name)+1;
    snprintf(service.value, service.length+1, "%s@%s",
             serviceptr, conn->proxy.name);

    gss_major_status = gss_import_name(&gss_minor_status, &service,
                                       gss_nt_service_name, &server);
  }

  gss_release_buffer(&gss_status, &service); /* clear allocated memory */

  if(check_gss_err(data,gss_major_status,
                   gss_minor_status,"gss_import_name()")) {
    failf(data, "Failed to create service name.");
    gss_release_name(&gss_status, &server);
    return CURLE_COULDNT_CONNECT;
  }

  /* As long as we need to keep sending some context info, and there's no  */
  /* errors, keep sending it...                                            */
  for(;;) {
    gss_major_status = gss_init_sec_context(&gss_minor_status,
                                            GSS_C_NO_CREDENTIAL,
                                            &gss_context, server,
                                            GSS_C_NULL_OID,
                                            GSS_C_MUTUAL_FLAG |
                                            GSS_C_REPLAY_FLAG,
                                            0,
                                            NULL,
                                            gss_token,
                                            NULL,
                                            &gss_send_token,
                                            &gss_ret_flags,
                                            NULL);

    if(gss_token != GSS_C_NO_BUFFER)
      gss_release_buffer(&gss_status, &gss_recv_token);
    if(check_gss_err(data,gss_major_status,
                     gss_minor_status,"gss_init_sec_context")) {
      gss_release_name(&gss_status, &server);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_release_buffer(&gss_status, &gss_send_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to initial GSSAPI token.");
      return CURLE_COULDNT_CONNECT;
    }

    if(gss_send_token.length != 0) {
      socksreq[0] = 1;    /* gssapi subnegotiation version */
      socksreq[1] = 1;    /* authentication message type */
      us_length = htons((short)gss_send_token.length);
      memcpy(socksreq+2,&us_length,sizeof(short));

      code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
      if((code != CURLE_OK) || (4 != written)) {
        failf(data, "Failed to send GSSAPI authentication request.");
        gss_release_name(&gss_status, &server);
        gss_release_buffer(&gss_status, &gss_recv_token);
        gss_release_buffer(&gss_status, &gss_send_token);
        gss_delete_sec_context(&gss_status, &gss_context, NULL);
        return CURLE_COULDNT_CONNECT;
      }

      code = Curl_write_plain(conn, sock, (char *)gss_send_token.value,
                              gss_send_token.length, &written);

      if((code != CURLE_OK) || ((ssize_t)gss_send_token.length != written)) {
        failf(data, "Failed to send GSSAPI authentication token.");
        gss_release_name(&gss_status, &server);
        gss_release_buffer(&gss_status, &gss_recv_token);
        gss_release_buffer(&gss_status, &gss_send_token);
        gss_delete_sec_context(&gss_status, &gss_context, NULL);
        return CURLE_COULDNT_CONNECT;
      }

    }

    gss_release_buffer(&gss_status, &gss_send_token);
    gss_release_buffer(&gss_status, &gss_recv_token);
    if(gss_major_status != GSS_S_CONTINUE_NEEDED) break;

    /* analyse response */

    /*   GSSAPI response looks like
     * +----+------+-----+----------------+
     * |VER | MTYP | LEN |     TOKEN      |
     * +----+------+----------------------+
     * | 1  |  1   |  2  | up to 2^16 - 1 |
     * +----+------+-----+----------------+
     */

    result=Curl_blockread_all(conn, sock, (char *)socksreq, 4,
                              &actualread, timeout);
    if(result != CURLE_OK || actualread != 4) {
      failf(data, "Failed to receive GSSAPI authentication response.");
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if(socksreq[1] == 255) { /* status / message type */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    if(socksreq[1] != 1) { /* status / messgae type */
      failf(data, "Invalid GSSAPI authentication response type (%d %d).",
            socksreq[0], socksreq[1]);
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(&us_length, socksreq+2, sizeof(short));
    us_length = ntohs(us_length);

    gss_recv_token.length=us_length;
    gss_recv_token.value=malloc(us_length);
    if(!gss_recv_token.value) {
      failf(data,
            "Could not allocate memory for GSSAPI authentication "
            "response token.");
      gss_release_name(&gss_status, &server);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_OUT_OF_MEMORY;
    }

    result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
                              gss_recv_token.length,
                              &actualread, timeout);

    if(result != CURLE_OK || actualread != us_length) {
      failf(data, "Failed to receive GSSAPI authentication token.");
      gss_release_name(&gss_status, &server);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    gss_token = &gss_recv_token;
  }

  gss_release_name(&gss_status, &server);

  /* Everything is good so far, user was authenticated! */
  gss_major_status = gss_inquire_context (&gss_minor_status, gss_context,
                                          &gss_client_name, NULL, NULL, NULL,
                                          NULL, NULL, NULL);
  if(check_gss_err(data,gss_major_status,
                   gss_minor_status,"gss_inquire_context")) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    failf(data, "Failed to determine user name.");
    return CURLE_COULDNT_CONNECT;
  }
  gss_major_status = gss_display_name(&gss_minor_status, gss_client_name,
                                      &gss_send_token, NULL);
  if(check_gss_err(data,gss_major_status,
                   gss_minor_status,"gss_display_name")) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    gss_release_buffer(&gss_status, &gss_send_token);
    failf(data, "Failed to determine user name.");
    return CURLE_COULDNT_CONNECT;
  }
  user=malloc(gss_send_token.length+1);
  if(!user) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    gss_release_name(&gss_status, &gss_client_name);
    gss_release_buffer(&gss_status, &gss_send_token);
    return CURLE_OUT_OF_MEMORY;
  }

  memcpy(user, gss_send_token.value, gss_send_token.length);
  user[gss_send_token.length] = '\0';
  gss_release_name(&gss_status, &gss_client_name);
  gss_release_buffer(&gss_status, &gss_send_token);
  infof(data, "SOCKS5 server authencticated user %s with gssapi.\n",user);
  free(user);
  user=NULL;

  /* Do encryption */
  socksreq[0] = 1;    /* gssapi subnegotiation version */
  socksreq[1] = 2;    /* encryption message type */

  gss_enc = 0; /* no data protection */
  /* do confidentiality protection if supported */
  if(gss_ret_flags & GSS_C_CONF_FLAG)
    gss_enc = 2;
  /* else do integrity protection */
  else if(gss_ret_flags & GSS_C_INTEG_FLAG)
    gss_enc = 1;

  infof(data, "SOCKS5 server supports gssapi %s data protection.\n",
        (gss_enc==0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
  /* force for the moment to no data protection */
  gss_enc = 0;
  /*
   * Sending the encryption type in clear seems wrong. It should be
   * protected with gss_seal()/gss_wrap(). See RFC1961 extract below
   * The NEC reference implementations on which this is based is
   * therefore at fault
   *
   *  +------+------+------+.......................+
   *  + ver  | mtyp | len  |   token               |
   *  +------+------+------+.......................+
   *  + 0x01 | 0x02 | 0x02 | up to 2^16 - 1 octets |
   *  +------+------+------+.......................+
   *
   *   Where:
   *
   *  - "ver" is the protocol version number, here 1 to represent the
   *    first version of the SOCKS/GSS-API protocol
   *
   *  - "mtyp" is the message type, here 2 to represent a protection
   *    -level negotiation message
   *
   *  - "len" is the length of the "token" field in octets
   *
   *  - "token" is the GSS-API encapsulated protection level
   *
   * The token is produced by encapsulating an octet containing the
   * required protection level using gss_seal()/gss_wrap() with conf_req
   * set to FALSE.  The token is verified using gss_unseal()/
   * gss_unwrap().
   *
   */
  if(data->set.socks5_gssapi_nec) {
    us_length = htons((short)1);
    memcpy(socksreq+2,&us_length,sizeof(short));
  }
  else {
    gss_send_token.length = 1;
    gss_send_token.value = malloc(1);
    if(!gss_send_token.value) {
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_OUT_OF_MEMORY;
    }
    memcpy(gss_send_token.value, &gss_enc, 1);

    gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0,
                                GSS_C_QOP_DEFAULT, &gss_send_token,
                                &gss_conf_state, &gss_w_token);

    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_wrap")) {
      gss_release_buffer(&gss_status, &gss_send_token);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to wrap GSSAPI encryption value into token.");
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_send_token);

    us_length = htons((short)gss_w_token.length);
    memcpy(socksreq+2,&us_length,sizeof(short));
  }

  code = Curl_write_plain(conn, sock, (char *)socksreq, 4, &written);
  if((code != CURLE_OK) || (4 != written)) {
    failf(data, "Failed to send GSSAPI encryption request.");
    gss_release_buffer(&gss_status, &gss_w_token);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(data->set.socks5_gssapi_nec) {
    memcpy(socksreq, &gss_enc, 1);
    code = Curl_write_plain(conn, sock, socksreq, 1, &written);
    if((code != CURLE_OK) || ( 1 != written)) {
      failf(data, "Failed to send GSSAPI encryption type.");
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }
  } else {
    code = Curl_write_plain(conn, sock, (char *)gss_w_token.value,
                            gss_w_token.length, &written);
    if((code != CURLE_OK) || ((ssize_t)gss_w_token.length != written)) {
      failf(data, "Failed to send GSSAPI encryption type.");
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_w_token);
  }

  result=Curl_blockread_all(conn, sock, (char *)socksreq, 4,
                            &actualread, timeout);
  if(result != CURLE_OK || actualread != 4) {
    failf(data, "Failed to receive GSSAPI encryption response.");
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  /* ignore the first (VER) byte */
  if(socksreq[1] == 255) { /* status / message type */
    failf(data, "User was rejected by the SOCKS5 server (%d %d).",
          socksreq[0], socksreq[1]);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[1] != 2) { /* status / messgae type */
    failf(data, "Invalid GSSAPI encryption response type (%d %d).",
          socksreq[0], socksreq[1]);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  memcpy(&us_length, socksreq+2, sizeof(short));
  us_length = ntohs(us_length);

  gss_recv_token.length= us_length;
  gss_recv_token.value=malloc(gss_recv_token.length);
  if(!gss_recv_token.value) {
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_OUT_OF_MEMORY;
  }
  result=Curl_blockread_all(conn, sock, (char *)gss_recv_token.value,
                            gss_recv_token.length,
                            &actualread, timeout);

  if(result != CURLE_OK || actualread != us_length) {
    failf(data, "Failed to receive GSSAPI encryptrion type.");
    gss_release_buffer(&gss_status, &gss_recv_token);
    gss_delete_sec_context(&gss_status, &gss_context, NULL);
    return CURLE_COULDNT_CONNECT;
  }

  if(!data->set.socks5_gssapi_nec) {
    gss_major_status = gss_unwrap(&gss_minor_status, gss_context,
                                  &gss_recv_token, &gss_w_token,
                                  0, GSS_C_QOP_DEFAULT);

    if(check_gss_err(data,gss_major_status,gss_minor_status,"gss_unwrap")) {
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      failf(data, "Failed to unwrap GSSAPI encryption value into token.");
      return CURLE_COULDNT_CONNECT;
    }
    gss_release_buffer(&gss_status, &gss_recv_token);

    if(gss_w_token.length != 1) {
      failf(data, "Invalid GSSAPI encryption response length (%d).",
            gss_w_token.length);
      gss_release_buffer(&gss_status, &gss_w_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(socksreq,gss_w_token.value,gss_w_token.length);
    gss_release_buffer(&gss_status, &gss_w_token);
  }
  else {
    if(gss_recv_token.length != 1) {
      failf(data, "Invalid GSSAPI encryption response length (%d).",
            gss_recv_token.length);
      gss_release_buffer(&gss_status, &gss_recv_token);
      gss_delete_sec_context(&gss_status, &gss_context, NULL);
      return CURLE_COULDNT_CONNECT;
    }

    memcpy(socksreq,gss_recv_token.value,gss_recv_token.length);
    gss_release_buffer(&gss_status, &gss_recv_token);
  }

  infof(data, "SOCKS5 access with%s protection granted.\n",
        (socksreq[0]==0)?"out gssapi data":
        ((socksreq[0]==1)?" gssapi integrity":" gssapi confidentiality"));

  conn->socks5_gssapi_enctype = socksreq[0];
  if(socksreq[0] == 0)
    gss_delete_sec_context(&gss_status, &gss_context, NULL);

  return CURLE_OK;
}
/*
 * Function: destroy_client
 *
 * Purpose: destroys a client entry and removes it from the database
 *
 * Arguments:
 *
 *	client_data	(r) the client to be destroyed
 *
 * Effects:
 *
 * client_data->context is deleted with gss_delete_sec_context.
 * client_data's entry in the database is destroyed.  client_data is
 * freed.
 */
static void destroy_client(svc_auth_gssapi_data *client_data)
{
     OM_uint32 gssstat, minor_stat;
     gss_buffer_desc out_buf;
     client_list *c, *c2;

     PRINTF(("destroy_client: destroying client_data\n"));
     L_PRINTF(2, ("destroy_client: client_data = %p\n", (void *) client_data));

#ifdef DEBUG_GSSAPI
     if (svc_debug_gssapi >= 3)
	  dump_db("before frees");
#endif

     /* destroy client struct even if error occurs */

     gssstat = gss_delete_sec_context(&minor_stat, &client_data->context,
				      &out_buf);
     if (gssstat != GSS_S_COMPLETE)
	  AUTH_GSSAPI_DISPLAY_STATUS(("deleting context", gssstat,
				      minor_stat));
     
     gss_release_buffer(&minor_stat, &out_buf);
     gss_release_name(&minor_stat, &client_data->client_name);
     if (client_data->prev_verf.length != 0)
	  gss_release_buffer(&minor_stat, &client_data->prev_verf);

     if (clients == NULL) {
	  PRINTF(("destroy_client: called on empty database\n"));
	  abort();
     } else if (clients->client == client_data) {
	  c = clients;
	  clients = clients->next;
	  free(c);
     } else {
	  c2 = clients;
	  c = clients->next;
	  while (c) {
	       if (c->client == client_data) {
		    c2->next = c->next;
		    free(c);
		    goto done;
	       } else {
		    c2 = c;
		    c = c->next;
	       }
	  }
	  PRINTF(("destroy_client: client_handle delete failed\n"));
	  abort();
     }
     
done:
     
     L_PRINTF(2, ("destroy_client: client %d destroyed\n", client_data->key));
     
     free(client_data);
     
#if 0 /*ifdef PURIFY*/
     purify_watch_n(client_data, sizeof(*client_data), "rw");
#endif
}
Exemple #5
0
CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
{
  struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg:
    &conn->data->state.negotiate;
  char *encoded = NULL;
  size_t len = 0;
  char *userp;
  CURLcode error;
  OM_uint32 discard_st;

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
  if(checkprefix("Negotiate", neg_ctx->protocol)) {
    ASN1_OBJECT    *object              = NULL;
    unsigned char  *responseToken       = NULL;
    size_t          responseTokenLength = 0;
    gss_buffer_desc spnegoToken         = GSS_C_EMPTY_BUFFER;

    responseToken = malloc(neg_ctx->output_token.length);
    if(responseToken == NULL)
      return CURLE_OUT_OF_MEMORY;
    memcpy(responseToken, neg_ctx->output_token.value,
           neg_ctx->output_token.length);
    responseTokenLength = neg_ctx->output_token.length;

    object = OBJ_txt2obj("1.2.840.113554.1.2.2", 1);
    if(!object) {
      Curl_safefree(responseToken);
      return CURLE_OUT_OF_MEMORY;
    }

    if(!makeSpnegoInitialToken(object,
                               responseToken,
                               responseTokenLength,
                               (unsigned char**)&spnegoToken.value,
                               &spnegoToken.length)) {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      infof(conn->data, "Make SPNEGO Initial Token failed\n");
    }
    else if(!spnegoToken.value || !spnegoToken.length) {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      if(spnegoToken.value)
        Curl_safefree(spnegoToken.value);
      infof(conn->data, "Make SPNEGO Initial Token succeeded (NULL token)\n");
    }
    else {
      Curl_safefree(responseToken);
      ASN1_OBJECT_free(object);
      gss_release_buffer(&discard_st, &neg_ctx->output_token);
      neg_ctx->output_token.value = spnegoToken.value;
      neg_ctx->output_token.length = spnegoToken.length;
      infof(conn->data, "Make SPNEGO Initial Token succeeded\n");
    }
  }
#endif
  error = Curl_base64_encode(conn->data,
                             neg_ctx->output_token.value,
                             neg_ctx->output_token.length,
                             &encoded, &len);
  if(error) {
	#ifdef HAVE_SPNEGO
	  Curl_safefree(neg_ctx->output_token.value);	
	#else 
	  gss_release_buffer(&discard_st, &neg_ctx->output_token);
	#endif
    neg_ctx->output_token.value = NULL;
    neg_ctx->output_token.length = 0;
    return error;
  }

  if(!encoded || !len) {
    #ifdef HAVE_SPNEGO
	  Curl_safefree(neg_ctx->output_token.value);	
	#else 
	  gss_release_buffer(&discard_st, &neg_ctx->output_token);
	#endif
    neg_ctx->output_token.value = NULL;
    neg_ctx->output_token.length = 0;
    return CURLE_REMOTE_ACCESS_DENIED;
  }

  userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "",
                  neg_ctx->protocol, encoded);
  if(proxy) {
    Curl_safefree(conn->allocptr.proxyuserpwd);
    conn->allocptr.proxyuserpwd = userp;
  }
  else {
    Curl_safefree(conn->allocptr.userpwd);
    conn->allocptr.userpwd = userp;
  }

  Curl_safefree(encoded);
  Curl_cleanup_negotiate(conn->data);

  return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK;
}
Exemple #6
0
DWORD
LWIGetGSSSecurityContextInfo(
    gss_ctx_id_t gss_ctx,
    PSTR *pszClientName
    )
{
    DWORD dwError = ERROR_SUCCESS;
    int gss_rc = 0;
    OM_uint32 minor_status = 0;
    gss_name_t src = GSS_C_NO_NAME;
    gss_buffer_desc src_name = GSS_C_EMPTY_BUFFER;
    gss_OID src_type = GSS_C_NULL_OID;

    PSTR pszClient = NULL;

    /* Fetch security context information to make it available
       on the server side (e.g. for security checks) */
    gss_rc = gss_inquire_context(&minor_status,
                                 gss_ctx,
                                 &src,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL);
    if (gss_rc == GSS_S_COMPLETE) {

        /*
         * Get calling principal name
         */
        gss_rc = gss_display_name(&minor_status, src, &src_name, &src_type);
        if (gss_rc != GSS_S_COMPLETE) {
            /*
             * TODO: error handling
             */

        }

        dwError = EVTStrndup(src_name.value, src_name.length, &pszClient);
        BAIL_ON_EVT_ERROR(dwError);

    } else {
        /* error handling */
    }

    *pszClientName = pszClient;

cleanup:

    gss_release_buffer(&minor_status, &src_name);

    if (src)
    {
        gss_release_name(&minor_status, &src);
    }

    return dwError;

error:
    EVT_SAFE_FREE_STRING(pszClient);
    *pszClientName = NULL;

    goto cleanup;
}
Exemple #7
0
/*
 * AEAD wrap API for a single piece of associated data, for compatibility
 * with MIT and as specified by draft-howard-gssapi-aead-00.txt.
 *
 * @ingroup gssapi
 */
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_wrap_aead(OM_uint32 *minor_status,
	      gss_ctx_id_t context_handle,
              int conf_req_flag,
              gss_qop_t qop_req,
              gss_buffer_t input_assoc_buffer,
              gss_buffer_t input_payload_buffer,
              int *conf_state,
              gss_buffer_t output_message_buffer)
{
    OM_uint32 major_status, tmp, flags = 0;
    gss_iov_buffer_desc iov[5];
    size_t i;
    unsigned char *p;

    memset(iov, 0, sizeof(iov));

    iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;

    iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
    if (input_assoc_buffer)
	iov[1].buffer = *input_assoc_buffer;

    iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
    if (input_payload_buffer)
	iov[2].buffer.length = input_payload_buffer->length;

    gss_inquire_context(minor_status, context_handle, NULL, NULL,
			NULL, NULL, &flags, NULL, NULL);

    /* krb5 mech rejects padding/trailer if DCE-style is set */
    iov[3].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
					    : GSS_IOV_BUFFER_TYPE_PADDING;
    iov[4].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
					    : GSS_IOV_BUFFER_TYPE_TRAILER;

    major_status = gss_wrap_iov_length(minor_status, context_handle,
				       conf_req_flag, qop_req, conf_state,
				       iov, 5);
    if (GSS_ERROR(major_status))
	return major_status;

    for (i = 0, output_message_buffer->length = 0; i < 5; i++) {
        if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
	    continue;

	output_message_buffer->length += iov[i].buffer.length;
    }

    output_message_buffer->value = malloc(output_message_buffer->length);
    if (output_message_buffer->value == NULL) {
	*minor_status = ENOMEM;
	return GSS_S_FAILURE;
    }

    for (i = 0, p = output_message_buffer->value; i < 5; i++) {
	if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
	    continue;
	else if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA)
	    memcpy(p, input_payload_buffer->value, input_payload_buffer->length);

	iov[i].buffer.value = p;
	p += iov[i].buffer.length;
    }

    major_status = gss_wrap_iov(minor_status, context_handle, conf_req_flag,
				qop_req, conf_state, iov, 5);
    if (GSS_ERROR(major_status))
        gss_release_buffer(&tmp, output_message_buffer);

    return major_status;
}
static OM_uint32
attr_pname_to_uid(OM_uint32 *minor,
                  const gss_mechanism mech,
                  const gss_name_t mech_name,
                  uid_t *uidp)
{
    OM_uint32 major = GSS_S_UNAVAILABLE;
#ifndef NO_PASSWORD
    OM_uint32 tmpMinor;
    int more = -1;

    if (mech->gss_get_name_attribute == NULL)
        return GSS_S_UNAVAILABLE;

    while (more != 0) {
        gss_buffer_desc value;
        gss_buffer_desc display_value;
        int authenticated = 0, complete = 0, code;
        char pwbuf[BUFSIZ];
        struct passwd pw, *pwd;
        char *localLoginUser;

        major = mech->gss_get_name_attribute(minor,
                                             mech_name,
                                             GSS_C_ATTR_LOCAL_LOGIN_USER,
                                             &authenticated,
                                             &complete,
                                             &value,
                                             &display_value,
                                             &more);
        if (GSS_ERROR(major)) {
            map_error(minor, mech);
            break;
        }

        localLoginUser = malloc(value.length + 1);
        if (localLoginUser == NULL) {
            major = GSS_S_FAILURE;
            *minor = ENOMEM;
            break;
        }

        memcpy(localLoginUser, value.value, value.length);
        localLoginUser[value.length] = '\0';

        code = k5_getpwnam_r(localLoginUser, &pw, pwbuf, sizeof(pwbuf), &pwd);

        free(localLoginUser);
        gss_release_buffer(&tmpMinor, &value);
        gss_release_buffer(&tmpMinor, &display_value);

        if (code == 0 && pwd != NULL) {
            *uidp = pwd->pw_uid;
            major = GSS_S_COMPLETE;
            *minor = 0;
            break;
        } else
            major = GSS_S_UNAVAILABLE;
    }
#endif /* !NO_PASSWORD */

    return major;
}
Exemple #9
0
/*
 * Create a new client struct from a file descriptor and establish a GSS-API
 * context as a specified service with an incoming client and fills out the
 * client struct.  Returns a new client struct on success and NULL on failure,
 * logging an appropriate error message.
 */
struct client *
server_new_client(int fd, gss_cred_id_t creds)
{
    struct client *client;
    struct sockaddr_storage ss;
    socklen_t socklen;
    size_t length;
    char *buffer;
    gss_buffer_desc send_tok, recv_tok, name_buf;
    gss_name_t name = GSS_C_NO_NAME;
    gss_OID doid;
    OM_uint32 major = 0;
    OM_uint32 minor = 0;
    OM_uint32 acc_minor;
    int flags, status;
    static const OM_uint32 req_gss_flags
        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);

    /* Create and initialize a new client struct. */
    client = xcalloc(1, sizeof(struct client));
    client->fd = fd;
    client->context = GSS_C_NO_CONTEXT;
    client->user = NULL;
    client->output = NULL;
    client->hostname = NULL;
    client->ipaddress = NULL;

    /* Fill in hostname and IP address. */
    socklen = sizeof(ss);
    if (getpeername(fd, (struct sockaddr *) &ss, &socklen) != 0) {
        syswarn("cannot get peer address");
        goto fail;
    }
    length = INET6_ADDRSTRLEN;
    buffer = xmalloc(length);
    client->ipaddress = buffer;
    status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length,
                         NULL, 0, NI_NUMERICHOST);
    if (status != 0) {
        syswarn("cannot translate IP address of client: %s",
                gai_strerror(status));
        goto fail;
    }
    length = NI_MAXHOST;
    buffer = xmalloc(length);
    status = getnameinfo((struct sockaddr *) &ss, socklen, buffer, length,
                         NULL, 0, NI_NAMEREQD);
    if (status == 0)
        client->hostname = buffer;
    else
        free(buffer);

    /* Accept the initial (worthless) token. */
    status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH,
                        TIMEOUT);
    if (status != TOKEN_OK) {
        warn_token("receiving initial token", status, major, minor);
        goto fail;
    }
    free(recv_tok.value);
    if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL))
        client->protocol = 2;
    else if (flags == (TOKEN_NOOP | TOKEN_CONTEXT_NEXT))
        client->protocol = 1;
    else {
        warn("bad token flags %d in initial token", flags);
        goto fail;
    }

    /* Now, do the real work of negotiating the context. */
    do {
        status = token_recv(client->fd, &flags, &recv_tok, TOKEN_MAX_LENGTH,
                            TIMEOUT);
        if (status != TOKEN_OK) {
            warn_token("receiving context token", status, major, minor);
            goto fail;
        }
        if (flags == TOKEN_CONTEXT)
            client->protocol = 1;
        else if (flags != (TOKEN_CONTEXT | TOKEN_PROTOCOL)) {
            warn("bad token flags %d in context token", flags);
            free(recv_tok.value);
            goto fail;
        }
        debug("received context token (size=%lu)",
              (unsigned long) recv_tok.length);
        major = gss_accept_sec_context(&acc_minor, &client->context, creds,
                    &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &name, &doid,
                    &send_tok, &client->flags, NULL, NULL);
        free(recv_tok.value);

        /* Send back a token if we need to. */
        if (send_tok.length != 0) {
            debug("sending context token (size=%lu)",
                  (unsigned long) send_tok.length);
            flags = TOKEN_CONTEXT;
            if (client->protocol > 1)
                flags |= TOKEN_PROTOCOL;
            status = token_send(client->fd, flags, &send_tok, TIMEOUT);
            if (status != TOKEN_OK) {
                warn_token("sending context token", status, major, minor);
                gss_release_buffer(&minor, &send_tok);
                goto fail;
            }
            gss_release_buffer(&minor, &send_tok);
        }

        /* Bail out if we lose. */
        if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED) {
            warn_gssapi("while accepting context", major, acc_minor);
            goto fail;
        }
        if (major == GSS_S_CONTINUE_NEEDED)
            debug("continue needed while accepting context");
    } while (major == GSS_S_CONTINUE_NEEDED);

    /* Make sure that the appropriate context flags are set. */
    if (client->protocol > 1) {
        if ((client->flags & req_gss_flags) != req_gss_flags) {
            warn("client did not negotiate appropriate GSS-API flags");
            goto fail;
        }
    }

    /* Get the display version of the client name and store it. */
    major = gss_display_name(&minor, name, &name_buf, &doid);
    if (major != GSS_S_COMPLETE) {
        warn_gssapi("while displaying client name", major, minor);
        goto fail;
    }
    major = gss_release_name(&minor, &name);
    client->user = xstrndup(name_buf.value, name_buf.length);
    gss_release_buffer(&minor, &name_buf);
    return client;

fail:
    if (client->context != GSS_C_NO_CONTEXT)
        gss_delete_sec_context(&minor, &client->context, GSS_C_NO_BUFFER);
    if (name != GSS_C_NO_NAME)
        gss_release_name(&minor, &name);
    if (client->ipaddress != NULL)
        free(client->ipaddress);
    if (client->hostname != NULL)
        free(client->hostname);
    free(client);
    return NULL;
}
Exemple #10
0
/* this performs a SASL/gssapi bind
   we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
   is very dependent on correctly configured DNS whereas
   this routine is much less fragile
   see RFC2078 and RFC2222 for details
*/
static ADS_STATUS ads_sasl_gssapi_do_bind(ADS_STRUCT *ads, const gss_name_t serv_name)
{
	uint32_t minor_status;
	gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
	gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
	gss_OID mech_type = GSS_C_NULL_OID;
	gss_buffer_desc output_token, input_token;
	uint32_t req_flags, ret_flags;
	int conf_state;
	struct berval cred;
	struct berval *scred = NULL;
	int i=0;
	int gss_rc, rc;
	uint8_t *p;
	uint32_t max_msg_size = ADS_SASL_WRAPPING_OUT_MAX_WRAPPED;
	uint8_t wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
	ADS_STATUS status;

	input_token.value = NULL;
	input_token.length = 0;

	status = ads_init_gssapi_cred(ads, &gss_cred);
	if (!ADS_ERR_OK(status)) {
		goto failed;
	}

	/*
	 * Note: here we always ask the gssapi for sign and seal
	 *       as this is negotiated later after the mutal
	 *       authentication
	 */
	req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;

	for (i=0; i < MAX_GSS_PASSES; i++) {
		gss_rc = gss_init_sec_context(&minor_status,
					  gss_cred,
					  &context_handle,
					  serv_name,
					  mech_type,
					  req_flags,
					  0,
					  NULL,
					  &input_token,
					  NULL,
					  &output_token,
					  &ret_flags,
					  NULL);
		if (scred) {
			ber_bvfree(scred);
			scred = NULL;
		}
		if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
			status = ADS_ERROR_GSS(gss_rc, minor_status);
			goto failed;
		}

		cred.bv_val = (char *)output_token.value;
		cred.bv_len = output_token.length;

		rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
				      &scred);
		if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
			status = ADS_ERROR(rc);
			goto failed;
		}

		if (output_token.value) {
			gss_release_buffer(&minor_status, &output_token);
		}

		if (scred) {
			input_token.value = scred->bv_val;
			input_token.length = scred->bv_len;
		} else {
			input_token.value = NULL;
			input_token.length = 0;
		}

		if (gss_rc == 0) break;
	}

	gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
			    &conf_state,NULL);
	if (scred) {
		ber_bvfree(scred);
		scred = NULL;
	}
	if (gss_rc) {
		status = ADS_ERROR_GSS(gss_rc, minor_status);
		goto failed;
	}

	p = (uint8_t *)output_token.value;

#if 0
	file_save("sasl_gssapi.dat", output_token.value, output_token.length);
#endif

	if (p) {
		wrap_type = CVAL(p,0);
		SCVAL(p,0,0);
		max_msg_size = RIVAL(p,0);
	}

	gss_release_buffer(&minor_status, &output_token);

	if (!(wrap_type & ads->ldap.wrap_type)) {
		/*
		 * the server doesn't supports the wrap
		 * type we want :-(
		 */
		DEBUG(0,("The ldap sasl wrap type doesn't match wanted[%d] server[%d]\n",
			ads->ldap.wrap_type, wrap_type));
		DEBUGADD(0,("You may want to set the 'client ldap sasl wrapping' option\n"));
		status = ADS_ERROR_NT(NT_STATUS_NOT_SUPPORTED);
		goto failed;
	}

	/* 0x58 is the minimum windows accepts */
	if (max_msg_size < 0x58) {
		max_msg_size = 0x58;
	}

	output_token.length = 4;
	output_token.value = SMB_MALLOC(output_token.length);
	if (!output_token.value) {
		output_token.length = 0;
		status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
		goto failed;
	}
	p = (uint8_t *)output_token.value;

	RSIVAL(p,0,max_msg_size);
	SCVAL(p,0,ads->ldap.wrap_type);

	/*
	 * we used to add sprintf("dn:%s", ads->config.bind_path) here.
	 * but using ads->config.bind_path is the wrong! It should be
	 * the DN of the user object!
	 *
	 * w2k3 gives an error when we send an incorrect DN, but sending nothing
	 * is ok and matches the information flow used in GSS-SPNEGO.
	 */

	gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
			&output_token, /* used as *input* here. */
			&conf_state,
			&input_token); /* Used as *output* here. */
	if (gss_rc) {
		status = ADS_ERROR_GSS(gss_rc, minor_status);
		output_token.length = 0;
		SAFE_FREE(output_token.value);
		goto failed;
	}

	/* We've finished with output_token. */
	SAFE_FREE(output_token.value);
	output_token.length = 0;

	cred.bv_val = (char *)input_token.value;
	cred.bv_len = input_token.length;

	rc = ldap_sasl_bind_s(ads->ldap.ld, NULL, "GSSAPI", &cred, NULL, NULL, 
			      &scred);
	gss_release_buffer(&minor_status, &input_token);
	status = ADS_ERROR(rc);
	if (!ADS_ERR_OK(status)) {
		goto failed;
	}

	if (ads->ldap.wrap_type > ADS_SASLWRAP_TYPE_PLAIN) {
		gss_rc = gss_wrap_size_limit(&minor_status, context_handle,
					     (ads->ldap.wrap_type == ADS_SASLWRAP_TYPE_SEAL),
					     GSS_C_QOP_DEFAULT,
					     max_msg_size, &ads->ldap.out.max_unwrapped);
		if (gss_rc) {
			status = ADS_ERROR_GSS(gss_rc, minor_status);
			goto failed;
		}

		ads->ldap.out.sig_size = max_msg_size - ads->ldap.out.max_unwrapped;
		ads->ldap.in.min_wrapped = 0x2C; /* taken from a capture with LDAP unbind */
		ads->ldap.in.max_wrapped = ADS_SASL_WRAPPING_IN_MAX_WRAPPED;
		status = ads_setup_sasl_wrapping(ads, &ads_sasl_gssapi_ops, context_handle);
		if (!ADS_ERR_OK(status)) {
			DEBUG(0, ("ads_setup_sasl_wrapping() failed: %s\n",
				ads_errstr(status)));
			goto failed;
		}
		/* make sure we don't free context_handle */
		context_handle = GSS_C_NO_CONTEXT;
	}

failed:
	if (gss_cred != GSS_C_NO_CREDENTIAL)
		gss_release_cred(&minor_status, &gss_cred);
	if (context_handle != GSS_C_NO_CONTEXT)
		gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);

	if(scred)
		ber_bvfree(scred);
	return status;
}
Exemple #11
0
/*
 * Function: client_establish_context
 *
 * Purpose: establishes a GSS-API context with a specified service and
 * returns the context handle
 *
 * Arguments:
 *
 *      s                   (r) an established TCP connection to the service
 *      service_name(r) the ASCII service name of the service
 *      gss_flags       (r) GSS-API delegation flag (if any)
 *      auth_flag       (r) whether to actually do authentication
 *  v1_format   (r) whether the v1 sample protocol should be used
 *      oid                 (r) OID of the mechanism to use
 *      context         (w) the established GSS-API context
 *      ret_flags       (w) the returned flags from init_sec_context
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * service_name is imported as a GSS-API name and a GSS-API context is
 * established with the corresponding service; the service should be
 * listening on the TCP connection s.  The default GSS-API mechanism
 * is used, and mutual authentication and replay detection are
 * requested.
 *
 * If successful, the context handle is returned in context.  If
 * unsuccessful, the GSS-API error messages are displayed on stderr
 * and -1 is returned.
 */
static int
client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
                         int auth_flag, int v1_format, gss_OID oid,
                         char *username, char *password,
                         gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
{
    if (auth_flag) {
        gss_buffer_desc send_tok, recv_tok, *token_ptr;
        gss_name_t target_name;
        OM_uint32 maj_stat, min_stat, init_sec_min_stat;
        int token_flags;
        gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
        gss_name_t gss_username = GSS_C_NO_NAME;
        gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
#ifndef MECH_EAP
        struct gss_channel_bindings_struct cb = {GSS_C_AF_NULLADDR, {0, NULL},
                                                 GSS_C_AF_NULLADDR, {0, NULL},
            {strlen("HLJHLJHLJHLJHJKLHLJHLJH"), "HLJHLJHLJHLJHJKLHLJHLJH"}};
#endif

        if (spnego) {
            mechs.elements = &gss_spnego_mechanism_oid_desc;
            mechs.count = 1;
            mechsp = &mechs;
        } else if (oid != GSS_C_NO_OID) {
            mechs.elements = oid;
            mechs.count = 1;
            mechsp = &mechs;
        } else {
            mechs.elements = NULL;
            mechs.count = 0;
        }

        if (username != NULL) {
            send_tok.value = username;
            send_tok.length = strlen(username);

            maj_stat = gss_import_name(&min_stat, &send_tok,
                                       (gss_OID) gss_nt_user_name,
                                       &gss_username);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("parsing client name", maj_stat, min_stat);
                return -1;
            }
        }

        if (password != NULL) {
            gss_buffer_desc pwbuf;

            pwbuf.value = password;
            pwbuf.length = strlen(password);

            maj_stat = gss_acquire_cred_with_password(&min_stat,
                                                      gss_username,
                                                      &pwbuf, 0,
                                                      mechsp, GSS_C_INITIATE,
                                                      &cred, NULL, NULL);
        } else if (gss_username != GSS_C_NO_NAME) {
            maj_stat = gss_acquire_cred(&min_stat,
                                        gss_username, 0,
                                        mechsp, GSS_C_INITIATE,
                                        &cred, NULL, NULL);
        } else
            maj_stat = GSS_S_COMPLETE;
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("acquiring creds", maj_stat, min_stat);
            gss_release_name(&min_stat, &gss_username);
            return -1;
        }
        if (spnego && oid != GSS_C_NO_OID) {
            gss_OID_set_desc neg_mechs;

            neg_mechs.elements = oid;
            neg_mechs.count = 1;

            maj_stat = gss_set_neg_mechs(&min_stat, cred, &neg_mechs);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("setting neg mechs", maj_stat, min_stat);
                gss_release_name(&min_stat, &gss_username);
                gss_release_cred(&min_stat, &cred);
                return -1;
            }
        }
        gss_release_name(&min_stat, &gss_username);

        /*
         * Import the name into target_name.  Use send_tok to save
         * local variable space.
         */
        send_tok.value = service_name;
        send_tok.length = strlen(service_name);
        maj_stat = gss_import_name(&min_stat, &send_tok,
                                   (gss_OID) gss_nt_service_name,
                                   &target_name);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("parsing name", maj_stat, min_stat);
            return -1;
        }

        if (!v1_format) {
            if (send_token(s, TOKEN_NOOP | TOKEN_CONTEXT_NEXT, empty_token) <
                0) {
                (void) gss_release_name(&min_stat, &target_name);
                return -1;
            }
        }

        /*
         * Perform the context-establishement loop.
         *
         * On each pass through the loop, token_ptr points to the token
         * to send to the server (or GSS_C_NO_BUFFER on the first pass).
         * Every generated token is stored in send_tok which is then
         * transmitted to the server; every received token is stored in
         * recv_tok, which token_ptr is then set to, to be processed by
         * the next call to gss_init_sec_context.
         *
         * GSS-API guarantees that send_tok's length will be non-zero
         * if and only if the server is expecting another token from us,
         * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
         * and only if the server has another token to send us.
         */

        token_ptr = GSS_C_NO_BUFFER;
        *gss_context = GSS_C_NO_CONTEXT;

        do {
            maj_stat = gss_init_sec_context(&init_sec_min_stat,
                                            cred, gss_context,
                                            target_name, mechs.elements,
                                            gss_flags, 0,
#ifdef MECH_EAP
                                            NULL, /* channel bindings */
#else
                                            &cb,
#endif
                                            token_ptr, NULL, /* mech type */
                                            &send_tok, ret_flags,
                                            NULL);  /* time_rec */

            if (token_ptr != GSS_C_NO_BUFFER)
                free(recv_tok.value);

            if (send_tok.length != 0) {
                if (verbose)
                    printf("Sending init_sec_context token (size=%d)...",
                           (int) send_tok.length);
                if (send_token(s, v1_format ? 0 : TOKEN_CONTEXT, &send_tok) <
                    0) {
                    (void) gss_release_buffer(&min_stat, &send_tok);
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
                }
            }
            (void) gss_release_buffer(&min_stat, &send_tok);

            if (maj_stat != GSS_S_COMPLETE
                && maj_stat != GSS_S_CONTINUE_NEEDED) {
                display_status("initializing context", maj_stat,
                               init_sec_min_stat);
                (void) gss_release_name(&min_stat, &target_name);
                if (*gss_context != GSS_C_NO_CONTEXT)
                    gss_delete_sec_context(&min_stat, gss_context,
                                           GSS_C_NO_BUFFER);
                return -1;
            }

            if (maj_stat == GSS_S_CONTINUE_NEEDED) {
                if (verbose)
                    printf("continue needed...");
                if (recv_token(s, &token_flags, &recv_tok) < 0) {
                    (void) gss_release_name(&min_stat, &target_name);
                    return -1;
                }
                token_ptr = &recv_tok;
            }
            if (verbose)
                printf("\n");
        } while (maj_stat == GSS_S_CONTINUE_NEEDED);

        (void) gss_release_cred(&min_stat, &cred);
        (void) gss_release_name(&min_stat, &target_name);
    } else {
        if (send_token(s, TOKEN_NOOP, empty_token) < 0)
            return -1;
    }

    return 0;
}
Exemple #12
0
static void
constrained_delegate(gss_OID_set desired_mechs, gss_name_t target,
                     gss_cred_id_t delegated_cred_handle,
                     gss_cred_id_t verifier_cred_handle)
{
    OM_uint32 major, minor;
    gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
    gss_name_t cred_name = GSS_C_NO_NAME;
    OM_uint32 time_rec, lifetime;
    gss_cred_usage_t usage;
    gss_buffer_desc token;
    gss_OID_set mechs;

    printf("Constrained delegation tests follow\n");
    printf("-----------------------------------\n\n");

    if (gss_inquire_cred(&minor, verifier_cred_handle, &cred_name,
                         &lifetime, &usage, NULL) == GSS_S_COMPLETE) {
        display_canon_name("Proxy name", cred_name, &mech_krb5);
        (void)gss_release_name(&minor, &cred_name);
    }
    display_canon_name("Target name", target, &mech_krb5);
    if (gss_inquire_cred(&minor, delegated_cred_handle, &cred_name,
                         &lifetime, &usage, &mechs) == GSS_S_COMPLETE) {
        display_canon_name("Delegated name", cred_name, &mech_krb5);
        display_oid("Delegated mech", &mechs->elements[0]);
        (void)gss_release_name(&minor, &cred_name);
    }

    printf("\n");

    major = gss_init_sec_context(&minor, delegated_cred_handle,
                                 &initiator_context, target,
                                 mechs ? &mechs->elements[0] : &mech_krb5,
                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
                                 GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
                                 GSS_C_NO_BUFFER, NULL, &token, NULL,
                                 &time_rec);
    check_gsserr("gss_init_sec_context", major, minor);

    (void)gss_release_buffer(&minor, &token);
    (void)gss_delete_sec_context(&minor, &initiator_context, NULL);

    /* Ensure a second call does not acquire new ticket. */
    major = gss_init_sec_context(&minor, delegated_cred_handle,
                                 &initiator_context, target,
                                 mechs ? &mechs->elements[0] : &mech_krb5,
                                 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
                                 GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
                                 GSS_C_NO_BUFFER, NULL, &token, NULL,
                                 &time_rec);
    check_gsserr("gss_init_sec_context", major, minor);

    (void)gss_release_buffer(&minor, &token);
    (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
    (void)gss_release_oid_set(&minor, &mechs);

    /* We expect three tickets: our TGT, the evidence ticket, and the ticket to
     * the target service. */
    check_ticket_count(delegated_cred_handle, 3);
}
Exemple #13
0
static int
check_iprop_rpcsec_auth(struct svc_req *rqstp)
{
    /* XXX Since the client can authenticate against any principal in
       the database, we need to do a sanity check.  Only checking for
       "kiprop" now, but that means theoretically the client could be
       authenticating to kiprop on some other machine.  */
    /* Code taken from kadm_rpc_svc.c, tweaked.  */

     gss_ctx_id_t ctx;
     krb5_context kctx;
     OM_uint32 maj_stat, min_stat;
     gss_name_t name;
     krb5_principal princ;
     int ret, success;
     krb5_data *c1, *c2, *realm;
     gss_buffer_desc gss_str;
     kadm5_server_handle_t handle;
     size_t slen;
     char *sdots;

     success = 0;
     handle = (kadm5_server_handle_t)global_server_handle;

     if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
	  return 0;

     ctx = rqstp->rq_svccred;

     maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
				    NULL, NULL, NULL, NULL, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
	  krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: "
			   "failed inquire_context, stat=%u", maj_stat);
	  log_badauth(maj_stat, min_stat,
		      &rqstp->rq_xprt->xp_raddr, NULL);
	  goto fail_name;
     }

     kctx = handle->context;
     ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
     if (ret == 0)
	  goto fail_name;

     slen = gss_str.length;
     trunc_name(&slen, &sdots);
     /*
      * Since we accept with GSS_C_NO_NAME, the client can authenticate
      * against the entire kdb.  Therefore, ensure that the service
      * name is something reasonable.
      */
     if (krb5_princ_size(kctx, princ) != 2)
	  goto fail_princ;

     c1 = krb5_princ_component(kctx, princ, 0);
     c2 = krb5_princ_component(kctx, princ, 1);
     realm = krb5_princ_realm(kctx, princ);
     if (strncmp(handle->params.realm, realm->data, realm->length) == 0
	 && strncmp("kiprop", c1->data, c1->length) == 0) {
	 success = 1;
     }

fail_princ:
     if (!success) {
	 krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s",
			  (int) slen, (char *) gss_str.value, sdots);
     }
     gss_release_buffer(&min_stat, &gss_str);
     krb5_free_principal(kctx, princ);
fail_name:
     gss_release_name(&min_stat, &name);
     return success;
}
/*
 * Function: server_establish_context
 *
 * Purpose: establishses a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the client
 *      service_creds   (r) server credentials, from gss_acquire_cred
 *      context         (w) the established GSS-API context
 *      client_name     (w) the client's ASCII name
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
static int
server_establish_context(int s, gss_cred_id_t server_creds,
                         gss_ctx_id_t *context, gss_buffer_t client_name,
                         OM_uint32 *ret_flags)
{
    gss_buffer_desc send_tok, recv_tok;
    gss_name_t client;
    gss_OID doid;
    OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
    gss_buffer_desc oid_name;
    int     token_flags;

    if (recv_token(s, &token_flags, &recv_tok) < 0)
        return -1;

    if (recv_tok.value) {
        free(recv_tok.value);
        recv_tok.value = NULL;
    }

    if (!(token_flags & TOKEN_NOOP)) {
        if (logfile)
            fprintf(logfile, "Expected NOOP token, got %d token instead\n",
                    token_flags);
        return -1;
    }

    *context = GSS_C_NO_CONTEXT;

    if (token_flags & TOKEN_CONTEXT_NEXT) {
        do {
            if (recv_token(s, &token_flags, &recv_tok) < 0)
                return -1;

            if (verbose && logfile) {
                fprintf(logfile, "Received token (size=%d): \n",
                        (int) recv_tok.length);
                print_token(&recv_tok);
            }

            maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context,
                                              server_creds, &recv_tok,
                                              GSS_C_NO_CHANNEL_BINDINGS,
                                              &client, &doid, &send_tok,
                                              ret_flags,
                                              NULL,  /* time_rec */
                                              NULL); /* del_cred_handle */

            if (recv_tok.value) {
                free(recv_tok.value);
                recv_tok.value = NULL;
            }

            if (send_tok.length != 0) {
                if (verbose && logfile) {
                    fprintf(logfile,
                            "Sending accept_sec_context token (size=%d):\n",
                            (int) send_tok.length);
                    print_token(&send_tok);
                }
                if (send_token(s, TOKEN_CONTEXT, &send_tok) < 0) {
                    if (logfile)
                        fprintf(logfile, "failure sending token\n");
                    return -1;
                }

                (void) gss_release_buffer(&min_stat, &send_tok);
            }
            if (maj_stat != GSS_S_COMPLETE
                && maj_stat != GSS_S_CONTINUE_NEEDED) {
                display_status("accepting context", maj_stat,
                               acc_sec_min_stat);
                if (*context != GSS_C_NO_CONTEXT)
                    gss_delete_sec_context(&min_stat, context,
                                           GSS_C_NO_BUFFER);
                return -1;
            }

            if (verbose && logfile) {
                if (maj_stat == GSS_S_CONTINUE_NEEDED)
                    fprintf(logfile, "continue needed...\n");
                else
                    fprintf(logfile, "\n");
                fflush(logfile);
            }
        } while (maj_stat == GSS_S_CONTINUE_NEEDED);

        /* display the flags */
        display_ctx_flags(*ret_flags);

        if (verbose && logfile) {
            maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
            if (maj_stat != GSS_S_COMPLETE) {
                display_status("converting oid->string", maj_stat, min_stat);
                return -1;
            }
            fprintf(logfile, "Accepted connection using mechanism OID %.*s.\n",
                    (int) oid_name.length, (char *) oid_name.value);
            (void) gss_release_buffer(&min_stat, &oid_name);
        }

        maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("displaying name", maj_stat, min_stat);
            return -1;
        }
        enumerateAttributes(&min_stat, client, TRUE);
        showLocalIdentity(&min_stat, client);
        maj_stat = gss_release_name(&min_stat, &client);
        if (maj_stat != GSS_S_COMPLETE) {
            display_status("releasing name", maj_stat, min_stat);
            return -1;
        }
    } else {
        client_name->length = *ret_flags = 0;

        if (logfile)
            fprintf(logfile, "Accepted unauthenticated connection.\n");
    }

    return 0;
}
/*
 * Curl_sasl_create_gssapi_security_message()
 *
 * This is used to generate an already encoded GSSAPI (Kerberos V5) security
 * token message ready for sending to the recipient.
 *
 * Parameters:
 *
 * data    [in]     - The session handle.
 * chlg64  [in]     - Pointer to the optional base64 encoded challenge message.
 * krb5    [in/out] - The gssapi data struct being used and modified.
 * outptr  [in/out] - The address where a pointer to newly allocated memory
 *                    holding the result will be stored upon completion.
 * outlen  [out]    - The length of the output message.
 *
 * Returns CURLE_OK on success.
 */
CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data,
                                                  const char *chlg64,
                                                  struct kerberos5data *krb5,
                                                  char **outptr,
                                                  size_t *outlen)
{
  CURLcode result = CURLE_OK;
  size_t chlglen = 0;
  size_t messagelen = 0;
  unsigned char *chlg = NULL;
  unsigned char *message = NULL;
  OM_uint32 gss_status;
  OM_uint32 gss_major_status;
  OM_uint32 gss_minor_status;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  unsigned int indata = 0;
  unsigned int outdata = 0;
  gss_qop_t qop = GSS_C_QOP_DEFAULT;
  unsigned int sec_layer = 0;
  unsigned int max_size = 0;
  gss_name_t username = GSS_C_NO_NAME;
  gss_buffer_desc username_token;

  /* Decode the base-64 encoded input message */
  if(strlen(chlg64) && *chlg64 != '=') {
    result = Curl_base64_decode(chlg64, &chlg, &chlglen);
    if(result)
      return result;
  }

  /* Ensure we have a valid challenge message */
  if(!chlg) {
    infof(data, "GSSAPI handshake failure (empty security message)\n");

    return CURLE_BAD_CONTENT_ENCODING;
  }

  /* Get the fully qualified username back from the context */
  gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context,
                                         &username, NULL, NULL, NULL, NULL,
                                         NULL, NULL);
  if(GSS_ERROR(gss_major_status)) {
    Curl_gss_log_error(data, gss_minor_status,
                       "gss_inquire_context() failed: ");

    Curl_safefree(chlg);

    return CURLE_OUT_OF_MEMORY;
  }

  /* Convert the username from internal format to a displayable token */
  gss_major_status = gss_display_name(&gss_minor_status, username,
                                      &username_token, NULL);
  if(GSS_ERROR(gss_major_status)) {
    Curl_gss_log_error(data, gss_minor_status, "gss_display_name() failed: ");

    Curl_safefree(chlg);

    return CURLE_OUT_OF_MEMORY;
  }

  /* Setup the challenge "input" security buffer */
  input_token.value = chlg;
  input_token.length = chlglen;

  /* Decrypt the inbound challenge and obtain the qop */
  gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token,
                                &output_token, NULL, &qop);
  if(GSS_ERROR(gss_major_status)) {
    Curl_gss_log_error(data, gss_minor_status, "gss_unwrap() failed: ");

    gss_release_buffer(&gss_status, &username_token);
    Curl_safefree(chlg);

    return CURLE_BAD_CONTENT_ENCODING;
  }

  /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
  if(output_token.length != 4) {
    infof(data, "GSSAPI handshake failure (invalid security data)\n");

    gss_release_buffer(&gss_status, &username_token);
    Curl_safefree(chlg);

    return CURLE_BAD_CONTENT_ENCODING;
  }

  /* Copy the data out and free the challenge as it is not required anymore */
  memcpy(&indata, output_token.value, 4);
  gss_release_buffer(&gss_status, &output_token);
  Curl_safefree(chlg);

  /* Extract the security layer */
  sec_layer = indata & 0x000000FF;
  if(!(sec_layer & GSSAUTH_P_NONE)) {
    infof(data, "GSSAPI handshake failure (invalid security layer)\n");

    gss_release_buffer(&gss_status, &username_token);

    return CURLE_BAD_CONTENT_ENCODING;
  }

  /* Extract the maximum message size the server can receive */
  max_size = ntohl(indata & 0xFFFFFF00);
  if(max_size > 0) {
    /* The server has told us it supports a maximum receive buffer, however, as
       we don't require one unless we are encrypting data, we tell the server
       our receive buffer is zero. */
    max_size = 0;
  }

  /* Allocate our message */
  messagelen = sizeof(outdata) + username_token.length + 1;
  message = malloc(messagelen);
  if(!message) {
    gss_release_buffer(&gss_status, &username_token);

    return CURLE_OUT_OF_MEMORY;
  }

  /* Populate the message with the security layer, client supported receive
     message size and authorization identity including the 0x00 based
     terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization
     identity is not terminated with the zero-valued (%x00) octet." it seems
     necessary to include it. */
  outdata = htonl(max_size) | sec_layer;
  memcpy(message, &outdata, sizeof(outdata));
  memcpy(message + sizeof(outdata), username_token.value,
         username_token.length);
  message[messagelen - 1] = '\0';

  /* Free the username token as it is not required anymore */
  gss_release_buffer(&gss_status, &username_token);

  /* Setup the "authentication data" security buffer */
  input_token.value = message;
  input_token.length = messagelen;

  /* Encrypt the data */
  gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0,
                              GSS_C_QOP_DEFAULT, &input_token, NULL,
                              &output_token);
  if(GSS_ERROR(gss_major_status)) {
    Curl_gss_log_error(data, gss_minor_status, "gss_wrap() failed: ");

    Curl_safefree(message);

    return CURLE_OUT_OF_MEMORY;
  }

  /* Base64 encode the response */
  result = Curl_base64_encode(data, (char *) output_token.value,
                              output_token.length, outptr, outlen);

  /* Free the output buffer */
  gss_release_buffer(&gss_status, &output_token);

  /* Free the message buffer */
  Curl_safefree(message);

  return result;
}
Exemple #16
0
int
main(int argc, char **argv)
{
    struct http_req req;
    const char *host, *page;
    int i, done, print_body, gssapi_done, gssapi_started;
    char *headers[10]; /* XXX */
    int num_headers;
    gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
    gss_name_t server = GSS_C_NO_NAME;
    int optind = 0;
    gss_OID mech_oid;
    OM_uint32 flags;

    setprogname(argv[0]);

    if(getarg(http_args, num_http_args, argc, argv, &optind))
	usage(1);

    if (help_flag)
	usage (0);

    if(version_flag) {
	print_version(NULL);
	exit(0);
    }

    argc -= optind;
    argv += optind;

    mech_oid = select_mech(mech);

    if (argc != 1 && argc != 2)
	errx(1, "usage: %s host [page]", getprogname());
    host = argv[0];
    if (argc == 2)
	page = argv[1];
    else
	page = "/";

    flags = 0;
    if (delegate_flag)
	flags |= GSS_C_DELEG_FLAG;
    if (mutual_flag)
	flags |= GSS_C_MUTUAL_FLAG;

    done = 0;
    num_headers = 0;
    gssapi_done = 1;
    gssapi_started = 0;
    do {
	print_body = 0;

	http_query(host, page, headers, num_headers, &req);
	for (i = 0 ; i < num_headers; i++)
	    free(headers[i]);
	num_headers = 0;

	if (strstr(req.response, " 200 ") != NULL) {
	    print_body = 1;
	    done = 1;
	} else if (strstr(req.response, " 401 ") != NULL) {
	    if (http_find_header(&req, "WWW-Authenticate:") == NULL)
		errx(1, "Got %s but missed `WWW-Authenticate'", req.response);
	    gssapi_done = 0;
	}

	if (!gssapi_done) {
	    const char *h = http_find_header(&req, "WWW-Authenticate:");
	    if (h == NULL)
		errx(1, "Got %s but missed `WWW-Authenticate'", req.response);

	    if (strncasecmp(h, "Negotiate", 9) == 0) {
		OM_uint32 maj_stat, min_stat;
		gss_buffer_desc input_token, output_token;

		if (verbose_flag)
		    printf("Negotiate found\n");

		if (server == GSS_C_NO_NAME) {
		    char *name;
		    asprintf(&name, "%s@%s", gss_service, host);
		    input_token.length = strlen(name);
		    input_token.value = name;

		    maj_stat = gss_import_name(&min_stat,
					       &input_token,
					       GSS_C_NT_HOSTBASED_SERVICE,
					       &server);
		    if (GSS_ERROR(maj_stat))
			gss_err (1, min_stat, "gss_inport_name");
		    free(name);
		    input_token.length = 0;
		    input_token.value = NULL;
		}

		i = 9;
		while(h[i] && isspace((unsigned char)h[i]))
		    i++;
		if (h[i] != '\0') {
		    int len = strlen(&h[i]);
		    if (len == 0)
			errx(1, "invalid Negotiate token");
		    input_token.value = emalloc(len);
		    len = base64_decode(&h[i], input_token.value);
		    if (len < 0)
			errx(1, "invalid base64 Negotiate token %s", &h[i]);
		    input_token.length = len;
		} else {
		    if (gssapi_started)
			errx(1, "Negotiate already started");
		    gssapi_started = 1;

		    input_token.length = 0;
		    input_token.value = NULL;
		}

		maj_stat =
		    gss_init_sec_context(&min_stat,
					 GSS_C_NO_CREDENTIAL,
					 &context_hdl,
					 server,
					 mech_oid,
					 flags,
					 0,
					 GSS_C_NO_CHANNEL_BINDINGS,
					 &input_token,
					 NULL,
					 &output_token,
					 NULL,
					 NULL);
		if (GSS_ERROR(maj_stat))
		    gss_err (1, min_stat, "gss_init_sec_context");
		else if (maj_stat & GSS_S_CONTINUE_NEEDED)
		    gssapi_done = 0;
		else {
		    gss_name_t targ_name, src_name;
		    gss_buffer_desc name_buffer;
		    gss_OID mech_type;

		    gssapi_done = 1;

		    printf("Negotiate done: %s\n", mech);

		    maj_stat = gss_inquire_context(&min_stat,
						   context_hdl,
						   &src_name,
						   &targ_name,
						   NULL,
						   &mech_type,
						   NULL,
						   NULL,
						   NULL);
		    if (GSS_ERROR(maj_stat))
			gss_err (1, min_stat, "gss_inquire_context");

		    maj_stat = gss_display_name(&min_stat,
						src_name,
						&name_buffer,
						NULL);
		    if (GSS_ERROR(maj_stat))
			gss_err (1, min_stat, "gss_display_name");

		    printf("Source: %.*s\n",
			   (int)name_buffer.length,
			   (char *)name_buffer.value);

		    gss_release_buffer(&min_stat, &name_buffer);

		    maj_stat = gss_display_name(&min_stat,
						targ_name,
						&name_buffer,
						NULL);
		    if (GSS_ERROR(maj_stat))
			gss_err (1, min_stat, "gss_display_name");

		    printf("Target: %.*s\n",
			   (int)name_buffer.length,
			   (char *)name_buffer.value);

		    gss_release_name(&min_stat, &targ_name);
		    gss_release_buffer(&min_stat, &name_buffer);
		}

		if (output_token.length) {
		    char *neg_token;

		    base64_encode(output_token.value,
				  output_token.length,
				  &neg_token);

		    asprintf(&headers[0], "Authorization: Negotiate %s",
			     neg_token);

		    num_headers = 1;
		    free(neg_token);
		    gss_release_buffer(&min_stat, &output_token);
		}
		if (input_token.length)
		    free(input_token.value);

	    } else
		done = 1;
	} else
	    done = 1;

	if (verbose_flag) {
	    printf("%s\n\n", req.response);

	    for (i = 0; i < req.num_headers; i++)
		printf("%s\n", req.headers[i]);
	    printf("\n");
	}
	if (print_body || verbose_flag)
	    printf("%.*s\n", (int)req.body_size, (char *)req.body);

	http_req_free(&req);
    } while (!done);

    if (gssapi_done == 0)
	errx(1, "gssapi not done but http dance done");

    return 0;
}
/*
 * Curl_sasl_create_gssapi_user_message()
 *
 * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
 * message ready for sending to the recipient.
 *
 * Parameters:
 *
 * data        [in]     - The session handle.
 * userp       [in]     - The user name.
 * passdwp     [in]     - The user's password.
 * service     [in]     - The service type such as www, smtp, pop or imap.
 * mutual_auth [in]     - Flag specifing whether or not mutual authentication
 *                        is enabled.
 * chlg64      [in]     - Pointer to the optional base64 encoded challenge
 *                        message.
 * krb5        [in/out] - The gssapi data struct being used and modified.
 * outptr      [in/out] - The address where a pointer to newly allocated memory
 *                        holding the result will be stored upon completion.
 * outlen      [out]    - The length of the output message.
 *
 * Returns CURLE_OK on success.
 */
CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data,
                                              const char *userp,
                                              const char *passwdp,
                                              const char *service,
                                              const bool mutual_auth,
                                              const char *chlg64,
                                              struct kerberos5data *krb5,
                                              char **outptr, size_t *outlen)
{
  CURLcode result = CURLE_OK;
  size_t chlglen = 0;
  unsigned char *chlg = NULL;
  OM_uint32 gss_status;
  OM_uint32 gss_major_status;
  OM_uint32 gss_minor_status;
  gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;

  (void) userp;
  (void) passwdp;

  if(krb5->context == GSS_C_NO_CONTEXT) {
    /* Generate our SPN */
    char *spn = Curl_sasl_build_gssapi_spn(service,
                                           data->easy_conn->host.name);
    if(!spn)
      return CURLE_OUT_OF_MEMORY;

    /* Populate the SPN structure */
    spn_token.value = spn;
    spn_token.length = strlen(spn);

    /* Import the SPN */
    gss_major_status = gss_import_name(&gss_minor_status, &spn_token,
                                       gss_nt_service_name, &krb5->spn);
    if(GSS_ERROR(gss_major_status)) {
      Curl_gss_log_error(data, gss_minor_status, "gss_import_name() failed: ");

      return CURLE_OUT_OF_MEMORY;
    }
  }
  else {
    /* Decode the base-64 encoded challenge message */
    if(strlen(chlg64) && *chlg64 != '=') {
      result = Curl_base64_decode(chlg64, &chlg, &chlglen);
      if(result)
        return result;
    }

    /* Ensure we have a valid challenge message */
    if(!chlg) {
      infof(data, "GSSAPI handshake failure (empty challenge message)\n");

      return CURLE_BAD_CONTENT_ENCODING;
    }

    /* Setup the challenge "input" security buffer */
    input_token.value = chlg;
    input_token.length = chlglen;
  }

  gss_major_status = Curl_gss_init_sec_context(data,
                                               &gss_minor_status,
                                               &krb5->context,
                                               krb5->spn,
                                               &Curl_krb5_mech_oid,
                                               GSS_C_NO_CHANNEL_BINDINGS,
                                               &input_token,
                                               &output_token,
                                               mutual_auth,
                                               NULL);

  Curl_safefree(input_token.value);

  if(GSS_ERROR(gss_major_status)) {
    if(output_token.value)
      gss_release_buffer(&gss_status, &output_token);

    Curl_gss_log_error(data, gss_minor_status,
                       "gss_init_sec_context() failed: ");

    return CURLE_RECV_ERROR;
  }

  if(output_token.value && output_token.length) {
    /* Base64 encode the response */
    result = Curl_base64_encode(data, (char *) output_token.value,
                                output_token.length, outptr, outlen);

    gss_release_buffer(&gss_status, &output_token);
  }

  return result;
}
Exemple #18
0
int authenticate_gss_client_step(gss_client_state* state, const char* challenge)
{
    OM_uint32 maj_stat;
    OM_uint32 min_stat;
    OM_uint32 ret_flags; // Not used, but may be necessary for gss call.
    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
    int ret = AUTH_GSS_CONTINUE;
    
    // Always clear out the old response
    if (state->response != NULL)
    {
        free(state->response);
        state->response = NULL;
    }
    
    // If there is a challenge (data from the server) we need to give it to GSS
    if (challenge && *challenge)
    {
        size_t len;
        input_token.value = base64_decode(challenge, &len);
        input_token.length = len;
    }
    
    // Do GSSAPI step
    Py_BEGIN_ALLOW_THREADS
    maj_stat = gss_init_sec_context(&min_stat,
                                    NULL, //state->client_creds,
                                    &state->context,
                                    state->server_name,
                                    GSS_C_NO_OID,
                                    (OM_uint32)state->gss_flags,
                                    0,
                                    NULL, //GSS_C_NO_CHANNEL_BINDINGS,
                                    &input_token,
                                    NULL,
                                    &output_token,
                                    &ret_flags,
                                    NULL);
    Py_END_ALLOW_THREADS
    
    if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED))
    {
        set_gss_error(maj_stat, min_stat);
        ret = AUTH_GSS_ERROR;
        goto end;
    }
    
    ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
    // Grab the client response to send back to the server
    if (output_token.length)
    {
        state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
        maj_stat = gss_release_buffer(&min_stat, &output_token);
    }
    
    // Try to get the user name if we have completed all GSS operations
    if (ret == AUTH_GSS_COMPLETE)
    {
        gss_buffer_desc name_token;
        gss_name_t gssuser = GSS_C_NO_NAME;
        maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL,  NULL, NULL, NULL);
        if (GSS_ERROR(maj_stat))
        {
            set_gss_error(maj_stat, min_stat);
            ret = AUTH_GSS_ERROR;
            goto end;
        }
        
        name_token.length = 0;
        maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL);
        if (GSS_ERROR(maj_stat))
        {
            if (name_token.value)
                gss_release_buffer(&min_stat, &name_token);
            gss_release_name(&min_stat, &gssuser);
            
            set_gss_error(maj_stat, min_stat);
            ret = AUTH_GSS_ERROR;
            goto end;
        }
        else
        {
            state->username = (char *)malloc(name_token.length + 1);
            strncpy(state->username, (char*) name_token.value, name_token.length);
            state->username[name_token.length] = 0;
            gss_release_buffer(&min_stat, &name_token);
            gss_release_name(&min_stat, &gssuser);
        }
    }
end:
    if (output_token.value)
        gss_release_buffer(&min_stat, &output_token);
    if (input_token.value)
        free(input_token.value);
    return ret;
}
Exemple #19
0
/* this performs a SASL/gssapi bind
   we avoid using cyrus-sasl to make Samba more robust. cyrus-sasl
   is very dependent on correctly configured DNS whereas
   this routine is much less fragile
   see RFC2078 and RFC2222 for details
*/
static ADS_STATUS ads_sasl_gssapi_bind(ADS_STRUCT *ads)
{
	uint32 minor_status;
	gss_name_t serv_name;
	gss_buffer_desc input_name;
	gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
	gss_OID mech_type = GSS_C_NULL_OID;
	gss_buffer_desc output_token, input_token;
	uint32 ret_flags, conf_state;
	struct berval cred;
	struct berval *scred = NULL;
	int i=0;
	int gss_rc, rc;
	uint8 *p;
	uint32 max_msg_size = 0;
	char *sname = NULL;
	ADS_STATUS status;
	krb5_principal principal = NULL;
	krb5_context ctx = NULL;
	krb5_enctype enc_types[] = {
#ifdef ENCTYPE_ARCFOUR_HMAC
			ENCTYPE_ARCFOUR_HMAC,
#endif
			ENCTYPE_DES_CBC_MD5,
			ENCTYPE_NULL};
	gss_OID_desc nt_principal = 
	{10, CONST_DISCARD(char *, "\052\206\110\206\367\022\001\002\002\002")};

	/* we need to fetch a service ticket as the ldap user in the
	   servers realm, regardless of our realm */
	asprintf(&sname, "ldap/%s@%s", ads->config.ldap_server_name, ads->config.realm);

	initialize_krb5_error_table();
	status = ADS_ERROR_KRB5(krb5_init_context(&ctx));
	if (!ADS_ERR_OK(status)) {
		SAFE_FREE(sname);
		return status;
	}
	status = ADS_ERROR_KRB5(krb5_set_default_tgs_ktypes(ctx, enc_types));
	if (!ADS_ERR_OK(status)) {
		SAFE_FREE(sname);
		krb5_free_context(ctx);	
		return status;
	}
	status = ADS_ERROR_KRB5(smb_krb5_parse_name(ctx, sname, &principal));
	if (!ADS_ERR_OK(status)) {
		SAFE_FREE(sname);
		krb5_free_context(ctx);	
		return status;
	}

	input_name.value = &principal;
	input_name.length = sizeof(principal);

	gss_rc = gss_import_name(&minor_status, &input_name, &nt_principal, &serv_name);

	/*
	 * The MIT libraries have a *HORRIBLE* bug - input_value.value needs
	 * to point to the *address* of the krb5_principal, and the gss libraries
	 * to a shallow copy of the krb5_principal pointer - so we need to keep
	 * the krb5_principal around until we do the gss_release_name. MIT *SUCKS* !
	 * Just one more way in which MIT engineers screwed me over.... JRA.
	 */

	SAFE_FREE(sname);

	if (gss_rc) {
		krb5_free_principal(ctx, principal);
		krb5_free_context(ctx);	
		return ADS_ERROR_GSS(gss_rc, minor_status);
	}

	input_token.value = NULL;
	input_token.length = 0;

	for (i=0; i < MAX_GSS_PASSES; i++) {
		gss_rc = gss_init_sec_context(&minor_status,
					  GSS_C_NO_CREDENTIAL,
					  &context_handle,
					  serv_name,
					  mech_type,
					  GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
					  0,
					  NULL,
					  &input_token,
					  NULL,
					  &output_token,
					  &ret_flags,
					  NULL);

		if (input_token.value) {
			gss_release_buffer(&minor_status, &input_token);
		}

		if (gss_rc && gss_rc != GSS_S_CONTINUE_NEEDED) {
			status = ADS_ERROR_GSS(gss_rc, minor_status);
			goto failed;
		}

		cred.bv_val = (char *)output_token.value;
		cred.bv_len = output_token.length;

		rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
				      &scred);
		if (rc != LDAP_SASL_BIND_IN_PROGRESS) {
			status = ADS_ERROR(rc);
			goto failed;
		}

		if (output_token.value) {
			gss_release_buffer(&minor_status, &output_token);
		}

		if (scred) {
			input_token.value = scred->bv_val;
			input_token.length = scred->bv_len;
		} else {
			input_token.value = NULL;
			input_token.length = 0;
		}

		if (gss_rc == 0) break;
	}

	gss_rc = gss_unwrap(&minor_status,context_handle,&input_token,&output_token,
			    (int *)&conf_state,NULL);
	if (gss_rc) {
		status = ADS_ERROR_GSS(gss_rc, minor_status);
		goto failed;
	}

	gss_release_buffer(&minor_status, &input_token);

	p = (uint8 *)output_token.value;

#if 0
	file_save("sasl_gssapi.dat", output_token.value, output_token.length);
#endif

	if (p) {
		max_msg_size = (p[1]<<16) | (p[2]<<8) | p[3];
	}

	gss_release_buffer(&minor_status, &output_token);

	output_token.value = SMB_MALLOC(strlen(ads->config.bind_path) + 8);
	p = (uint8 *)output_token.value;

	*p++ = 1; /* no sign & seal selection */
	/* choose the same size as the server gave us */
	*p++ = max_msg_size>>16;
	*p++ = max_msg_size>>8;
	*p++ = max_msg_size;
	snprintf((char *)p, strlen(ads->config.bind_path)+4, "dn:%s", ads->config.bind_path);
	p += strlen((const char *)p);

	output_token.length = PTR_DIFF(p, output_token.value);

	gss_rc = gss_wrap(&minor_status, context_handle,0,GSS_C_QOP_DEFAULT,
			  &output_token, (int *)&conf_state,
			  &input_token);
	if (gss_rc) {
		status = ADS_ERROR_GSS(gss_rc, minor_status);
		goto failed;
	}

	free(output_token.value);

	cred.bv_val = (char *)input_token.value;
	cred.bv_len = input_token.length;

	rc = ldap_sasl_bind_s(ads->ld, NULL, "GSSAPI", &cred, NULL, NULL, 
			      &scred);
	status = ADS_ERROR(rc);

	gss_release_buffer(&minor_status, &input_token);

failed:

	gss_release_name(&minor_status, &serv_name);
	if (context_handle != GSS_C_NO_CONTEXT)
		gss_delete_sec_context(&minor_status, &context_handle, GSS_C_NO_BUFFER);
	krb5_free_principal(ctx, principal);
	krb5_free_context(ctx);	

	if(scred)
		ber_bvfree(scred);
	return status;
}
Exemple #20
0
int authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user, int protect)
{
	OM_uint32 maj_stat;
	OM_uint32 min_stat;
	gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
	gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
	int ret = AUTH_GSS_CONTINUE;
	char buf[4096];
#ifdef PRINTFS
    char server_conf_flags;
#endif
	unsigned long buf_size;
    
	// Always clear out the old response
	if (state->response != NULL)
	{
		free(state->response);
		state->response = NULL;
	}
    
	if (challenge && *challenge)
	{
		size_t len;
		input_token.value = base64_decode(challenge, &len);
		input_token.length = len;
	}
    
	if (user) {
#ifdef PRINTFS
		// get bufsize
		server_conf_flags = ((char*) input_token.value)[0];
#endif
		((char*) input_token.value)[0] = 0;
		buf_size = ntohl(*((long *) input_token.value));
		free(input_token.value);
#ifdef PRINTFS
		printf("User: %s, %c%c%c\n", user,
               server_conf_flags & GSS_AUTH_P_NONE      ? 'N' : '-',
               server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
               server_conf_flags & GSS_AUTH_P_PRIVACY   ? 'P' : '-');
		printf("Maximum GSS token size is %ld\n", buf_size);
#endif
        
		// agree to terms (hack!)
		buf_size = htonl(buf_size); // not relevant without integrity/privacy
		memcpy(buf, &buf_size, 4);
		buf[0] = GSS_AUTH_P_NONE;
		// server decides if principal can log in as user
		strncpy(buf + 4, user, sizeof(buf) - 4);
		input_token.value = buf;
		input_token.length = 4 + strlen(user);
	}
    
	// Do GSSAPI wrap
	maj_stat = gss_wrap(&min_stat,
						state->context,
						protect,
						GSS_C_QOP_DEFAULT,
						&input_token,
						NULL,
						&output_token);
    
	if (maj_stat != GSS_S_COMPLETE)
	{
		set_gss_error(maj_stat, min_stat);
		ret = AUTH_GSS_ERROR;
		goto end;
	}
	else
		ret = AUTH_GSS_COMPLETE;
	// Grab the client response to send back to the server
	if (output_token.length)
	{
		state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
		maj_stat = gss_release_buffer(&min_stat, &output_token);
	}
end:
	if (output_token.value)
		gss_release_buffer(&min_stat, &output_token);

    if (input_token.value)
        gss_release_buffer(&min_stat, &input_token);

	return ret;
}
enum auth_stat gssrpc__svcauth_gssapi(
     register struct svc_req *rqst,
     register struct rpc_msg *msg,
     bool_t *no_dispatch)
{
     XDR xdrs;
     auth_gssapi_creds creds;
     auth_gssapi_init_arg call_arg;
     auth_gssapi_init_res call_res;
     gss_buffer_desc output_token, in_buf, out_buf;
     gss_cred_id_t server_creds;
     struct gss_channel_bindings_struct bindings, *bindp;
     OM_uint32 gssstat, minor_stat, time_rec;
     struct opaque_auth *cred, *verf;
     svc_auth_gssapi_data *client_data;
     int i;
     enum auth_stat ret;
     OM_uint32 ret_flags;
     uint32_t seq_num;

     PRINTF(("svcauth_gssapi: starting\n"));
     
     /* clean up expired entries */
     clean_client();

     /* use AUTH_NONE until there is a client_handle */
     rqst->rq_xprt->xp_auth = &svc_auth_none;
     
     memset((char *) &call_res, 0, sizeof(call_res));
     creds.client_handle.length = 0;
     creds.client_handle.value = NULL;
     
     cred = &msg->rm_call.cb_cred;
     verf = &msg->rm_call.cb_verf;
     
     if (cred->oa_length == 0) {
	  PRINTF(("svcauth_gssapi: empty creds, failing\n"));
	  LOG_MISCERR("empty client credentials");
	  ret = AUTH_BADCRED;
	  goto error;
     }

     PRINTF(("svcauth_gssapi: decoding credentials\n"));
     xdrmem_create(&xdrs, cred->oa_base, cred->oa_length, XDR_DECODE); 
     memset((char *) &creds, 0, sizeof(creds));
     if (! xdr_authgssapi_creds(&xdrs, &creds)) {
	  PRINTF(("svcauth_gssapi: failed decoding creds\n"));
	  LOG_MISCERR("protocol error in client credentials");
	  xdr_free(xdr_authgssapi_creds, &creds);
	  XDR_DESTROY(&xdrs);
	  ret = AUTH_BADCRED;
	  goto error;
     }
     XDR_DESTROY(&xdrs);

     PRINTF(("svcauth_gssapi: got credentials, version %d, client_handle len %d\n",
	     creds.version, (int) creds.client_handle.length));

     if (creds.version != 2) {
 	  PRINTF(("svcauth_gssapi: bad credential version\n"));
 	  LOG_MISCERR("unsupported client credentials version");
 	  ret = AUTH_BADCRED;
 	  goto error;
     }

#ifdef DEBUG_GSSAPI
     if (svc_debug_gssapi) {
	  if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_EXIT) {
	       PRINTF(("svcauth_gssapi: GSSAPI_EXIT, cleaning up\n"));
	       svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
	       xdr_free(xdr_authgssapi_creds, &creds);
	       cleanup();
	       exit(0);
	  }
     }
#endif
	  
     /*
      * If this is an auth_msg and proc is GSSAPI_INIT, then create a
      * client handle for this client.  Otherwise, look up the
      * existing handle.
      */
     if (creds.auth_msg && rqst->rq_proc == AUTH_GSSAPI_INIT) {
	  if (creds.client_handle.length != 0) {
	       PRINTF(("svcauth_gssapi: non-empty handle on GSSAPI_INIT\n"));
	       LOG_MISCERR("protocol error in client handle");
	       ret = AUTH_FAILED;
	       goto error;
	  }
	       
	  PRINTF(("svcauth_gssapi: GSSAPI_INIT, creating client.\n"));
	  
	  client_data = create_client();
	  if (client_data == NULL) {
	       PRINTF(("svcauth_gssapi: create_client failed\n"));
	       LOG_MISCERR("internal error creating client record");
	       ret = AUTH_FAILED;
	       goto error;
	  }
     } else {
	  if (creds.client_handle.length == 0) {
	       PRINTF(("svcauth_gssapi: expected non-empty creds\n"));
	       LOG_MISCERR("protocol error in client credentials");
	       ret = AUTH_FAILED;
	       goto error;
	  }
	  
	  PRINTF(("svcauth_gssapi: incoming client_handle %d, len %d\n", 
		  *((uint32_t *) creds.client_handle.value),
		  (int) creds.client_handle.length));

	  client_data = get_client(&creds.client_handle);
	  if (client_data == NULL) {
	       PRINTF(("svcauth_gssapi: client_handle lookup failed\n"));
	       LOG_MISCERR("invalid client handle received");
	       ret = AUTH_BADCRED;
	       goto error;
	  }
	  PRINTF(("svcauth_gssapi: client_handle lookup succeeded\n"));
     }

     /* any response we send will use client_handle, so set it now */
     call_res.client_handle.length = sizeof(client_data->key);
     call_res.client_handle.value = (char *) &client_data->key;
     
     /* mark this call as using AUTH_GSSAPI via client_data's SVCAUTH */
     rqst->rq_xprt->xp_auth = &client_data->svcauth;

     if (client_data->established == FALSE) {
	  PRINTF(("svcauth_gssapi: context is not established\n"));

	  if (creds.auth_msg == FALSE) {
	       PRINTF(("svcauth_gssapi: expected auth_msg TRUE\n"));
	       LOG_MISCERR("protocol error on incomplete connection");
	       ret = AUTH_REJECTEDCRED;
	       goto error;
	  }

	  /*
	   * If the context is not established, then only GSSAPI_INIT
	   * and _CONTINUE requests are valid.
	   */
	  if (rqst->rq_proc != AUTH_GSSAPI_INIT && rqst->rq_proc !=
	      AUTH_GSSAPI_CONTINUE_INIT) {
	       PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
		       rqst->rq_proc));
	       LOG_MISCERR("protocol error on incomplete connection");
	       ret = AUTH_FAILED;
	       goto error;
	  }

	  /* call is for us, deserialize arguments */
	  memset(&call_arg, 0, sizeof(call_arg));
	  if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg,
			    &call_arg)) {
	       PRINTF(("svcauth_gssapi: cannot decode args\n"));
	       LOG_MISCERR("protocol error in procedure arguments");
	       ret = AUTH_BADCRED;
	       goto error;
	  }

	  /*
	   * Process the call arg version number.
	   * 
	   * Set the krb5_gss backwards-compatibility mode based on client
	   * version.  This controls whether the AP_REP message is
	   * encrypted with the session key (version 2+, correct) or the
	   * session subkey (version 1, incorrect).  This function can
	   * never fail, so we don't bother checking its return value.
	   */
	  switch (call_arg.version) {
	  case 1:
	  case 2:
	       LOG_MISCERR("Warning: Accepted old RPC protocol request");
	       call_res.version = 1;
	       break;
	  case 3:
	  case 4:
	       /* 3 and 4 are essentially the same, don't bother warning */
	       call_res.version = call_arg.version;
	       break;
	  default:
	       PRINTF(("svcauth_gssapi: bad GSSAPI_INIT version\n"));
	       LOG_MISCERR("unsupported GSSAPI_INIT version");
	       ret = AUTH_BADCRED;
	       goto error;
	  }

#ifdef GSS_BACKWARD_HACK
	  krb5_gss_set_backward_mode(&minor_stat, call_arg.version == 1);
#endif

	  if (call_arg.version >= 3) {
	       memset(&bindings, 0, sizeof(bindings));
	       bindings.application_data.length = 0;
	       bindings.initiator_addrtype = GSS_C_AF_INET;
	       bindings.initiator_address.length = 4;
	       bindings.initiator_address.value =
		    &svc_getcaller(rqst->rq_xprt)->sin_addr.s_addr;

	       if (rqst->rq_xprt->xp_laddrlen > 0) {
		    bindings.acceptor_addrtype = GSS_C_AF_INET;
		    bindings.acceptor_address.length = 4;
		    bindings.acceptor_address.value =
			 &rqst->rq_xprt->xp_laddr.sin_addr.s_addr;
	       } else {
		    LOG_MISCERR("cannot get local address");
		    ret = AUTH_FAILED;
		    goto error;
	       }


	       bindp = &bindings;
	  } else {
	       bindp = GSS_C_NO_CHANNEL_BINDINGS;
	  }

	  /*
	   * If the client's server_creds is already set, use it.
	   * Otherwise, try each credential in server_creds_list until
	   * one of them succeedes, then set the client server_creds
	   * to that.  If all fail, the client's server_creds isn't
	   * set (which is fine, because the client will be gc'ed
	   * anyway).
	   *
	   * If accept_sec_context returns something other than
	   * success and GSS_S_FAILURE, then assume different
	   * credentials won't help and stop looping.
	   * 
	   * Note that there are really two cases here: (1) the client
	   * has a server_creds already, and (2) it does not.  They
	   * are both written in the same loop so that there is only
	   * one textual call to gss_accept_sec_context; in fact, in
	   * case (1), the loop is executed exactly once.
	   */
	  for (i = 0; i < server_creds_count; i++) {
	       if (client_data->server_creds != NULL) {
		    PRINTF(("svcauth_gssapi: using's clients server_creds\n"));
		    server_creds = client_data->server_creds;
	       } else {
		    PRINTF(("svcauth_gssapi: trying creds %d\n", i));
		    server_creds = server_creds_list[i];
	       }
	       
	       /* Free previous output_token from loop */
	       if(i != 0) gss_release_buffer(&minor_stat, &output_token);

	       call_res.gss_major =
		    gss_accept_sec_context(&call_res.gss_minor,
					   &client_data->context,
					   server_creds,
					   &call_arg.token,
					   bindp,
					   &client_data->client_name,
					   NULL,
					   &output_token,
					   &ret_flags,
					   &time_rec,
					   NULL);

	       if (server_creds == client_data->server_creds)
		    break;

	       PRINTF(("accept_sec_context returned 0x%x 0x%x wrong-princ=%#x\n",
		       call_res.gss_major, call_res.gss_minor, (int) KRB5KRB_AP_WRONG_PRINC));
	       if (call_res.gss_major == GSS_S_COMPLETE ||
		   call_res.gss_major == GSS_S_CONTINUE_NEEDED) {
		    /* server_creds was right, set it! */
		    PRINTF(("svcauth_gssapi: creds are correct, storing\n"));
		    client_data->server_creds = server_creds;
		    client_data->server_name = server_name_list[i];
		    break;
	       } else if (call_res.gss_major != GSS_S_FAILURE
#ifdef GSSAPI_KRB5
			  /*
			   * hard-coded because there is no other way
			   * to prevent all GSS_S_FAILURES from
			   * returning a "wrong principal in request"
			   * error
			   */
			  || ((krb5_error_code) call_res.gss_minor !=
			      (krb5_error_code) KRB5KRB_AP_WRONG_PRINC)
#endif
			  ) {
		    break;
	       }
	  }
	  
	  gssstat = call_res.gss_major;
	  minor_stat = call_res.gss_minor;

	  /* done with call args */
	  xdr_free(xdr_authgssapi_init_arg, &call_arg);

	  PRINTF(("svcauth_gssapi: accept_sec_context returned %#x %#x\n",
		  call_res.gss_major, call_res.gss_minor));
	  if (call_res.gss_major != GSS_S_COMPLETE &&
	      call_res.gss_major != GSS_S_CONTINUE_NEEDED) {
	       AUTH_GSSAPI_DISPLAY_STATUS(("accepting context",
					   call_res.gss_major,
					   call_res.gss_minor));

	       if (log_badauth != NULL)
		    (*log_badauth)(call_res.gss_major,
				   call_res.gss_minor,
				   &rqst->rq_xprt->xp_raddr,
				   log_badauth_data);
	       
	       gss_release_buffer(&minor_stat, &output_token);
	       svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res,
			     (caddr_t) &call_res);
	       *no_dispatch = TRUE;
	       ret = AUTH_OK;
	       goto error;
	  }
	      
	  if (output_token.length != 0) {
	       PRINTF(("svcauth_gssapi: got new output token\n"));
	       GSS_COPY_BUFFER(call_res.token, output_token);
	  }

	  if (gssstat == GSS_S_COMPLETE) {
	       client_data->seq_num = rand();
	       client_expire(client_data,
			     (time_rec == GSS_C_INDEFINITE ?
			      INDEF_EXPIRE : time_rec) + time(0));

	       PRINTF(("svcauth_gssapi: context established, isn %d\n", 
		       client_data->seq_num));

	       if (auth_gssapi_seal_seq(client_data->context,
					client_data->seq_num,
					&call_res.signed_isn) ==
		   FALSE) {
		    ret = AUTH_FAILED;
		    LOG_MISCERR("internal error sealing sequence number");
		    gss_release_buffer(&minor_stat, &output_token);
		    goto error;
	       }
	  }

	  PRINTF(("svcauth_gssapi: sending reply\n"));
	  svc_sendreply(rqst->rq_xprt, xdr_authgssapi_init_res,
			(caddr_t) &call_res);
	  *no_dispatch = TRUE;

	  /*
	   * If appropriate, set established to TRUE *after* sending
	   * response (otherwise, the client will receive the final
	   * token encrypted)
	   */
	  if (gssstat == GSS_S_COMPLETE) {
	       gss_release_buffer(&minor_stat, &call_res.signed_isn);
	       client_data->established = TRUE;
	  }
	  gss_release_buffer(&minor_stat, &output_token);
     } else {
	  PRINTF(("svcauth_gssapi: context is established\n"));

	  /* check the verifier */
	  PRINTF(("svcauth_gssapi: checking verifier, len %d\n",
		  verf->oa_length));
	  
	  in_buf.length = verf->oa_length;
	  in_buf.value = verf->oa_base;
	  
	  if (auth_gssapi_unseal_seq(client_data->context, &in_buf,
				     &seq_num) == FALSE) {
	       ret = AUTH_BADVERF;
	       LOG_MISCERR("internal error unsealing sequence number");
	       goto error;
	  }
	  
	  if (seq_num != client_data->seq_num + 1) {
	       PRINTF(("svcauth_gssapi: expected isn %d, got %d\n",
		       client_data->seq_num + 1, seq_num));
	       if (log_badverf != NULL)
		    (*log_badverf)(client_data->client_name,
				   client_data->server_name,
				   rqst, msg, log_badverf_data);
	       
	       ret = AUTH_REJECTEDVERF;
	       goto error;
	  }
	  client_data->seq_num++;
	  
	  PRINTF(("svcauth_gssapi: seq_num %d okay\n", seq_num));

	  /* free previous response verifier, if any */
	  if (client_data->prev_verf.length != 0) {
	       gss_release_buffer(&minor_stat, &client_data->prev_verf);
	       client_data->prev_verf.length = 0;
	  }
	  
	  /* prepare response verifier */
	  seq_num = client_data->seq_num + 1;
	  if (auth_gssapi_seal_seq(client_data->context, seq_num,
				   &out_buf) == FALSE) {
	       ret = AUTH_FAILED;
	       LOG_MISCERR("internal error sealing sequence number");
	       goto error;
	  }
	  
	  client_data->seq_num++;
	  
	  PRINTF(("svcauth_gssapi; response seq_num %d\n", seq_num));
	  
	  rqst->rq_xprt->xp_verf.oa_flavor = AUTH_GSSAPI;
	  rqst->rq_xprt->xp_verf.oa_base = out_buf.value; 
	  rqst->rq_xprt->xp_verf.oa_length = out_buf.length;

	  /* save verifier so it can be freed next time */
	  client_data->prev_verf.value = out_buf.value; 
	  client_data->prev_verf.length = out_buf.length;

	  /*
	   * Message is authentic.  If auth_msg if true, process the
	   * call; otherwise, return AUTH_OK so it will be dispatched
	   * to the application server.
	   */

	  if (creds.auth_msg == TRUE) {
	       /*
		* If process_token fails, then the token probably came
		* from an attacker.  No response (error or otherwise)
		* should be returned to the client, since it won't be
		* accepting one.
		*/

	       switch (rqst->rq_proc) {
	       case AUTH_GSSAPI_MSG:
		    PRINTF(("svcauth_gssapi: GSSAPI_MSG, getting args\n"));
		    memset(&call_arg, 0, sizeof(call_arg));
		    if (! svc_getargs(rqst->rq_xprt, xdr_authgssapi_init_arg,
				      &call_arg)) {
			 PRINTF(("svcauth_gssapi: cannot decode args\n"));
			 LOG_MISCERR("protocol error in call arguments");
			 xdr_free(xdr_authgssapi_init_arg, &call_arg);
			 ret = AUTH_BADCRED;
			 goto error;
		    }

		    PRINTF(("svcauth_gssapi: processing token\n"));
		    gssstat = gss_process_context_token(&minor_stat,
							client_data->context,
							&call_arg.token);

		    /* done with call args */
		    xdr_free(xdr_authgssapi_init_arg, &call_arg);
		    
		    if (gssstat != GSS_S_COMPLETE) {
			 AUTH_GSSAPI_DISPLAY_STATUS(("processing token",
						     gssstat, minor_stat));
			 ret = AUTH_FAILED;
			 goto error;
		    }

		    svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
		    *no_dispatch = TRUE;
		    break;

	       case AUTH_GSSAPI_DESTROY:
		    PRINTF(("svcauth_gssapi: GSSAPI_DESTROY\n"));
		    
		    PRINTF(("svcauth_gssapi: sending reply\n"));
		    svc_sendreply(rqst->rq_xprt, xdr_void, NULL);
		    *no_dispatch = TRUE;

		    destroy_client(client_data);
		    rqst->rq_xprt->xp_auth = NULL;
		    break;

	       default:
		    PRINTF(("svcauth_gssapi: unacceptable procedure %d\n",
			    rqst->rq_proc));
		    LOG_MISCERR("invalid call procedure number");
		    ret = AUTH_FAILED;
		    goto error;
	       }
	  } else {
	       /* set credentials for app server; comment in svc.c */
	       /* seems to imply this is incorrect, but I don't see */
	       /* any problem with it... */
	       rqst->rq_clntcred = (char *)client_data->client_name;
	       rqst->rq_svccred = (char *)client_data->context;
	  }
     }

     if (creds.client_handle.length != 0) {
	  PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
		  (int) creds.client_handle.length));
	  xdr_free(xdr_authgssapi_creds, &creds);
     }
     
     PRINTF(("\n"));
     return AUTH_OK;

error:
     if (creds.client_handle.length != 0) {
	  PRINTF(("svcauth_gssapi: freeing client_handle len %d\n",
		  (int) creds.client_handle.length));
	  xdr_free(xdr_authgssapi_creds, &creds);
     }
     
     PRINTF(("\n"));
     return ret;
}
Exemple #22
0
int authenticate_gss_server_step(gss_server_state *state, const char *challenge)
{
    OM_uint32 maj_stat;
    OM_uint32 min_stat;
    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
    int ret = AUTH_GSS_CONTINUE;
    
    // Always clear out the old response
    if (state->response != NULL)
    {
        free(state->response);
        state->response = NULL;
    }
    
    // If there is a challenge (data from the server) we need to give it to GSS
    if (challenge && *challenge)
    {
        size_t len;
        input_token.value = base64_decode(challenge, &len);
        input_token.length = len;
    }
    else
    {
        PyErr_SetString(KrbException_class, "No challenge parameter in request from client");
        ret = AUTH_GSS_ERROR;
        goto end;
    }
    
    Py_BEGIN_ALLOW_THREADS
    maj_stat = gss_accept_sec_context(&min_stat,
                                      &state->context,
                                      state->server_creds,
                                      &input_token,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &state->client_name,
                                      NULL,
                                      &output_token,
                                      (OM_uint32 *)&state->gss_flags,
                                      NULL,
                                      &state->client_creds);
    Py_END_ALLOW_THREADS
    
    if (GSS_ERROR(maj_stat))
    {
        set_gss_error(maj_stat, min_stat);
        ret = AUTH_GSS_ERROR;
        goto end;
    }
    
    // Grab the server response to send back to the client
    if (output_token.length)
    {
        state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
        maj_stat = gss_release_buffer(&min_stat, &output_token);
    }
    
    // Get the user name
    maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL);
    if (GSS_ERROR(maj_stat))
    {
        set_gss_error(maj_stat, min_stat);
        ret = AUTH_GSS_ERROR;
        goto end;
    }
    state->username = (char *)malloc(output_token.length + 1);
    strncpy(state->username, (char*) output_token.value, output_token.length);
    state->username[output_token.length] = 0;
    
    // Get the target name if no server creds were supplied
    if (state->server_creds == GSS_C_NO_CREDENTIAL)
    {
        gss_name_t target_name = GSS_C_NO_NAME;
        maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL);
        if (GSS_ERROR(maj_stat))
        {
            set_gss_error(maj_stat, min_stat);
            ret = AUTH_GSS_ERROR;
            goto end;
        }
        maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL);
        if (GSS_ERROR(maj_stat))
        {
            set_gss_error(maj_stat, min_stat);
            ret = AUTH_GSS_ERROR;
            goto end;
        }
        state->targetname = (char *)malloc(output_token.length + 1);
        strncpy(state->targetname, (char*) output_token.value, output_token.length);
        state->targetname[output_token.length] = 0;
    }

    ret = AUTH_GSS_COMPLETE;
    
end:
    if (output_token.length)
        gss_release_buffer(&min_stat, &output_token);
    if (input_token.value)
        free(input_token.value);
    return ret;
}
Exemple #23
0
/* returning zero (0) means success, everything else is treated as "failure"
   with no care exactly what the failure was */
int Curl_input_negotiate(struct connectdata *conn, bool proxy,
                         const char *header)
{
  struct SessionHandle *data = conn->data;
  struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg:
    &data->state.negotiate;
  OM_uint32 major_status, minor_status, discard_st, min_stat;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  int ret;
  size_t len;
  size_t rawlen = 0;
  bool gss;
  const char* protocol;
  CURLcode error;

  while(*header && ISSPACE(*header))
    header++;
  if(checkprefix("GSS-Negotiate", header)) {
    protocol = "GSS-Negotiate";
    gss = TRUE;
  }
  else if(checkprefix("Negotiate", header)) {
    protocol = "Negotiate";
    gss = FALSE;
  }
  else
    return -1;

  if(neg_ctx->context) {
    if(neg_ctx->gss != gss) {
      return -1;
    }
  }
  else {
    neg_ctx->protocol = protocol;
    neg_ctx->gss = gss;
  }

  if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
    /* We finished successfully our part of authentication, but server
     * rejected it (since we're again here). Exit with an error since we
     * can't invent anything better */
    Curl_cleanup_negotiate(data);
    return -1;
  }

	if(neg_ctx->server_name == NULL &&
	  (ret = get_gss_name(conn, proxy, &neg_ctx->server_name)))
	return ret;
  	
  header += strlen(neg_ctx->protocol);
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if(len > 0) {
    error = Curl_base64_decode(header,
                               (unsigned char **)&input_token.value, &rawlen);
    if(error || rawlen == 0)
      return -1;
    input_token.length = rawlen;

    DEBUGASSERT(input_token.value != NULL);

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
    if(checkprefix("Negotiate", header)) {
      unsigned char  *spnegoToken       = NULL;
      size_t          spnegoTokenLength = 0;
      gss_buffer_desc mechToken         = GSS_C_EMPTY_BUFFER;

      spnegoToken = malloc(input_token.length);
      if(spnegoToken == NULL) {
        Curl_safefree(input_token.value);
        return CURLE_OUT_OF_MEMORY;
      }
      memcpy(spnegoToken, input_token.value, input_token.length);
      spnegoTokenLength = input_token.length;

      if(!parseSpnegoTargetToken(spnegoToken,
                                 spnegoTokenLength,
                                 NULL,
                                 NULL,
                                 (unsigned char**)&mechToken.value,
                                 &mechToken.length,
                                 NULL,
                                 NULL)) {
        Curl_safefree(spnegoToken);
        infof(data, "Parse SPNEGO Target Token failed\n");
      }
      else if(!mechToken.value || !mechToken.length) {
        Curl_safefree(spnegoToken);
        if(mechToken.value)
          gss_release_buffer(&discard_st, &mechToken);
        infof(data, "Parse SPNEGO Target Token succeeded (NULL token)\n");
      }
      else {
        Curl_safefree(spnegoToken);
        Curl_safefree(input_token.value);
        input_token.value = malloc(mechToken.length);
        if(input_token.value == NULL) {
          gss_release_buffer(&discard_st, &mechToken);
          return CURLE_OUT_OF_MEMORY;
        }
        memcpy(input_token.value, mechToken.value, mechToken.length);
        input_token.length = mechToken.length;
        gss_release_buffer(&discard_st, &mechToken);
        infof(data, "Parse SPNEGO Target Token succeeded\n");
      }
    }
#endif
  }

  major_status = Curl_gss_init_sec_context(conn,
                                           &minor_status,
                                           &neg_ctx->context,
                                           neg_ctx->server_name,
                                           GSS_C_NO_CHANNEL_BINDINGS,
                                           &input_token,
                                           &output_token,
                                           NULL);
  gss_release_cred(&min_stat, &conn->data->curl_gss_creds);
  Curl_safefree(input_token.value);
  /*To remove the memory leak issue*/
  if(neg_ctx->server_name != GSS_C_NO_NAME)
    gss_release_name(&min_stat, &neg_ctx->server_name);

  neg_ctx->status = major_status;
  if(GSS_ERROR(major_status)) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    log_gss_error(conn, minor_status, "KRB5_ERROR: gss_init_sec_context() failed: ");
	printf("\n KRB5_ERROR: gss_init_sec_context() failed error code : %d", minor_status);
    return -1;
  }

  if(!output_token.value || !output_token.length) {
    if(output_token.value)
      gss_release_buffer(&discard_st, &output_token);
    return -1;
  }

  neg_ctx->output_token = output_token;
  return 0;
}
Exemple #24
0
bool_t
xdr_rpc_gss_wrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
		      gss_ctx_id_t ctx, gss_qop_t qop,
		      rpc_gss_service_t svc, u_int seq)
{
	gss_buffer_desc	databuf, wrapbuf;
	OM_uint32	maj_stat, min_stat;
	int		start, end, conf_state;
	u_int		len;
	bool_t		xdr_stat;

	/* Skip databody length. */
	start = XDR_GETPOS(xdrs);
	XDR_SETPOS(xdrs, start + 4);
	
	/* Marshal rpc_gss_data_t (sequence number + arguments). */
	if (!xdr_u_int(xdrs, &seq) || !xdr_func(xdrs, xdr_ptr))
		return (FALSE);
	end = XDR_GETPOS(xdrs);

	/* Set databuf to marshalled rpc_gss_data_t. */
	databuf.length = end - start - 4;
	XDR_SETPOS(xdrs, start + 4);
	databuf.value = XDR_INLINE(xdrs, databuf.length);

	xdr_stat = FALSE;
	
	if (svc == rpc_gss_svc_integrity) {
		/* Marshal databody_integ length. */
		XDR_SETPOS(xdrs, start);
		len = databuf.length;
		if (!xdr_u_int(xdrs, &len))
			return (FALSE);
		
		/* Checksum rpc_gss_data_t. */
		maj_stat = gss_get_mic(&min_stat, ctx, qop,
				       &databuf, &wrapbuf);
		if (maj_stat != GSS_S_COMPLETE) {
			log_debug("gss_get_mic failed");
			return (FALSE);
		}
		/* Marshal checksum. */
		XDR_SETPOS(xdrs, end);
		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
		gss_release_buffer(&min_stat, &wrapbuf);
	}		
	else if (svc == rpc_gss_svc_privacy) {
		/* Encrypt rpc_gss_data_t. */
		maj_stat = gss_wrap(&min_stat, ctx, TRUE, qop, &databuf,
				    &conf_state, &wrapbuf);
		if (maj_stat != GSS_S_COMPLETE) {
			log_status("gss_wrap", NULL, maj_stat, min_stat);
			return (FALSE);
		}
		/* Marshal databody_priv. */
		XDR_SETPOS(xdrs, start);
		xdr_stat = xdr_gss_buffer_desc(xdrs, &wrapbuf);
		gss_release_buffer(&min_stat, &wrapbuf);
	}
	return (xdr_stat);
}
Exemple #25
0
static int
get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
{
	u_int32_t	maj_stat, min_stat;
	gss_buffer_desc	name;
	char		*sname;
	int		res = -1;
	uid_t		uid, gid;
	gss_OID		name_type = GSS_C_NO_OID;
	char		*secname;

	maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
	if (maj_stat != GSS_S_COMPLETE) {
		pgsserr("get_ids: gss_display_name",
			maj_stat, min_stat, mech);
		goto out;
	}
	if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
	    !(sname = calloc(name.length + 1, 1))) {
		printerr(0, "WARNING: get_ids: error allocating %d bytes "
			"for sname\n", name.length + 1);
		gss_release_buffer(&min_stat, &name);
		goto out;
	}
	memcpy(sname, name.value, name.length);
	printerr(1, "sname = %s\n", sname);
	gss_release_buffer(&min_stat, &name);

	res = -EINVAL;
	if ((secname = mech2file(mech)) == NULL) {
		printerr(0, "WARNING: get_ids: error mapping mech to "
			"file for name '%s'\n", sname);
		goto out_free;
	}
	nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
	res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
	if (res < 0) {
		/*
		 * -ENOENT means there was no mapping, any other error
		 * value means there was an error trying to do the
		 * mapping.
		 * If there was no mapping, we send down the value -1
		 * to indicate that the anonuid/anongid for the export
		 * should be used.
		 */
		if (res == -ENOENT) {
			cred->cr_uid = -1;
			cred->cr_gid = -1;
			cred->cr_ngroups = 0;
			res = 0;
			goto out_free;
		}
		printerr(1, "WARNING: get_ids: failed to map name '%s' "
			"to uid/gid: %s\n", sname, strerror(-res));
		goto out_free;
	}
	cred->cr_uid = uid;
	cred->cr_gid = gid;
	add_supplementary_groups(secname, sname, cred);
	res = 0;
out_free:
	free(sname);
out:
	return res;
}
Exemple #26
0
bool_t
xdr_rpc_gss_unwrap_data(XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr,
			gss_ctx_id_t ctx, gss_qop_t qop,
			rpc_gss_service_t svc, u_int seq)
{
	XDR		tmpxdrs;
	gss_buffer_desc	databuf, wrapbuf;
	OM_uint32	maj_stat, min_stat;
	u_int		seq_num, conf_state, qop_state;
	bool_t		xdr_stat;

	if (xdr_func == (xdrproc_t) xdr_void || xdr_ptr == NULL)
		return (TRUE);
	
	memset(&databuf, 0, sizeof(databuf));
	memset(&wrapbuf, 0, sizeof(wrapbuf));
	
	if (svc == rpc_gss_svc_integrity) {
		/* Decode databody_integ. */
		if (!xdr_gss_buffer_desc(xdrs, &databuf)) {
			log_debug("xdr decode databody_integ failed");
			return (FALSE);
		}
		/* Decode checksum. */
		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
			mem_free(databuf.value, databuf.length);
			log_debug("xdr decode checksum failed");
			return (FALSE);
		}
		/* Verify checksum and QOP. */
		maj_stat = gss_verify_mic(&min_stat, ctx, &databuf,
					  &wrapbuf, &qop_state);
		mem_free(wrapbuf.value, wrapbuf.length);
		
		if (maj_stat != GSS_S_COMPLETE || qop_state != qop) {
			mem_free(databuf.value, databuf.length);
			log_status("gss_verify_mic", NULL, maj_stat, min_stat);
			return (FALSE);
		}
	} else if (svc == rpc_gss_svc_privacy) {
		/* Decode databody_priv. */
		if (!xdr_gss_buffer_desc(xdrs, &wrapbuf)) {
			log_debug("xdr decode databody_priv failed");
			return (FALSE);
		}
		/* Decrypt databody. */
		maj_stat = gss_unwrap(&min_stat, ctx, &wrapbuf, &databuf,
				      &conf_state, &qop_state);
		
		mem_free(wrapbuf.value, wrapbuf.length);
		
		/* Verify encryption and QOP. */
		if (maj_stat != GSS_S_COMPLETE || qop_state != qop ||
			conf_state != TRUE) {
			gss_release_buffer(&min_stat, &databuf);
			log_status("gss_unwrap", NULL, maj_stat, min_stat);
			return (FALSE);
		}
	}
	/* Decode rpc_gss_data_t (sequence number + arguments). */
	xdrmem_create(&tmpxdrs, databuf.value, databuf.length, XDR_DECODE);
	xdr_stat = (xdr_u_int(&tmpxdrs, &seq_num) &&
	    xdr_func(&tmpxdrs, xdr_ptr));
	XDR_DESTROY(&tmpxdrs);

	/*
	 * Integrity service allocates databuf via XDR so free it the
	 * same way.
	 */
	if (svc == rpc_gss_svc_integrity) {
		xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &databuf);
	} else {
		gss_release_buffer(&min_stat, &databuf);
	}
	
	/* Verify sequence number. */
	if (xdr_stat == TRUE && seq_num != seq) {
		log_debug("wrong sequence number in databody");
		return (FALSE);
	}
	return (xdr_stat);
}
Exemple #27
0
int
main (int argc, char *argv[])
{
  gss_uint32 maj_stat, min_stat, ret_flags, time_rec;
  gss_buffer_desc bufdesc, bufdesc2;
  gss_name_t servername = GSS_C_NO_NAME, name;
  gss_ctx_id_t cctx = GSS_C_NO_CONTEXT;
  gss_ctx_id_t sctx = GSS_C_NO_CONTEXT;
  gss_cred_id_t server_creds;
  Shishi *handle;
  size_t i;
  struct gss_channel_bindings_struct cb;

  memset (&cb, 0, sizeof (cb));
  cb.application_data.length = 3;
  cb.application_data.value = (char *) "hej";

  do
    if (strcmp (argv[argc - 1], "-v") == 0 ||
	strcmp (argv[argc - 1], "--verbose") == 0)
      debug = 1;
    else if (strcmp (argv[argc - 1], "-b") == 0 ||
	     strcmp (argv[argc - 1], "--break-on-error") == 0)
      break_on_error = 1;
    else if (strcmp (argv[argc - 1], "-h") == 0 ||
	     strcmp (argv[argc - 1], "-?") == 0 ||
	     strcmp (argv[argc - 1], "--help") == 0)
      {
	printf ("Usage: %s [-vbh?] [--verbose] [--break-on-error] [--help]\n",
		argv[0]);
	return 1;
      }
  while (argc-- > 1);

  handle = shishi ();

  /* Name of service. */

  bufdesc.value = (char *) "*****@*****.**";
  bufdesc.length = strlen (bufdesc.value);

  maj_stat = gss_import_name (&min_stat, &bufdesc,
			      GSS_C_NT_HOSTBASED_SERVICE, &servername);
  if (GSS_ERROR (maj_stat))
    fail ("gss_import_name (host/server)\n");

  /* Get credential, for server. */

  maj_stat = gss_acquire_cred (&min_stat, servername, 0,
			       GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
			       &server_creds, NULL, NULL);
  if (GSS_ERROR (maj_stat))
    {
      fail ("gss_acquire_cred");
      display_status ("acquire credentials", maj_stat, min_stat);
    }

  for (i = 0; i < 3; i++)
    {
      /* Start client. */

      switch (i)
	{
	case 0:
	  maj_stat = gss_init_sec_context (&min_stat,
					   GSS_C_NO_CREDENTIAL,
					   &cctx,
					   servername,
					   GSS_KRB5,
					   GSS_C_MUTUAL_FLAG |
					   GSS_C_REPLAY_FLAG |
					   GSS_C_SEQUENCE_FLAG,
					   0,
					   GSS_C_NO_CHANNEL_BINDINGS,
					   GSS_C_NO_BUFFER, NULL,
					   &bufdesc2, NULL, NULL);
	  if (maj_stat != GSS_S_CONTINUE_NEEDED)
	    fail ("loop 0 init failure\n");
	  break;

	case 1:
	  /* Default OID, channel bindings. */
	  maj_stat = gss_init_sec_context (&min_stat,
					   GSS_C_NO_CREDENTIAL,
					   &cctx,
					   servername,
					   GSS_C_NO_OID,
					   GSS_C_MUTUAL_FLAG |
					   GSS_C_REPLAY_FLAG |
					   GSS_C_SEQUENCE_FLAG,
					   0,
					   &cb,
					   GSS_C_NO_BUFFER, NULL,
					   &bufdesc2, NULL, NULL);
	  if (maj_stat != GSS_S_CONTINUE_NEEDED)
	    fail ("loop 0 init failure\n");
	  break;

	case 2:
	  /* No mutual authentication. */
	  maj_stat = gss_init_sec_context (&min_stat,
					   GSS_C_NO_CREDENTIAL,
					   &cctx,
					   servername,
					   GSS_KRB5,
					   GSS_C_REPLAY_FLAG |
					   GSS_C_CONF_FLAG |
					   GSS_C_SEQUENCE_FLAG,
					   0,
					   GSS_C_NO_CHANNEL_BINDINGS,
					   GSS_C_NO_BUFFER, NULL,
					   &bufdesc2, &ret_flags, NULL);
	  if (ret_flags != (GSS_C_REPLAY_FLAG |
			    GSS_C_CONF_FLAG |
			    GSS_C_SEQUENCE_FLAG | GSS_C_PROT_READY_FLAG))
	    fail ("loop 2 ret_flags failure (%d)\n", ret_flags);
	  if (maj_stat != GSS_S_COMPLETE)
	    fail ("loop 1 init failure\n");
	  break;

	default:
	  fail ("default?!\n");
	  break;
	}
      if (GSS_ERROR (maj_stat))
	{
	  fail ("gss_accept_sec_context failure\n");
	  display_status ("accept_sec_context", maj_stat, min_stat);
	}

      if (debug)
	{
	  char *p = bufdesc2.value;
	  Shishi_asn1 apreq = shishi_der2asn1_apreq (handle,
						     p + 17,
						     bufdesc2.length - 17);
	  printf ("\nClient AP-REQ:\n\n");
	  shishi_apreq_print (handle, stdout, apreq);
	}

      /* Start server. */

      switch (i)
	{
	case 0:
	  maj_stat = gss_accept_sec_context (&min_stat,
					     &sctx,
					     server_creds,
					     &bufdesc2,
					     GSS_C_NO_CHANNEL_BINDINGS,
					     &name,
					     NULL,
					     &bufdesc,
					     &ret_flags, &time_rec, NULL);
	  if (ret_flags != (GSS_C_MUTUAL_FLAG |
			    /* XXX GSS_C_REPLAY_FLAG |
			       GSS_C_SEQUENCE_FLAG | */
			    GSS_C_PROT_READY_FLAG))
	    fail ("loop 0 accept flag failure (%d)\n", ret_flags);
	  break;

	case 1:
	  maj_stat = gss_accept_sec_context (&min_stat,
					     &sctx,
					     server_creds,
					     &bufdesc2,
					     &cb,
					     &name,
					     NULL,
					     &bufdesc,
					     &ret_flags, &time_rec, NULL);
	  break;

	case 2:
	  maj_stat = gss_accept_sec_context (&min_stat,
					     &sctx,
					     server_creds,
					     &bufdesc2,
					     GSS_C_NO_CHANNEL_BINDINGS,
					     &name,
					     NULL,
					     &bufdesc,
					     &ret_flags, &time_rec, NULL);
	  break;
	default:
	  fail ("default?!\n");
	  break;
	}
      if (GSS_ERROR (maj_stat))
	{
	  fail ("gss_accept_sec_context failure\n");
	  display_status ("accept_sec_context", maj_stat, min_stat);
	}

      if (debug)
	{
	  char *p = bufdesc2.value;
	  Shishi_asn1 aprep =
	    shishi_der2asn1_aprep (handle, p + 15, bufdesc.length - 15);
	  printf ("\nServer AP-REP:\n\n");
	  shishi_aprep_print (handle, stdout, aprep);
	}

      switch (i)
	{
	case 0:
	  maj_stat = gss_init_sec_context (&min_stat,
					   GSS_C_NO_CREDENTIAL,
					   &cctx,
					   servername,
					   GSS_KRB5,
					   GSS_C_MUTUAL_FLAG |
					   GSS_C_REPLAY_FLAG |
					   GSS_C_SEQUENCE_FLAG,
					   0,
					   GSS_C_NO_CHANNEL_BINDINGS,
					   &bufdesc, NULL,
					   &bufdesc2, NULL, NULL);
	  break;

	case 1:
	  /* Check ret_flags. */
	  maj_stat = gss_init_sec_context (&min_stat,
					   GSS_C_NO_CREDENTIAL,
					   &cctx,
					   servername,
					   GSS_KRB5,
					   GSS_C_MUTUAL_FLAG |
					   GSS_C_REPLAY_FLAG |
					   GSS_C_SEQUENCE_FLAG,
					   0,
					   GSS_C_NO_CHANNEL_BINDINGS,
					   &bufdesc, NULL,
					   &bufdesc2, &ret_flags, &time_rec);
	  if (ret_flags != (GSS_C_MUTUAL_FLAG |
			    GSS_C_REPLAY_FLAG |
			    GSS_C_SEQUENCE_FLAG | GSS_C_PROT_READY_FLAG))
	    fail ("loop 1 ret_flags failure (%d)\n", ret_flags);
	  break;

	  /* No case 2. */

	default:
	  break;
	}
      if (GSS_ERROR (maj_stat))
	{
	  fail ("gss_init_sec_context failure (2)\n");
	  display_status ("init_sec_context", maj_stat, min_stat);
	}

      {
	gss_buffer_desc pt, pt2, ct;
	int conf_state;
	gss_qop_t qop_state;

	pt.value = (char *) "foo";
	pt.length = strlen (pt.value) + 1;
	maj_stat = gss_wrap (&min_stat, cctx, 0, 0, &pt, &conf_state, &ct);
	if (GSS_ERROR (maj_stat))
	  {
	    fail ("client gss_wrap failure\n");
	    display_status ("client wrap", maj_stat, min_stat);
	  }

	maj_stat = gss_unwrap (&min_stat, sctx,
			       &ct, &pt2, &conf_state, &qop_state);
	if (GSS_ERROR (maj_stat))
	  {
	    fail ("server gss_unwrap failure\n");
	    display_status ("client wrap", maj_stat, min_stat);
	  }

	if (pt.length != pt2.length
	    || memcmp (pt2.value, pt.value, pt.length) != 0)
	  fail ("wrap+unwrap failed (%d, %d, %.*s)\n",
		(int) pt.length, (int) pt2.length, (int) pt2.length,
		(char *) pt2.value);

	gss_release_buffer (&min_stat, &ct);
	gss_release_buffer (&min_stat, &pt2);
      }

      maj_stat = gss_delete_sec_context (&min_stat, &cctx, GSS_C_NO_BUFFER);
      if (GSS_ERROR (maj_stat))
	{
	  fail ("client gss_delete_sec_context failure\n");
	  display_status ("client delete_sec_context", maj_stat, min_stat);
	}

      maj_stat = gss_delete_sec_context (&min_stat, &sctx, GSS_C_NO_BUFFER);
      if (GSS_ERROR (maj_stat))
	{
	  fail ("server gss_delete_sec_context failure\n");
	  display_status ("server delete_sec_context", maj_stat, min_stat);
	}

      success ("loop %d ok\n", (int) i);
    }

  /* Clean up. */

  maj_stat = gss_release_cred (&min_stat, &server_creds);
  if (GSS_ERROR (maj_stat))
    {
      fail ("gss_release_cred");
      display_status ("release credentials", maj_stat, min_stat);
    }

  maj_stat = gss_release_name (&min_stat, &servername);
  if (GSS_ERROR (maj_stat))
    {
      fail ("gss_release_name failure\n");
      display_status ("gss_release_name", maj_stat, min_stat);
    }

  shishi_done (handle);

  /* We're done. */

  if (debug)
    printf ("Kerberos 5 security context self tests done with %d errors\n",
	    error_count);

  return error_count ? 1 : 0;
}
/**
 * Return a copy of the short description from the instance data
 * @ingroup globus_gssapi_error_object 
 * 
 * @param error
 *        The error object to retrieve the data from.
 * @return
 *        String containing the short description if it exists, NULL
 *        otherwise.
 */
static
char *
globus_l_error_gssapi_printable(
    globus_object_t *                   error)
{
    OM_uint32	                        minor_status;
    OM_uint32                           message_context;
    gss_buffer_desc                     status_string_desc 
        = GSS_C_EMPTY_BUFFER;
    gss_buffer_t                        status_string = &status_string_desc;
    char *                              msg = NULL;
    char *                              tmp;
    int                                 len = 0;
    globus_l_gssapi_error_data_t *      data;
    
    data = (globus_l_gssapi_error_data_t *)
         globus_object_get_local_instance_data(error);
    
    if(data->is_globus_gsi)
    {
        message_context = 0;
        do
        {
            if(gss_display_status(&minor_status,
                                   data->major_status,
                                   GSS_C_GSS_CODE,
                                   GSS_C_NO_OID,
                                   &message_context,
                                   status_string) == GSS_S_COMPLETE)
            {
                if(status_string->length)
                {
                    if(msg)
                    {
                        tmp = globus_realloc(
                            msg,
                            sizeof(char) * (len + status_string->length + 1));
                    }
                    else
                    {
                        tmp = globus_malloc(
                            sizeof(char) * (status_string->length + 1));
                    }
                    if(tmp)
                    {
                        memcpy(
                            tmp + len,
                            status_string->value,
                            status_string->length);
                        msg = tmp;
                        len += status_string->length;
                    }
                }
                gss_release_buffer(&minor_status, status_string);
            }
        } while(message_context != 0);
        
        if(msg)
        {
            if(msg[len - 1] == '\n')
            {
                len--;
            }
            msg[len] = '\0';
        }
    }
    else
    {
        globus_gss_assist_display_status_str(
            &msg, NULL, data->major_status, data->minor_status, 0);
    }
    
    return msg;
}/* globus_l_error_gssapi_printable */
int Curl_input_negotiate(struct connectdata *conn, const char *header)
{
  struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
  OM_uint32 major_status, minor_status, minor_status2;
  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
  int ret;
  size_t len;
  bool gss;
  const char* protocol;

  while(*header && ISSPACE(*header))
    header++;
  if(checkprefix("GSS-Negotiate", header)) {
    protocol = "GSS-Negotiate";
    gss = TRUE;
  }
  else if (checkprefix("Negotiate", header)) {
    protocol = "Negotiate";
    gss = FALSE;
  }
  else
    return -1;

  if (neg_ctx->context) {
    if (neg_ctx->gss != gss) {
      return -1;
    }
  }
  else {
    neg_ctx->protocol = protocol;
    neg_ctx->gss = gss;
  }

  if (neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) {
    /* We finished succesfully our part of authentication, but server
     * rejected it (since we're again here). Exit with an error since we
     * can't invent anything better */
    Curl_cleanup_negotiate(conn->data);
    return -1;
  }

  if (neg_ctx->server_name == NULL &&
      (ret = get_gss_name(conn, &neg_ctx->server_name)))
    return ret;

  header += strlen(neg_ctx->protocol);
  while(*header && ISSPACE(*header))
    header++;

  len = strlen(header);
  if (len > 0) {
    int rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value);
    if (rawlen < 0)
      return -1;
    input_token.length = rawlen;

#ifdef HAVE_SPNEGO /* Handle SPNEGO */
    if (checkprefix("Negotiate", header)) {
        ASN1_OBJECT *   object            = NULL;
        int             rc                = 1;
        unsigned char * spnegoToken       = NULL;
        size_t          spnegoTokenLength = 0;
        unsigned char * mechToken         = NULL;
        size_t          mechTokenLength   = 0;

        spnegoToken = malloc(input_token.length);
        if (input_token.value == NULL)
          return ENOMEM;
        spnegoTokenLength = input_token.length;

        object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1);
        if (!parseSpnegoTargetToken(spnegoToken,
                                    spnegoTokenLength,
                                    NULL,
                                    NULL,
                                    &mechToken,
                                    &mechTokenLength,
                                    NULL,
                                    NULL)) {
          free(spnegoToken);
          spnegoToken = NULL;
          infof(conn->data, "Parse SPNEGO Target Token failed\n");
        }
        else {
          free(input_token.value);
          input_token.value = NULL;
          input_token.value = malloc(mechTokenLength);
          memcpy(input_token.value, mechToken,mechTokenLength);
          input_token.length = mechTokenLength;
          free(mechToken);
          mechToken = NULL;
          infof(conn->data, "Parse SPNEGO Target Token succeeded\n");
        }
    }
#endif
  }

  major_status = gss_init_sec_context(&minor_status,
                                      GSS_C_NO_CREDENTIAL,
                                      &neg_ctx->context,
                                      neg_ctx->server_name,
                                      GSS_C_NO_OID,
                                      GSS_C_DELEG_FLAG,
                                      0,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &input_token,
                                      NULL,
                                      &output_token,
                                      NULL,
                                      NULL);
  if (input_token.length > 0)
    gss_release_buffer(&minor_status2, &input_token);
  neg_ctx->status = major_status;
  if (GSS_ERROR(major_status)) {
    /* Curl_cleanup_negotiate(conn->data) ??? */
    log_gss_error(conn, minor_status,
                  (char *)"gss_init_sec_context() failed: ");
    return -1;
  }

  if (output_token.length == 0) {
    return -1;
  }

  neg_ctx->output_token = output_token;
  /* conn->bits.close = FALSE; */

  return 0;
}
Exemple #30
0
/*
 * Continue GSS authentication with next token as needed.
 */
static int
pg_GSS_continue(PGconn *conn)
{
	OM_uint32	maj_stat,
				min_stat,
				lmin_s;

	maj_stat = gss_init_sec_context(&min_stat,
									GSS_C_NO_CREDENTIAL,
									&conn->gctx,
									conn->gtarg_nam,
									GSS_C_NO_OID,
									GSS_C_MUTUAL_FLAG,
									0,
									GSS_C_NO_CHANNEL_BINDINGS,
		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
									NULL,
									&conn->goutbuf,
									NULL,
									NULL);

	if (conn->gctx != GSS_C_NO_CONTEXT)
	{
		free(conn->ginbuf.value);
		conn->ginbuf.value = NULL;
		conn->ginbuf.length = 0;
	}

	if (conn->goutbuf.length != 0)
	{
		/*
		 * GSS generated data to send to the server. We don't care if it's the
		 * first or subsequent packet, just send the same kind of password
		 * packet.
		 */
		if (pqPacketSend(conn, 'p',
						 conn->goutbuf.value, conn->goutbuf.length)
			!= STATUS_OK)
		{
			gss_release_buffer(&lmin_s, &conn->goutbuf);
			return STATUS_ERROR;
		}
	}
	gss_release_buffer(&lmin_s, &conn->goutbuf);

	if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
	{
		pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
					 conn,
					 maj_stat, min_stat);
		gss_release_name(&lmin_s, &conn->gtarg_nam);
		if (conn->gctx)
			gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
		return STATUS_ERROR;
	}

	if (maj_stat == GSS_S_COMPLETE)
		gss_release_name(&lmin_s, &conn->gtarg_nam);

	return STATUS_OK;
}