/*
 * Naming extensions based local login authorization.
 */
static OM_uint32
attr_authorize_localname(OM_uint32 *minor,
                         const gss_name_t name,
                         const gss_union_name_t unionUser)
{
    OM_uint32 major = GSS_S_UNAVAILABLE; /* attribute not present */
    gss_buffer_t externalName;
    int more = -1;

    if (unionUser->name_type != GSS_C_NO_OID &&
            !g_OID_equal(unionUser->name_type, GSS_C_NT_USER_NAME))
        return (GSS_S_BAD_NAMETYPE);

    externalName = unionUser->external_name;
    assert(externalName != GSS_C_NO_BUFFER);

    while (more != 0 && major != GSS_S_COMPLETE) {
        OM_uint32 tmpMajor, tmpMinor;
        gss_buffer_desc value;
        gss_buffer_desc display_value;
        int authenticated = 0, complete = 0;

        tmpMajor = gss_get_name_attribute(minor,
                                          name,
                                          GSS_C_ATTR_LOCAL_LOGIN_USER,
                                          &authenticated,
                                          &complete,
                                          &value,
                                          &display_value,
                                          &more);
        if (GSS_ERROR(tmpMajor)) {
            major = tmpMajor;
            break;
        }

        if (authenticated &&
                value.length == externalName->length &&
                memcmp(value.value, externalName->value, externalName->length) == 0)
            major = GSS_S_COMPLETE;
        else
            major = GSS_S_UNAUTHORIZED;

        gss_release_buffer(&tmpMinor, &value);
        gss_release_buffer(&tmpMinor, &display_value);
    }

    return (major);
}
示例#2
0
OM_uint32
ntlm_gss_get_name_attribute(OM_uint32 *minor_status,
			      gss_name_t name,
			      gss_buffer_t attr,
			      int *authenticated,
			      int *complete,
			      gss_buffer_t value,
			      gss_buffer_t display_value,
			      int *more)
{
	OM_uint32 ret;
	ret = gss_get_name_attribute(minor_status,
				     name,
				     attr,
				     authenticated,
				     complete,
				     value,
				     display_value,
				     more);
	return (ret);
}
示例#3
0
文件: common.c 项目: PADL/krb5
static void
dump_attribute(gss_name_t name, gss_buffer_t attribute, int noisy)
{
    OM_uint32 major, minor;
    gss_buffer_desc value;
    gss_buffer_desc display_value;
    int authenticated = 0;
    int complete = 0;
    int more = -1;
    unsigned int i;

    while (more != 0) {
        value.value = NULL;
        display_value.value = NULL;

        major = gss_get_name_attribute(&minor, name, attribute, &authenticated,
                                       &complete, &value, &display_value,
                                       &more);
        check_gsserr("gss_get_name_attribute", major, minor);

        printf("Attribute %.*s %s %s\n\n%.*s\n",
               (int)attribute->length, (char *)attribute->value,
               authenticated ? "Authenticated" : "",
               complete ? "Complete" : "",
               (int)display_value.length, (char *)display_value.value);

        if (noisy) {
            for (i = 0; i < value.length; i++) {
                if ((i % 32) == 0)
                    printf("\n");
                printf("%02x", ((char *)value.value)[i] & 0xFF);
            }
            printf("\n\n");
        }

        (void)gss_release_buffer(&minor, &value);
        (void)gss_release_buffer(&minor, &display_value);
    }
}
示例#4
0
enum auth_stat
_svcauth_gss(struct svc_req *req, struct rpc_msg *msg,
	     bool *no_dispatch)
{
	XDR xdrs[1];
	SVCAUTH *auth;
	struct svc_rpc_gss_data *gd = NULL;
	struct rpc_gss_cred *gc = NULL;
	struct rpc_gss_init_res gr;
	int call_stat, offset;
	OM_uint32 min_stat;
	bool gd_locked = false;
	bool gd_hashed = false;

	/* Initialize reply. */
	req->rq_verf = _null_auth;

	/* Unserialize client credentials. */
	if (req->rq_cred.oa_length <= 0)
		svcauth_gss_return(AUTH_BADCRED);

	gc = (struct rpc_gss_cred *)req->rq_clntcred;
	memset(gc, 0, sizeof(struct rpc_gss_cred));

	xdrmem_create(xdrs, req->rq_cred.oa_base, req->rq_cred.oa_length,
		      XDR_DECODE);

	if (!xdr_rpc_gss_cred(xdrs, gc)) {
		XDR_DESTROY(xdrs);
		svcauth_gss_return(AUTH_BADCRED);
	}
	XDR_DESTROY(xdrs);

	/* Check version. */
	if (gc->gc_v != RPCSEC_GSS_VERSION)
		svcauth_gss_return(AUTH_BADCRED);

	if (gc->gc_seq > RPCSEC_GSS_MAXSEQ)
		svcauth_gss_return(RPCSEC_GSS_CTXPROBLEM);

	if (gc->gc_proc > RPCSEC_GSS_MAXPROC)
		svcauth_gss_return(AUTH_BADCRED);

	/* Check RPCSEC_GSS service. */
	if (gc->gc_svc != RPCSEC_GSS_SVC_NONE
	    && gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY
	    && gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY)
		svcauth_gss_return(AUTH_BADCRED);

	/* Context lookup. */
	if ((gc->gc_proc == RPCSEC_GSS_DATA)
	    || (gc->gc_proc == RPCSEC_GSS_DESTROY)) {

		/* Per RFC 2203 5.3.3.3, if a valid security context
		 * cannot be found to authorize a request, the
		 * implementation returns RPCSEC_GSS_CREDPROBLEM.
		 * N.B., we are explicitly allowed to discard contexts
		 * for any reason (e.g., to save space). */
		gd = authgss_ctx_hash_get(gc);
		if (!gd)
			svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM);
		gd_hashed = true;
		if (gc->gc_svc != gd->sec.svc)
			gd->sec.svc = gc->gc_svc;
	}

	if (!gd) {
		/* Allocate and set up server auth handle. */
		auth = mem_alloc(sizeof(SVCAUTH));
		gd = alloc_svc_rpc_gss_data();
		auth->svc_ah_ops = &svc_auth_gss_ops;
		auth->svc_ah_private = (caddr_t) gd;
		gd->auth = auth;
	}

	/* Serialize context. */
	mutex_lock(&gd->lock);
	gd_locked = true;

	/* thread auth */
	req->rq_auth = gd->auth;

	/* Check sequence number. */
	if (gd->established) {
		if (get_time_fast() >= gd->endtime) {
			*no_dispatch = true;
			svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM);
		}

		/* XXX implied serialization?  or just fudging?  advance if
		 * greater? */
		offset = gd->seqlast - gc->gc_seq;
		if (offset < 0) {
			gd->seqlast = gc->gc_seq;
			offset = 0 - offset;
			gd->seqmask <<= offset;
			offset = 0;
		} else if (offset >= gd->win || (gd->seqmask & (1 << offset))) {
			*no_dispatch = true;
			svcauth_gss_return(AUTH_OK);
		}
		gd->seqmask |= (1 << offset);	/* XXX harmless */

		req->rq_ap1 = (void *)(uintptr_t) gc->gc_seq; /* GCC casts */
		req->rq_clntname = (char *) gd->client_name;
		req->rq_svcname = (char *) gd->ctx;
	}

	/* gd->established */
	/* Handle RPCSEC_GSS control procedure. */
	switch (gc->gc_proc) {

	case RPCSEC_GSS_INIT:
	case RPCSEC_GSS_CONTINUE_INIT:

		if (req->rq_proc != NULLPROC)
			svcauth_gss_return(AUTH_FAILED); /* XXX ? */

		/* XXX why unconditionally acquire creds? */
		if (!svcauth_gss_acquire_cred())
			svcauth_gss_return(AUTH_FAILED);

		if (!svcauth_gss_accept_sec_context(req, gd, &gr))
			svcauth_gss_return(AUTH_REJECTEDCRED);

		if (!svcauth_gss_nextverf(req, gd, htonl(gr.gr_win))) {
			/* XXX check */
			gss_release_buffer(&min_stat, &gr.gr_token);
			mem_free(gr.gr_ctx.value, 0);
			svcauth_gss_return(AUTH_FAILED);
		}

		*no_dispatch = true;

		call_stat =
		    svc_sendreply(req->rq_xprt, req,
				  (xdrproc_t) xdr_rpc_gss_init_res,
				  (caddr_t) &gr);

		/* XXX */
		gss_release_buffer(&min_stat, &gr.gr_token);
		gss_release_buffer(&min_stat, &gd->checksum);
		mem_free(gr.gr_ctx.value, 0);

		if (!call_stat)
			svcauth_gss_return(AUTH_FAILED);

		if (gr.gr_major == GSS_S_COMPLETE) {
			gd->established = true;
			if (!gd_hashed) {

				/* krb5 pac -- try all that apply */
				gss_buffer_desc attr, display_buffer;

				/* completely generic */
				int auth = 1, comp = 0, more = -1;

				memset(&gd->pac.ms_pac, 0,
				       sizeof(gss_buffer_desc));
				memset(&display_buffer, 0,
				       sizeof(gss_buffer_desc));

				/* MS AD */
				attr.value = "urn:mspac:";
				attr.length = 10;

				gr.gr_major =
				    gss_get_name_attribute(&gr.gr_minor,
							   gd->client_name,
							   &attr, &auth, &comp,
							   &gd->pac.ms_pac,
							   &display_buffer,
							   &more);

				if (gr.gr_major == GSS_S_COMPLETE) {
					/* dont need it */
					gss_release_buffer(&gr.gr_minor,
							   &display_buffer);
					gd->flags |= SVC_RPC_GSS_FLAG_MSPAC;
				}

				(void)authgss_ctx_hash_set(gd);
			}
		}
		break;

		/* XXX next 2 cases:  is it correct to leave gd in cache
		 * after a validate or verf failure ? */

	case RPCSEC_GSS_DATA:
		call_stat = svcauth_gss_validate(req, gd, msg);
		switch (call_stat) {
		default:
			svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM);
		case 0:
			break;
		}

		if (!svcauth_gss_nextverf(req, gd, htonl(gc->gc_seq)))
			svcauth_gss_return(AUTH_FAILED);
		break;

	case RPCSEC_GSS_DESTROY:
		if (req->rq_proc != NULLPROC)
			svcauth_gss_return(AUTH_FAILED);	/* XXX ? */

		if (svcauth_gss_validate(req, gd, msg))
			svcauth_gss_return(RPCSEC_GSS_CREDPROBLEM);

		if (!svcauth_gss_nextverf(req, gd, htonl(gc->gc_seq)))
			svcauth_gss_return(AUTH_FAILED);

		*no_dispatch = true;

		(void)authgss_ctx_hash_del(gd);

		/* avoid lock order reversal gd->lock, xprt->xp_lock */
		mutex_unlock(&gd->lock);
		gd_locked = false;

		call_stat =
		    svc_sendreply(req->rq_xprt, req, (xdrproc_t) xdr_void,
				  (caddr_t) NULL);

		/* We acquired a reference on gd with authgss_ctx_hash_get
		 * call.  Time to release the reference as we don't need
		 * gd anymore.
		 */
		unref_svc_rpc_gss_data(gd, SVC_RPC_GSS_FLAG_NONE);
		req->rq_auth = &svc_auth_none;

		break;

	default:
		svcauth_gss_return(AUTH_REJECTEDCRED);
		break;
	}

	svcauth_gss_return(AUTH_OK);
}
示例#5
0
static int
gssattr_dynacl_mask(
	void			*priv,
	Operation		*op,
	Entry			*target,
	AttributeDescription	*desc,
	struct berval		*val,
	int			nmatch,
	regmatch_t		*matches,
	slap_access_t		*grant,
	slap_access_t		*deny )
{
	gssattr_t	*gssattr = (gssattr_t *)priv;
	sasl_conn_t	*sasl_ctx = op->o_conn->c_sasl_authctx;
	gss_name_t	gss_name = GSS_C_NO_NAME;
	OM_uint32	major, minor;
	int		more = -1;
	int		authenticated, complete;
	gss_buffer_desc	attr = GSS_C_EMPTY_BUFFER;
	int		granted = 0;

	ACL_INVALIDATE( *deny );

	if ( sasl_ctx == NULL ||
	     sasl_getprop( sasl_ctx, SASL_GSS_PEER_NAME, (const void **)&gss_name) != 0 ||
	     gss_name == GSS_C_NO_NAME ) {
		return 0;
	}

	attr.length = gssattr->gssattr_name.bv_len;
	attr.value = gssattr->gssattr_name.bv_val;

	while ( more != 0 ) {
		AclRegexMatches amatches = { 0 };
		gss_buffer_desc	gss_value = GSS_C_EMPTY_BUFFER;
		gss_buffer_desc	gss_display_value = GSS_C_EMPTY_BUFFER;
		struct berval bv_value;

		major = gss_get_name_attribute( &minor, gss_name, &attr,
						&authenticated, &complete,
						&gss_value, &gss_display_value, &more );
		if ( GSS_ERROR( major ) ) {
			break;
		} else if ( authenticated == 0 ) {
			gss_release_buffer( &minor, &gss_value );
			gss_release_buffer( &minor, &gss_display_value );
			continue;
		}

		bv_value.bv_len = gss_value.length;
		bv_value.bv_val = (char *)gss_value.value;

		if ( !ber_bvccmp( &gssattr->gssattr_value, '*' ) ) {
			if ( gssattr->gssattr_style != ACL_STYLE_BASE ) {
				amatches.dn_count = nmatch;
				AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
			}

			switch ( gssattr->gssattr_style ) {
			case ACL_STYLE_REGEX:
				/* XXX assumes value NUL terminated */
				granted = regex_matches( &gssattr->gssattr_value, bv_value.bv_val,
							  &target->e_nname, val, &amatches );
				break;
			case ACL_STYLE_EXPAND: {
				struct berval bv;
				char buf[ACL_BUF_SIZE];

				bv.bv_len = sizeof( buf ) - 1;
				bv.bv_val = buf;

				granted = ( acl_string_expand( &bv, &gssattr->gssattr_value,
							       &target->e_nname, val,
							       &amatches ) == 0 ) &&
					  ( ber_bvstrcmp( &bv, &bv_value) == 0 );
				break;
			}
			case ACL_STYLE_BASE:
				granted = ( ber_bvstrcmp( &gssattr->gssattr_value, &bv_value ) == 0 );
				break;
			default:
				assert(0);
				break;
			}
		} else {
			granted = 1;
		}

		gss_release_buffer( &minor, &gss_value );
		gss_release_buffer( &minor, &gss_display_value );

		if ( granted ) {
			break;
		}
	}

	if ( granted ) {
		ACL_LVL_ASSIGN_WRITE( *grant );
	}

	return 0;
}
示例#6
0
NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx,
				gss_ctx_id_t gssapi_context,
				gss_name_t gss_client_name,
				DATA_BLOB *pac_blob)
{
	NTSTATUS status;
	OM_uint32 gss_maj, gss_min;
#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE
/*
 * gss_get_name_attribute() in MIT krb5 1.10.0 can return unintialized pac_display_buffer
 * and later gss_release_buffer() will crash on attempting to release it.
 *
 * So always initialize the buffer descriptors.
 *
 * See following links for more details:
 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658514
 * http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=7087
 */
	gss_buffer_desc pac_buffer = {
		.value = NULL,
		.length = 0
	};
	gss_buffer_desc pac_display_buffer = {
		.value = NULL,
		.length = 0
	};
	gss_buffer_desc pac_name = {
		.value = discard_const("urn:mspac:"),
		.length = sizeof("urn:mspac:")-1
	};
	int more = -1;
	int authenticated = false;
	int complete = false;

	gss_maj = gss_get_name_attribute(
		&gss_min, gss_client_name, &pac_name,
		&authenticated, &complete,
		&pac_buffer, &pac_display_buffer, &more);

	if (gss_maj != 0) {
		gss_OID oid = discard_const(gss_mech_krb5);
		DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute "
			   "failed: %s\n", gssapi_error_string(mem_ctx,
							       gss_maj, gss_min,
							       oid));
		return NT_STATUS_ACCESS_DENIED;
	} else if (authenticated && complete) {
		/* The PAC blob is returned directly */
		*pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value,
					    pac_buffer.length);

		if (!pac_blob->data) {
			status = NT_STATUS_NO_MEMORY;
		} else {
			status = NT_STATUS_OK;
		}

		gss_maj = gss_release_buffer(&gss_min, &pac_buffer);
		gss_maj = gss_release_buffer(&gss_min, &pac_display_buffer);
		return status;
	} else {
		DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n",
			  authenticated ? "true" : "false",
			  complete ? "true" : "false",
			  more ? "true" : "false"));
		return NT_STATUS_ACCESS_DENIED;
	}

