xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c (revision ba5ca68405ba4441c86a6cfc87f4ddcb3565c81d)
112b65585SGordon Ross /*
212b65585SGordon Ross  * This file and its contents are supplied under the terms of the
312b65585SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
412b65585SGordon Ross  * You may only use this file in accordance with the terms of version
512b65585SGordon Ross  * 1.0 of the CDDL.
612b65585SGordon Ross  *
712b65585SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
812b65585SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
912b65585SGordon Ross  * http://www.illumos.org/license/CDDL.
1012b65585SGordon Ross  */
1112b65585SGordon Ross 
1212b65585SGordon Ross /*
13*ba5ca684SMatt Barden  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
1412b65585SGordon Ross  */
1512b65585SGordon Ross 
1612b65585SGordon Ross /*
1712b65585SGordon Ross  * SPNEGO back-end for Kerberos.  See [MS-KILE]
1812b65585SGordon Ross  */
1912b65585SGordon Ross 
2012b65585SGordon Ross #include <sys/types.h>
2112b65585SGordon Ross #include <gssapi/gssapi_ext.h>
2212b65585SGordon Ross #include <gssapi/gssapi_krb5.h>
2312b65585SGordon Ross #include <krb5.h>
2412b65585SGordon Ross #include "smbd.h"
2512b65585SGordon Ross #include "smbd_authsvc.h"
2612b65585SGordon Ross 
2712b65585SGordon Ross /* From krb5/krb/pac.c (should have been exported) */
2812b65585SGordon Ross #define	PAC_LOGON_INFO		1
2912b65585SGordon Ross 
3012b65585SGordon Ross typedef struct krb5ssp_backend {
3112b65585SGordon Ross 	gss_ctx_id_t		be_gssctx;
3212b65585SGordon Ross 	char			*be_username;
3312b65585SGordon Ross 	gss_buffer_desc		be_authz_pac;
3412b65585SGordon Ross 	krb5_context		be_kctx;
3512b65585SGordon Ross 	krb5_pac		be_kpac;
3612b65585SGordon Ross 	krb5_data		be_pac;
3712b65585SGordon Ross } krb5ssp_backend_t;
3812b65585SGordon Ross 
3912b65585SGordon Ross static uint32_t
4012b65585SGordon Ross get_authz_data_pac(
4112b65585SGordon Ross 	gss_ctx_id_t context_handle,
4212b65585SGordon Ross 	gss_buffer_t ad_data);
4312b65585SGordon Ross 
4412b65585SGordon Ross static uint32_t
4512b65585SGordon Ross get_ssnkey(authsvc_context_t *ctx);
4612b65585SGordon Ross 
4712b65585SGordon Ross 
4812b65585SGordon Ross /*
4912b65585SGordon Ross  * Initialize this context for Kerberos, if possible.
5012b65585SGordon Ross  *
5112b65585SGordon Ross  * Should not get here unless libsmb smb_config_get_negtok
5212b65585SGordon Ross  * includes the Kerberos5 Mech OIDs in our spnego hint.
5312b65585SGordon Ross  *
5412b65585SGordon Ross  * Todo: allocate ctx->ctx_backend
5512b65585SGordon Ross  * See: krb5_gss_accept_sec_context()
5612b65585SGordon Ross  */
5712b65585SGordon Ross int
smbd_krb5ssp_init(authsvc_context_t * ctx)5812b65585SGordon Ross smbd_krb5ssp_init(authsvc_context_t *ctx)
5912b65585SGordon Ross {
6012b65585SGordon Ross 	krb5ssp_backend_t *be;
6112b65585SGordon Ross 
6212b65585SGordon Ross 	be = malloc(sizeof (*be));
6312b65585SGordon Ross 	if (be == 0)
6412b65585SGordon Ross 		return (NT_STATUS_NO_MEMORY);
6512b65585SGordon Ross 	bzero(be, sizeof (*be));
6612b65585SGordon Ross 	be->be_gssctx = GSS_C_NO_CONTEXT;
6712b65585SGordon Ross 	ctx->ctx_backend = be;
6812b65585SGordon Ross 
6912b65585SGordon Ross 	return (0);
7012b65585SGordon Ross }
7112b65585SGordon Ross 
7212b65585SGordon Ross /*
7312b65585SGordon Ross  * Todo: free ctx->ctx_backend
7412b65585SGordon Ross  */
7512b65585SGordon Ross void
smbd_krb5ssp_fini(authsvc_context_t * ctx)7612b65585SGordon Ross smbd_krb5ssp_fini(authsvc_context_t *ctx)
7712b65585SGordon Ross {
7812b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
7912b65585SGordon Ross 	uint32_t minor;
8012b65585SGordon Ross 
8112b65585SGordon Ross 	if (be == NULL)
8212b65585SGordon Ross 		return;
8312b65585SGordon Ross 
8412b65585SGordon Ross 	if (be->be_kctx != NULL) {
8512b65585SGordon Ross 		krb5_free_data_contents(be->be_kctx, &be->be_pac);
8612b65585SGordon Ross 
8712b65585SGordon Ross 		if (be->be_kpac != NULL)
8812b65585SGordon Ross 			krb5_pac_free(be->be_kctx, be->be_kpac);
8912b65585SGordon Ross 
9012b65585SGordon Ross 		krb5_free_context(be->be_kctx);
9112b65585SGordon Ross 	}
9212b65585SGordon Ross 
9312b65585SGordon Ross 	(void) gss_release_buffer(NULL, &be->be_authz_pac);
9412b65585SGordon Ross 
9512b65585SGordon Ross 	free(be->be_username);
9612b65585SGordon Ross 
9712b65585SGordon Ross 	if (be->be_gssctx != GSS_C_NO_CONTEXT) {
9812b65585SGordon Ross 		(void) gss_delete_sec_context(&minor, &be->be_gssctx,
9912b65585SGordon Ross 		    GSS_C_NO_BUFFER);
10012b65585SGordon Ross 	}
10112b65585SGordon Ross 
10212b65585SGordon Ross 	free(be);
10312b65585SGordon Ross }
10412b65585SGordon Ross 
105*ba5ca684SMatt Barden static char *krb5ssp_def_username = "Unknown-Kerberos-User";
106*ba5ca684SMatt Barden static char *krb5ssp_def_domain = "Unknown-Domain";
107*ba5ca684SMatt Barden 
10812b65585SGordon Ross /*
10912b65585SGordon Ross  * Handle a Kerberos auth message.
11012b65585SGordon Ross  *
111*ba5ca684SMatt Barden  * State across messages is in ctx->ctx_backend.
112*ba5ca684SMatt Barden  *
113*ba5ca684SMatt Barden  * Equivalent to smbd_user_auth_logon().
11412b65585SGordon Ross  */
11512b65585SGordon Ross int
smbd_krb5ssp_work(authsvc_context_t * ctx)11612b65585SGordon Ross smbd_krb5ssp_work(authsvc_context_t *ctx)
11712b65585SGordon Ross {
11812b65585SGordon Ross 	gss_buffer_desc	intok, outtok;
11912b65585SGordon Ross 	gss_buffer_desc namebuf;
12012b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
12112b65585SGordon Ross 	gss_name_t gname = NULL;
12212b65585SGordon Ross 	OM_uint32 major, minor, ret_flags;
12312b65585SGordon Ross 	gss_OID name_type = GSS_C_NULL_OID;
12412b65585SGordon Ross 	gss_OID mech_type = GSS_C_NULL_OID;
12512b65585SGordon Ross 	krb5_error_code kerr;
12612b65585SGordon Ross 	uint32_t status;
127*ba5ca684SMatt Barden 	smb_token_t *token = NULL;
12812b65585SGordon Ross 
12912b65585SGordon Ross 	intok.length = ctx->ctx_ibodylen;
13012b65585SGordon Ross 	intok.value  = ctx->ctx_ibodybuf;
13112b65585SGordon Ross 	bzero(&outtok, sizeof (gss_buffer_desc));
13212b65585SGordon Ross 	bzero(&namebuf, sizeof (gss_buffer_desc));
13312b65585SGordon Ross 
134*ba5ca684SMatt Barden 	assert(be->be_username == NULL);
135*ba5ca684SMatt Barden 
13612b65585SGordon Ross 	/* Do this early, for error message support. */
13712b65585SGordon Ross 	kerr = krb5_init_context(&be->be_kctx);
13812b65585SGordon Ross 	if (kerr != 0) {
13912b65585SGordon Ross 		smbd_report("krb5ssp, krb5_init_ctx: %s",
14012b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
14112b65585SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
14212b65585SGordon Ross 	}
14312b65585SGordon Ross 
14412b65585SGordon Ross 	major = gss_accept_sec_context(&minor, &be->be_gssctx,
14512b65585SGordon Ross 	    GSS_C_NO_CREDENTIAL, &intok,
14612b65585SGordon Ross 	    GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
14712b65585SGordon Ross 	    &ret_flags, NULL, NULL);
14812b65585SGordon Ross 
14912b65585SGordon Ross 	if (outtok.length == 0)
15012b65585SGordon Ross 		ctx->ctx_obodylen = 0;
15112b65585SGordon Ross 	else if (outtok.length <= ctx->ctx_obodylen) {
15212b65585SGordon Ross 		ctx->ctx_obodylen = outtok.length;
15312b65585SGordon Ross 		(void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
15412b65585SGordon Ross 		free(outtok.value);
15512b65585SGordon Ross 		outtok.value = NULL;
15612b65585SGordon Ross 	} else {
15712b65585SGordon Ross 		free(ctx->ctx_obodybuf);
15812b65585SGordon Ross 		ctx->ctx_obodybuf = outtok.value;
15912b65585SGordon Ross 		ctx->ctx_obodylen = outtok.length;
16012b65585SGordon Ross 		outtok.value = NULL;
16112b65585SGordon Ross 	}
16212b65585SGordon Ross 
16312b65585SGordon Ross 	if (GSS_ERROR(major)) {
16412b65585SGordon Ross 		smbd_report("krb5ssp: gss_accept_sec_context, "
16512b65585SGordon Ross 		    "mech=0x%x, major=0x%x, minor=0x%x",
16612b65585SGordon Ross 		    (int)mech_type, major, minor);
16712b65585SGordon Ross 		smbd_report(" krb5: %s",
16812b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, minor));
169*ba5ca684SMatt Barden 		status = NT_STATUS_WRONG_PASSWORD;
170*ba5ca684SMatt Barden 		goto out;
17112b65585SGordon Ross 	}
17212b65585SGordon Ross 
17312b65585SGordon Ross 	switch (major) {
17412b65585SGordon Ross 	case GSS_S_COMPLETE:
17512b65585SGordon Ross 		break;
17612b65585SGordon Ross 	case GSS_S_CONTINUE_NEEDED:
17712b65585SGordon Ross 		if (outtok.length > 0) {
17812b65585SGordon Ross 			ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
17912b65585SGordon Ross 			/* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
18012b65585SGordon Ross 			return (0);
18112b65585SGordon Ross 		}
182*ba5ca684SMatt Barden 		status = NT_STATUS_WRONG_PASSWORD;
183*ba5ca684SMatt Barden 		goto out;
18412b65585SGordon Ross 	default:
185*ba5ca684SMatt Barden 		status = NT_STATUS_WRONG_PASSWORD;
186*ba5ca684SMatt Barden 		goto out;
18712b65585SGordon Ross 	}
18812b65585SGordon Ross 
18912b65585SGordon Ross 	/*
19012b65585SGordon Ross 	 * OK, we got GSS_S_COMPLETE.  Get the name so we can use it
19112b65585SGordon Ross 	 * in log messages if we get failures decoding the PAC etc.
19212b65585SGordon Ross 	 * Then get the PAC, decode it, build the logon token.
19312b65585SGordon Ross 	 */
19412b65585SGordon Ross 
19512b65585SGordon Ross 	if (gname != NULL && GSS_S_COMPLETE ==
19612b65585SGordon Ross 	    gss_display_name(&minor, gname, &namebuf, &name_type)) {
19712b65585SGordon Ross 		/* Save the user name. */
19812b65585SGordon Ross 		be->be_username = strdup(namebuf.value);
19912b65585SGordon Ross 		(void) gss_release_buffer(&minor, &namebuf);
20012b65585SGordon Ross 		(void) gss_release_name(&minor, &gname);
20112b65585SGordon Ross 		if (be->be_username == NULL) {
20212b65585SGordon Ross 			return (NT_STATUS_NO_MEMORY);
20312b65585SGordon Ross 		}
20412b65585SGordon Ross 	}
20512b65585SGordon Ross 
20612b65585SGordon Ross 	/*
20712b65585SGordon Ross 	 * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
20812b65585SGordon Ross 	 */
20912b65585SGordon Ross 	status = get_authz_data_pac(be->be_gssctx,
21012b65585SGordon Ross 	    &be->be_authz_pac);
21112b65585SGordon Ross 	if (status)
212*ba5ca684SMatt Barden 		goto out;
21312b65585SGordon Ross 
21412b65585SGordon Ross 	kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
21512b65585SGordon Ross 	    be->be_authz_pac.length, &be->be_kpac);
21612b65585SGordon Ross 	if (kerr) {
21712b65585SGordon Ross 		smbd_report("krb5ssp, krb5_pac_parse: %s",
21812b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
219*ba5ca684SMatt Barden 		status = NT_STATUS_UNSUCCESSFUL;
220*ba5ca684SMatt Barden 		goto out;
22112b65585SGordon Ross 	}
22212b65585SGordon Ross 
22312b65585SGordon Ross 	kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
22412b65585SGordon Ross 	    PAC_LOGON_INFO, &be->be_pac);
22512b65585SGordon Ross 	if (kerr) {
22612b65585SGordon Ross 		smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
22712b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
228*ba5ca684SMatt Barden 		status = NT_STATUS_UNSUCCESSFUL;
229*ba5ca684SMatt Barden 		goto out;
23012b65585SGordon Ross 	}
23112b65585SGordon Ross 
23212b65585SGordon Ross 	ctx->ctx_token = calloc(1, sizeof (smb_token_t));
233*ba5ca684SMatt Barden 	if (ctx->ctx_token == NULL) {
234*ba5ca684SMatt Barden 		status = NT_STATUS_NO_MEMORY;
235*ba5ca684SMatt Barden 		goto out;
236*ba5ca684SMatt Barden 	}
23712b65585SGordon Ross 
23812b65585SGordon Ross 	status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
23912b65585SGordon Ross 	    be->be_pac.length);
24012b65585SGordon Ross 	if (status)
241*ba5ca684SMatt Barden 		goto out;
24212b65585SGordon Ross 
24312b65585SGordon Ross 	status = get_ssnkey(ctx);
24412b65585SGordon Ross 	if (status)
245*ba5ca684SMatt Barden 		goto out;
24612b65585SGordon Ross 
247*ba5ca684SMatt Barden 	if (!smb_token_setup_common(ctx->ctx_token)) {
248*ba5ca684SMatt Barden 		status = NT_STATUS_UNSUCCESSFUL;
249*ba5ca684SMatt Barden 		goto out;
250*ba5ca684SMatt Barden 	}
25112b65585SGordon Ross 
25212b65585SGordon Ross 	/* Success! */
25312b65585SGordon Ross 	ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
25412b65585SGordon Ross 
255*ba5ca684SMatt Barden 	status = 0;
256*ba5ca684SMatt Barden 	token = ctx->ctx_token;
257*ba5ca684SMatt Barden 
258*ba5ca684SMatt Barden 	/*
259*ba5ca684SMatt Barden 	 * Before we return, audit successful and failed logons.
260*ba5ca684SMatt Barden 	 *
261*ba5ca684SMatt Barden 	 * Successful logons are audited using the username and domain
262*ba5ca684SMatt Barden 	 * contained in the ticket (where the domain comes from the PAC data).
263*ba5ca684SMatt Barden 	 *
264*ba5ca684SMatt Barden 	 * Failed logins use a 'default' domain. If we fail after obtaining
265*ba5ca684SMatt Barden 	 * the username in the ticket, we audit under that username.
266*ba5ca684SMatt Barden 	 *
267*ba5ca684SMatt Barden 	 * Prior to decoding the username, we only audit failures where we'll
268*ba5ca684SMatt Barden 	 * return NT_STATUS_WRONG_PASSWORD, so that we audit attempts with
269*ba5ca684SMatt Barden 	 * invalid (or forged) tickets. These records use a 'default' username;
270*ba5ca684SMatt Barden 	 * As such, they serve only to inform an administrator that
271*ba5ca684SMatt Barden 	 * a particular client used a bad ticket, but does not contain any
272*ba5ca684SMatt Barden 	 * information on the ticket itself.
273*ba5ca684SMatt Barden 	 *
274*ba5ca684SMatt Barden 	 * Once we have a username, we'll audit all failed authentications.
275*ba5ca684SMatt Barden 	 */
276*ba5ca684SMatt Barden out:
277*ba5ca684SMatt Barden 	status = smbd_logon_final(token, &ctx->ctx_clinfo.lci_clnt_ipaddr,
278*ba5ca684SMatt Barden 	    (be->be_username != NULL) ? be->be_username : krb5ssp_def_username,
279*ba5ca684SMatt Barden 	    krb5ssp_def_domain, status);
280*ba5ca684SMatt Barden 
281*ba5ca684SMatt Barden 	return (status);
28212b65585SGordon Ross }
28312b65585SGordon Ross 
28412b65585SGordon Ross /*
28512b65585SGordon Ross  * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
28612b65585SGordon Ross  * and: KRB5_AUTHDATA_WIN2K_PAC
28712b65585SGordon Ross  */
28812b65585SGordon Ross static const gss_OID_desc
28912b65585SGordon Ross oid_ex_authz_data_pac = {
29012b65585SGordon Ross 	13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
29112b65585SGordon Ross 
29212b65585SGordon Ross /*
29312b65585SGordon Ross  * See: krb5_gss_inquire_sec_context_by_oid()
29412b65585SGordon Ross  * and krb5_gss_inquire_sec_context_by_oid_ops[],
29512b65585SGordon Ross  * gss_krb5int_extract_authz_data_from_sec_context()
29612b65585SGordon Ross  */
29712b65585SGordon Ross static uint32_t
get_authz_data_pac(gss_ctx_id_t context_handle,gss_buffer_t ad_data)29812b65585SGordon Ross get_authz_data_pac(
29912b65585SGordon Ross 	gss_ctx_id_t context_handle,
30012b65585SGordon Ross 	gss_buffer_t ad_data)
30112b65585SGordon Ross {
30212b65585SGordon Ross 	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
30312b65585SGordon Ross 	OM_uint32 major, minor;
30412b65585SGordon Ross 	uint32_t status = NT_STATUS_UNSUCCESSFUL;
30512b65585SGordon Ross 
30612b65585SGordon Ross 	if (ad_data == NULL)
30712b65585SGordon Ross 		goto out;
30812b65585SGordon Ross 
30912b65585SGordon Ross 	major = gss_inquire_sec_context_by_oid(
31012b65585SGordon Ross 	    &minor,
31112b65585SGordon Ross 	    context_handle,
31212b65585SGordon Ross 	    (gss_OID)&oid_ex_authz_data_pac,
31312b65585SGordon Ross 	    &data_set);
31412b65585SGordon Ross 	if (GSS_ERROR(major)) {
31512b65585SGordon Ross 		smbd_report("krb5ssp, gss_inquire...PAC, "
31612b65585SGordon Ross 		    "major=0x%x, minor=0x%x", major, minor);
31712b65585SGordon Ross 		goto out;
31812b65585SGordon Ross 	}
31912b65585SGordon Ross 
32012b65585SGordon Ross 	if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) {
32112b65585SGordon Ross 		goto out;
32212b65585SGordon Ross 	}
32312b65585SGordon Ross 
32412b65585SGordon Ross 	/* Only need the first element? */
32512b65585SGordon Ross 	ad_data->length = data_set->elements[0].length;
32612b65585SGordon Ross 	ad_data->value = malloc(ad_data->length);
32712b65585SGordon Ross 	if (ad_data->value == NULL) {
32812b65585SGordon Ross 		status = NT_STATUS_NO_MEMORY;
32912b65585SGordon Ross 		goto out;
33012b65585SGordon Ross 	}
33112b65585SGordon Ross 	bcopy(data_set->elements[0].value, ad_data->value, ad_data->length);
33212b65585SGordon Ross 	status = 0;
33312b65585SGordon Ross 
33412b65585SGordon Ross out:
33512b65585SGordon Ross 	(void) gss_release_buffer_set(&minor, &data_set);
33612b65585SGordon Ross 
33712b65585SGordon Ross 	return (status);
33812b65585SGordon Ross }
33912b65585SGordon Ross 
34012b65585SGordon Ross /*
34112b65585SGordon Ross  * Get the session key, and save it in the token.
34212b65585SGordon Ross  *
34312b65585SGordon Ross  * See: krb5_gss_inquire_sec_context_by_oid(),
34412b65585SGordon Ross  * krb5_gss_inquire_sec_context_by_oid_ops[], and
34512b65585SGordon Ross  * gss_krb5int_inq_session_key
34612b65585SGordon Ross  */
34712b65585SGordon Ross static uint32_t
get_ssnkey(authsvc_context_t * ctx)34812b65585SGordon Ross get_ssnkey(authsvc_context_t *ctx)
34912b65585SGordon Ross {
35012b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
35112b65585SGordon Ross 	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
35212b65585SGordon Ross 	OM_uint32 major, minor;
35312b65585SGordon Ross 	size_t keylen;
35412b65585SGordon Ross 	uint32_t status = NT_STATUS_UNSUCCESSFUL;
35512b65585SGordon Ross 
35612b65585SGordon Ross 	major = gss_inquire_sec_context_by_oid(&minor,
35712b65585SGordon Ross 	    be->be_gssctx, GSS_C_INQ_SSPI_SESSION_KEY, &data_set);
35812b65585SGordon Ross 	if (GSS_ERROR(major)) {
35912b65585SGordon Ross 		smbd_report("krb5ssp, failed to get session key, "
36012b65585SGordon Ross 		    "major=0x%x, minor=0x%x", major, minor);
36112b65585SGordon Ross 		goto out;
36212b65585SGordon Ross 	}
36312b65585SGordon Ross 
36412b65585SGordon Ross 	/*
36512b65585SGordon Ross 	 * The key is in the first element
36612b65585SGordon Ross 	 */
36712b65585SGordon Ross 	if (data_set == GSS_C_NO_BUFFER_SET ||
36812b65585SGordon Ross 	    data_set->count == 0 ||
36912b65585SGordon Ross 	    data_set->elements[0].length == 0 ||
37012b65585SGordon Ross 	    data_set->elements[0].value == NULL) {
37112b65585SGordon Ross 		smbd_report("krb5ssp: Session key is missing");
37212b65585SGordon Ross 		goto out;
37312b65585SGordon Ross 	}
37412b65585SGordon Ross 	if ((keylen = data_set->elements[0].length) < SMBAUTH_HASH_SZ) {
37512b65585SGordon Ross 		smbd_report("krb5ssp: Session key too short (%d)",
37612b65585SGordon Ross 		    data_set->elements[0].length);
37712b65585SGordon Ross 		goto out;
37812b65585SGordon Ross 	}
37912b65585SGordon Ross 
38012b65585SGordon Ross 	ctx->ctx_token->tkn_ssnkey.val = malloc(keylen);
38112b65585SGordon Ross 	if (ctx->ctx_token->tkn_ssnkey.val == NULL) {
38212b65585SGordon Ross 		status = NT_STATUS_NO_MEMORY;
38312b65585SGordon Ross 		goto out;
38412b65585SGordon Ross 	}
38512b65585SGordon Ross 	ctx->ctx_token->tkn_ssnkey.len = keylen;
38612b65585SGordon Ross 	bcopy(data_set->elements[0].value,
38712b65585SGordon Ross 	    ctx->ctx_token->tkn_ssnkey.val, keylen);
38812b65585SGordon Ross 	status = 0;
38912b65585SGordon Ross 
39012b65585SGordon Ross out:
39112b65585SGordon Ross 	(void) gss_release_buffer_set(&minor, &data_set);
39212b65585SGordon Ross 	return (status);
39312b65585SGordon Ross }
394