#elif defined(HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID)
	gss_OID_desc pac_data_oid = {
		.elements = discard_const(EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID),
		.length = EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH
	};

	gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;

	/* If we didn't have the routine to get a verified, validated
	 * PAC (supplied only by MIT at the time of writing), then try
	 * with the Heimdal OID (fetches the PAC directly and always
	 * validates) */
	gss_maj = gss_inquire_sec_context_by_oid(
				&gss_min, gssapi_context,
				&pac_data_oid, &set);

	/* First check for the error MIT gives for an unknown OID */
	if (gss_maj == GSS_S_UNAVAILABLE) {
		DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
			  "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
	} else if (gss_maj != 0) {
		DEBUG(2, ("obtaining PAC via GSSAPI gss_inqiure_sec_context_by_oid (Heimdal OID) failed: %s\n",
			  gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5)));
	} else {
		if (set == GSS_C_NO_BUFFER_SET) {
			DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
				  "data in results.\n"));
			return NT_STATUS_INTERNAL_ERROR;
		}

		/* The PAC blob is returned directly */
		*pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value,
					    set->elements[0].length);
		if (!pac_blob->data) {
			status = NT_STATUS_NO_MEMORY;
		} else {
			status = NT_STATUS_OK;
		}

		gss_maj = gss_release_buffer_set(&gss_min, &set);
		return status;
	}
#else
	DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
		  "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
#endif
	return NT_STATUS_ACCESS_DENIED;
}

NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx,
				gss_ctx_id_t gssapi_context,
				DATA_BLOB *session_key, 
				uint32_t *keytype)
{
	OM_uint32 gss_min, gss_maj;
	gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;

	gss_maj = gss_inquire_sec_context_by_oid(
				&gss_min, gssapi_context,
				&gse_sesskey_inq_oid, &set);
	if (gss_maj) {
		DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
			  gssapi_error_string(mem_ctx,
					      gss_maj,
					      gss_min,
					      discard_const_p(struct gss_OID_desc_struct,
							      gss_mech_krb5))));
		return NT_STATUS_NO_USER_SESSION_KEY;
	}

	if ((set == GSS_C_NO_BUFFER_SET) ||
	    (set->count == 0)) {
#ifdef HAVE_GSSKRB5_GET_SUBKEY
		krb5_keyblock *subkey;
		gss_maj = gsskrb5_get_subkey(&gss_min,
					     gssapi_context,
					     &subkey);
		if (gss_maj != 0) {
			DEBUG(1, ("NO session key for this mech\n"));
			return NT_STATUS_NO_USER_SESSION_KEY;
		}
		if (session_key) {
			*session_key = data_blob_talloc(mem_ctx,
							KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
		}
		if (keytype) {
			*keytype = KRB5_KEY_TYPE(subkey);
		}
		krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
		return NT_STATUS_OK;
#else
		DEBUG(0, ("gss_inquire_sec_context_by_oid didn't return any session key (and no alternative method available)\n"));
		return NT_STATUS_NO_USER_SESSION_KEY;
#endif
	}

	if (session_key) {
		*session_key = data_blob_talloc(mem_ctx, set->elements[0].value,
						set->elements[0].length);
	}

	if (keytype) {
		int diflen, i;
		const uint8_t *p;

		*keytype = 0;
		if (set->count < 2) {

#ifdef HAVE_GSSKRB5_GET_SUBKEY
			krb5_keyblock *subkey;
			gss_maj = gsskrb5_get_subkey(&gss_min,
						     gssapi_context,
						     &subkey);
			if (gss_maj == 0) {
				*keytype = KRB5_KEY_TYPE(subkey);
				krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
			}
#endif
			gss_maj = gss_release_buffer_set(&gss_min, &set);
	
			return NT_STATUS_OK;

		} else if (memcmp(set->elements[1].value,
				  gse_sesskeytype_oid.elements,
				  gse_sesskeytype_oid.length) != 0) {
			/* Perhaps a non-krb5 session key */
			gss_maj = gss_release_buffer_set(&gss_min, &set);
			return NT_STATUS_OK;
		}
		p = (const uint8_t *)set->elements[1].value + gse_sesskeytype_oid.length;
		diflen = set->elements[1].length - gse_sesskeytype_oid.length;
		if (diflen <= 0) {
			gss_maj = gss_release_buffer_set(&gss_min, &set);
			return NT_STATUS_INVALID_PARAMETER;
		}
		for (i = 0; i < diflen; i++) {
			*keytype = (*keytype << 7) | (p[i] & 0x7f);
			if (i + 1 != diflen && (p[i] & 0x80) == 0) {
				gss_maj = gss_release_buffer_set(&gss_min, &set);
				return NT_STATUS_INVALID_PARAMETER;
			}
		}
	}

	gss_maj = gss_release_buffer_set(&gss_min, &set);
	return NT_STATUS_OK;
}


char *gssapi_error_string(TALLOC_CTX *mem_ctx,
			  OM_uint32 maj_stat, OM_uint32 min_stat,
			  const gss_OID mech)
{
	OM_uint32 disp_min_stat, disp_maj_stat;
	gss_buffer_desc maj_error_message;
	gss_buffer_desc min_error_message;
	char *maj_error_string, *min_error_string;
	OM_uint32 msg_ctx = 0;

	char *ret;

	maj_error_message.value = NULL;
	min_error_message.value = NULL;
	maj_error_message.length = 0;
	min_error_message.length = 0;

	disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat,
					   GSS_C_GSS_CODE, mech,
					   &msg_ctx, &maj_error_message);
	if (disp_maj_stat != 0) {
		maj_error_message.value = NULL;
		maj_error_message.length = 0;
	}
	disp_maj_stat = gss_display_status(&disp_min_stat, min_stat,
					   GSS_C_MECH_CODE, mech,
					   &msg_ctx, &min_error_message);
	if (disp_maj_stat != 0) {
		min_error_message.value = NULL;
		min_error_message.length = 0;
	}

	maj_error_string = talloc_strndup(mem_ctx,
					  (char *)maj_error_message.value,
					  maj_error_message.length);

	min_error_string = talloc_strndup(mem_ctx,
					  (char *)min_error_message.value,
					  min_error_message.length);

	ret = talloc_asprintf(mem_ctx, "%s: %s",
				maj_error_string, min_error_string);

	talloc_free(maj_error_string);
	talloc_free(min_error_string);

	gss_release_buffer(&disp_min_stat, &maj_error_message);
	gss_release_buffer(&disp_min_stat, &min_error_message);

	return ret;
}