xref: /titanic_51/usr/src/cmd/smbsrv/smbd/smbd_krb5ssp.c (revision 12b65585e720714b31036daaa2b30eb76014048e)
1*12b65585SGordon Ross /*
2*12b65585SGordon Ross  * This file and its contents are supplied under the terms of the
3*12b65585SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
4*12b65585SGordon Ross  * You may only use this file in accordance with the terms of version
5*12b65585SGordon Ross  * 1.0 of the CDDL.
6*12b65585SGordon Ross  *
7*12b65585SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
8*12b65585SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
9*12b65585SGordon Ross  * http://www.illumos.org/license/CDDL.
10*12b65585SGordon Ross  */
11*12b65585SGordon Ross 
12*12b65585SGordon Ross /*
13*12b65585SGordon Ross  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
14*12b65585SGordon Ross  */
15*12b65585SGordon Ross 
16*12b65585SGordon Ross /*
17*12b65585SGordon Ross  * SPNEGO back-end for Kerberos.  See [MS-KILE]
18*12b65585SGordon Ross  */
19*12b65585SGordon Ross 
20*12b65585SGordon Ross #include <sys/types.h>
21*12b65585SGordon Ross #include <gssapi/gssapi_ext.h>
22*12b65585SGordon Ross #include <gssapi/gssapi_krb5.h>
23*12b65585SGordon Ross #include <krb5.h>
24*12b65585SGordon Ross #include "smbd.h"
25*12b65585SGordon Ross #include "smbd_authsvc.h"
26*12b65585SGordon Ross 
27*12b65585SGordon Ross /* From krb5/krb/pac.c (should have been exported) */
28*12b65585SGordon Ross #define	PAC_LOGON_INFO		1
29*12b65585SGordon Ross 
30*12b65585SGordon Ross typedef struct krb5ssp_backend {
31*12b65585SGordon Ross 	gss_ctx_id_t		be_gssctx;
32*12b65585SGordon Ross 	char			*be_username;
33*12b65585SGordon Ross 	gss_buffer_desc		be_authz_pac;
34*12b65585SGordon Ross 	krb5_context		be_kctx;
35*12b65585SGordon Ross 	krb5_pac		be_kpac;
36*12b65585SGordon Ross 	krb5_data		be_pac;
37*12b65585SGordon Ross } krb5ssp_backend_t;
38*12b65585SGordon Ross 
39*12b65585SGordon Ross static uint32_t
40*12b65585SGordon Ross get_authz_data_pac(
41*12b65585SGordon Ross 	gss_ctx_id_t context_handle,
42*12b65585SGordon Ross 	gss_buffer_t ad_data);
43*12b65585SGordon Ross 
44*12b65585SGordon Ross static uint32_t
45*12b65585SGordon Ross get_ssnkey(authsvc_context_t *ctx);
46*12b65585SGordon Ross 
47*12b65585SGordon Ross 
48*12b65585SGordon Ross /*
49*12b65585SGordon Ross  * Initialize this context for Kerberos, if possible.
50*12b65585SGordon Ross  *
51*12b65585SGordon Ross  * Should not get here unless libsmb smb_config_get_negtok
52*12b65585SGordon Ross  * includes the Kerberos5 Mech OIDs in our spnego hint.
53*12b65585SGordon Ross  *
54*12b65585SGordon Ross  * Todo: allocate ctx->ctx_backend
55*12b65585SGordon Ross  * See: krb5_gss_accept_sec_context()
56*12b65585SGordon Ross  */
57*12b65585SGordon Ross int
58*12b65585SGordon Ross smbd_krb5ssp_init(authsvc_context_t *ctx)
59*12b65585SGordon Ross {
60*12b65585SGordon Ross 	krb5ssp_backend_t *be;
61*12b65585SGordon Ross 
62*12b65585SGordon Ross 	be = malloc(sizeof (*be));
63*12b65585SGordon Ross 	if (be == 0)
64*12b65585SGordon Ross 		return (NT_STATUS_NO_MEMORY);
65*12b65585SGordon Ross 	bzero(be, sizeof (*be));
66*12b65585SGordon Ross 	be->be_gssctx = GSS_C_NO_CONTEXT;
67*12b65585SGordon Ross 	ctx->ctx_backend = be;
68*12b65585SGordon Ross 
69*12b65585SGordon Ross 	return (0);
70*12b65585SGordon Ross }
71*12b65585SGordon Ross 
72*12b65585SGordon Ross /*
73*12b65585SGordon Ross  * Todo: free ctx->ctx_backend
74*12b65585SGordon Ross  */
75*12b65585SGordon Ross void
76*12b65585SGordon Ross smbd_krb5ssp_fini(authsvc_context_t *ctx)
77*12b65585SGordon Ross {
78*12b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
79*12b65585SGordon Ross 	uint32_t minor;
80*12b65585SGordon Ross 
81*12b65585SGordon Ross 	if (be == NULL)
82*12b65585SGordon Ross 		return;
83*12b65585SGordon Ross 
84*12b65585SGordon Ross 	if (be->be_kctx != NULL) {
85*12b65585SGordon Ross 		krb5_free_data_contents(be->be_kctx, &be->be_pac);
86*12b65585SGordon Ross 
87*12b65585SGordon Ross 		if (be->be_kpac != NULL)
88*12b65585SGordon Ross 			krb5_pac_free(be->be_kctx, be->be_kpac);
89*12b65585SGordon Ross 
90*12b65585SGordon Ross 		krb5_free_context(be->be_kctx);
91*12b65585SGordon Ross 	}
92*12b65585SGordon Ross 
93*12b65585SGordon Ross 	(void) gss_release_buffer(NULL, &be->be_authz_pac);
94*12b65585SGordon Ross 
95*12b65585SGordon Ross 	free(be->be_username);
96*12b65585SGordon Ross 
97*12b65585SGordon Ross 	if (be->be_gssctx != GSS_C_NO_CONTEXT) {
98*12b65585SGordon Ross 		(void) gss_delete_sec_context(&minor, &be->be_gssctx,
99*12b65585SGordon Ross 		    GSS_C_NO_BUFFER);
100*12b65585SGordon Ross 	}
101*12b65585SGordon Ross 
102*12b65585SGordon Ross 	free(be);
103*12b65585SGordon Ross }
104*12b65585SGordon Ross 
105*12b65585SGordon Ross /*
106*12b65585SGordon Ross  * Handle a Kerberos auth message.
107*12b65585SGordon Ross  *
108*12b65585SGordon Ross  * State across messages is in ctx->ctx_backend
109*12b65585SGordon Ross  */
110*12b65585SGordon Ross int
111*12b65585SGordon Ross smbd_krb5ssp_work(authsvc_context_t *ctx)
112*12b65585SGordon Ross {
113*12b65585SGordon Ross 	gss_buffer_desc	intok, outtok;
114*12b65585SGordon Ross 	gss_buffer_desc namebuf;
115*12b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
116*12b65585SGordon Ross 	gss_name_t gname = NULL;
117*12b65585SGordon Ross 	OM_uint32 major, minor, ret_flags;
118*12b65585SGordon Ross 	gss_OID name_type = GSS_C_NULL_OID;
119*12b65585SGordon Ross 	gss_OID mech_type = GSS_C_NULL_OID;
120*12b65585SGordon Ross 	krb5_error_code kerr;
121*12b65585SGordon Ross 	uint32_t status;
122*12b65585SGordon Ross 
123*12b65585SGordon Ross 	intok.length = ctx->ctx_ibodylen;
124*12b65585SGordon Ross 	intok.value  = ctx->ctx_ibodybuf;
125*12b65585SGordon Ross 	bzero(&outtok, sizeof (gss_buffer_desc));
126*12b65585SGordon Ross 	bzero(&namebuf, sizeof (gss_buffer_desc));
127*12b65585SGordon Ross 
128*12b65585SGordon Ross 	/* Do this early, for error message support. */
129*12b65585SGordon Ross 	kerr = krb5_init_context(&be->be_kctx);
130*12b65585SGordon Ross 	if (kerr != 0) {
131*12b65585SGordon Ross 		smbd_report("krb5ssp, krb5_init_ctx: %s",
132*12b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
133*12b65585SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
134*12b65585SGordon Ross 	}
135*12b65585SGordon Ross 
136*12b65585SGordon Ross 	major = gss_accept_sec_context(&minor, &be->be_gssctx,
137*12b65585SGordon Ross 	    GSS_C_NO_CREDENTIAL, &intok,
138*12b65585SGordon Ross 	    GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
139*12b65585SGordon Ross 	    &ret_flags, NULL, NULL);
140*12b65585SGordon Ross 
141*12b65585SGordon Ross 	if (outtok.length == 0)
142*12b65585SGordon Ross 		ctx->ctx_obodylen = 0;
143*12b65585SGordon Ross 	else if (outtok.length <= ctx->ctx_obodylen) {
144*12b65585SGordon Ross 		ctx->ctx_obodylen = outtok.length;
145*12b65585SGordon Ross 		(void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
146*12b65585SGordon Ross 		free(outtok.value);
147*12b65585SGordon Ross 		outtok.value = NULL;
148*12b65585SGordon Ross 	} else {
149*12b65585SGordon Ross 		free(ctx->ctx_obodybuf);
150*12b65585SGordon Ross 		ctx->ctx_obodybuf = outtok.value;
151*12b65585SGordon Ross 		ctx->ctx_obodylen = outtok.length;
152*12b65585SGordon Ross 		outtok.value = NULL;
153*12b65585SGordon Ross 	}
154*12b65585SGordon Ross 
155*12b65585SGordon Ross 	if (GSS_ERROR(major)) {
156*12b65585SGordon Ross 		smbd_report("krb5ssp: gss_accept_sec_context, "
157*12b65585SGordon Ross 		    "mech=0x%x, major=0x%x, minor=0x%x",
158*12b65585SGordon Ross 		    (int)mech_type, major, minor);
159*12b65585SGordon Ross 		smbd_report(" krb5: %s",
160*12b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, minor));
161*12b65585SGordon Ross 		return (NT_STATUS_WRONG_PASSWORD);
162*12b65585SGordon Ross 	}
163*12b65585SGordon Ross 
164*12b65585SGordon Ross 	switch (major) {
165*12b65585SGordon Ross 	case GSS_S_COMPLETE:
166*12b65585SGordon Ross 		break;
167*12b65585SGordon Ross 	case GSS_S_CONTINUE_NEEDED:
168*12b65585SGordon Ross 		if (outtok.length > 0) {
169*12b65585SGordon Ross 			ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
170*12b65585SGordon Ross 			/* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
171*12b65585SGordon Ross 			return (0);
172*12b65585SGordon Ross 		}
173*12b65585SGordon Ross 		return (NT_STATUS_WRONG_PASSWORD);
174*12b65585SGordon Ross 	default:
175*12b65585SGordon Ross 		return (NT_STATUS_WRONG_PASSWORD);
176*12b65585SGordon Ross 	}
177*12b65585SGordon Ross 
178*12b65585SGordon Ross 	/*
179*12b65585SGordon Ross 	 * OK, we got GSS_S_COMPLETE.  Get the name so we can use it
180*12b65585SGordon Ross 	 * in log messages if we get failures decoding the PAC etc.
181*12b65585SGordon Ross 	 * Then get the PAC, decode it, build the logon token.
182*12b65585SGordon Ross 	 */
183*12b65585SGordon Ross 
184*12b65585SGordon Ross 	if (gname != NULL && GSS_S_COMPLETE ==
185*12b65585SGordon Ross 	    gss_display_name(&minor, gname, &namebuf, &name_type)) {
186*12b65585SGordon Ross 		/* Save the user name. */
187*12b65585SGordon Ross 		be->be_username = strdup(namebuf.value);
188*12b65585SGordon Ross 		(void) gss_release_buffer(&minor, &namebuf);
189*12b65585SGordon Ross 		(void) gss_release_name(&minor, &gname);
190*12b65585SGordon Ross 		if (be->be_username == NULL) {
191*12b65585SGordon Ross 			return (NT_STATUS_NO_MEMORY);
192*12b65585SGordon Ross 		}
193*12b65585SGordon Ross 	}
194*12b65585SGordon Ross 
195*12b65585SGordon Ross 	/*
196*12b65585SGordon Ross 	 * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
197*12b65585SGordon Ross 	 */
198*12b65585SGordon Ross 	status = get_authz_data_pac(be->be_gssctx,
199*12b65585SGordon Ross 	    &be->be_authz_pac);
200*12b65585SGordon Ross 	if (status)
201*12b65585SGordon Ross 		return (status);
202*12b65585SGordon Ross 
203*12b65585SGordon Ross 	kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
204*12b65585SGordon Ross 	    be->be_authz_pac.length, &be->be_kpac);
205*12b65585SGordon Ross 	if (kerr) {
206*12b65585SGordon Ross 		smbd_report("krb5ssp, krb5_pac_parse: %s",
207*12b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
208*12b65585SGordon Ross 		return (NT_STATUS_UNSUCCESSFUL);
209*12b65585SGordon Ross 	}
210*12b65585SGordon Ross 
211*12b65585SGordon Ross 	kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
212*12b65585SGordon Ross 	    PAC_LOGON_INFO, &be->be_pac);
213*12b65585SGordon Ross 	if (kerr) {
214*12b65585SGordon Ross 		smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
215*12b65585SGordon Ross 		    krb5_get_error_message(be->be_kctx, kerr));
216*12b65585SGordon Ross 		return (NT_STATUS_UNSUCCESSFUL);
217*12b65585SGordon Ross 	}
218*12b65585SGordon Ross 
219*12b65585SGordon Ross 	ctx->ctx_token = calloc(1, sizeof (smb_token_t));
220*12b65585SGordon Ross 	if (ctx->ctx_token == NULL)
221*12b65585SGordon Ross 		return (NT_STATUS_NO_MEMORY);
222*12b65585SGordon Ross 
223*12b65585SGordon Ross 	status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
224*12b65585SGordon Ross 	    be->be_pac.length);
225*12b65585SGordon Ross 	if (status)
226*12b65585SGordon Ross 		return (status);
227*12b65585SGordon Ross 
228*12b65585SGordon Ross 	status = get_ssnkey(ctx);
229*12b65585SGordon Ross 	if (status)
230*12b65585SGordon Ross 		return (status);
231*12b65585SGordon Ross 
232*12b65585SGordon Ross 	if (!smb_token_setup_common(ctx->ctx_token))
233*12b65585SGordon Ross 		return (NT_STATUS_UNSUCCESSFUL);
234*12b65585SGordon Ross 
235*12b65585SGordon Ross 	/* Success! */
236*12b65585SGordon Ross 	ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
237*12b65585SGordon Ross 
238*12b65585SGordon Ross 	return (0);
239*12b65585SGordon Ross }
240*12b65585SGordon Ross 
241*12b65585SGordon Ross /*
242*12b65585SGordon Ross  * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
243*12b65585SGordon Ross  * and: KRB5_AUTHDATA_WIN2K_PAC
244*12b65585SGordon Ross  */
245*12b65585SGordon Ross static const gss_OID_desc
246*12b65585SGordon Ross oid_ex_authz_data_pac = {
247*12b65585SGordon Ross 	13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
248*12b65585SGordon Ross 
249*12b65585SGordon Ross /*
250*12b65585SGordon Ross  * See: krb5_gss_inquire_sec_context_by_oid()
251*12b65585SGordon Ross  * and krb5_gss_inquire_sec_context_by_oid_ops[],
252*12b65585SGordon Ross  * gss_krb5int_extract_authz_data_from_sec_context()
253*12b65585SGordon Ross  */
254*12b65585SGordon Ross static uint32_t
255*12b65585SGordon Ross get_authz_data_pac(
256*12b65585SGordon Ross 	gss_ctx_id_t context_handle,
257*12b65585SGordon Ross 	gss_buffer_t ad_data)
258*12b65585SGordon Ross {
259*12b65585SGordon Ross 	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
260*12b65585SGordon Ross 	OM_uint32 major, minor;
261*12b65585SGordon Ross 	uint32_t status = NT_STATUS_UNSUCCESSFUL;
262*12b65585SGordon Ross 
263*12b65585SGordon Ross 	if (ad_data == NULL)
264*12b65585SGordon Ross 		goto out;
265*12b65585SGordon Ross 
266*12b65585SGordon Ross 	major = gss_inquire_sec_context_by_oid(
267*12b65585SGordon Ross 	    &minor,
268*12b65585SGordon Ross 	    context_handle,
269*12b65585SGordon Ross 	    (gss_OID)&oid_ex_authz_data_pac,
270*12b65585SGordon Ross 	    &data_set);
271*12b65585SGordon Ross 	if (GSS_ERROR(major)) {
272*12b65585SGordon Ross 		smbd_report("krb5ssp, gss_inquire...PAC, "
273*12b65585SGordon Ross 		    "major=0x%x, minor=0x%x", major, minor);
274*12b65585SGordon Ross 		goto out;
275*12b65585SGordon Ross 	}
276*12b65585SGordon Ross 
277*12b65585SGordon Ross 	if ((data_set == GSS_C_NO_BUFFER_SET) || (data_set->count == 0)) {
278*12b65585SGordon Ross 		goto out;
279*12b65585SGordon Ross 	}
280*12b65585SGordon Ross 
281*12b65585SGordon Ross 	/* Only need the first element? */
282*12b65585SGordon Ross 	ad_data->length = data_set->elements[0].length;
283*12b65585SGordon Ross 	ad_data->value = malloc(ad_data->length);
284*12b65585SGordon Ross 	if (ad_data->value == NULL) {
285*12b65585SGordon Ross 		status = NT_STATUS_NO_MEMORY;
286*12b65585SGordon Ross 		goto out;
287*12b65585SGordon Ross 	}
288*12b65585SGordon Ross 	bcopy(data_set->elements[0].value, ad_data->value, ad_data->length);
289*12b65585SGordon Ross 	status = 0;
290*12b65585SGordon Ross 
291*12b65585SGordon Ross out:
292*12b65585SGordon Ross 	(void) gss_release_buffer_set(&minor, &data_set);
293*12b65585SGordon Ross 
294*12b65585SGordon Ross 	return (status);
295*12b65585SGordon Ross }
296*12b65585SGordon Ross 
297*12b65585SGordon Ross /*
298*12b65585SGordon Ross  * Get the session key, and save it in the token.
299*12b65585SGordon Ross  *
300*12b65585SGordon Ross  * See: krb5_gss_inquire_sec_context_by_oid(),
301*12b65585SGordon Ross  * krb5_gss_inquire_sec_context_by_oid_ops[], and
302*12b65585SGordon Ross  * gss_krb5int_inq_session_key
303*12b65585SGordon Ross  */
304*12b65585SGordon Ross static uint32_t
305*12b65585SGordon Ross get_ssnkey(authsvc_context_t *ctx)
306*12b65585SGordon Ross {
307*12b65585SGordon Ross 	krb5ssp_backend_t *be = ctx->ctx_backend;
308*12b65585SGordon Ross 	gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
309*12b65585SGordon Ross 	OM_uint32 major, minor;
310*12b65585SGordon Ross 	size_t keylen;
311*12b65585SGordon Ross 	uint32_t status = NT_STATUS_UNSUCCESSFUL;
312*12b65585SGordon Ross 
313*12b65585SGordon Ross 	major = gss_inquire_sec_context_by_oid(&minor,
314*12b65585SGordon Ross 	    be->be_gssctx, GSS_C_INQ_SSPI_SESSION_KEY, &data_set);
315*12b65585SGordon Ross 	if (GSS_ERROR(major)) {
316*12b65585SGordon Ross 		smbd_report("krb5ssp, failed to get session key, "
317*12b65585SGordon Ross 		    "major=0x%x, minor=0x%x", major, minor);
318*12b65585SGordon Ross 		goto out;
319*12b65585SGordon Ross 	}
320*12b65585SGordon Ross 
321*12b65585SGordon Ross 	/*
322*12b65585SGordon Ross 	 * The key is in the first element
323*12b65585SGordon Ross 	 */
324*12b65585SGordon Ross 	if (data_set == GSS_C_NO_BUFFER_SET ||
325*12b65585SGordon Ross 	    data_set->count == 0 ||
326*12b65585SGordon Ross 	    data_set->elements[0].length == 0 ||
327*12b65585SGordon Ross 	    data_set->elements[0].value == NULL) {
328*12b65585SGordon Ross 		smbd_report("krb5ssp: Session key is missing");
329*12b65585SGordon Ross 		goto out;
330*12b65585SGordon Ross 	}
331*12b65585SGordon Ross 	if ((keylen = data_set->elements[0].length) < SMBAUTH_HASH_SZ) {
332*12b65585SGordon Ross 		smbd_report("krb5ssp: Session key too short (%d)",
333*12b65585SGordon Ross 		    data_set->elements[0].length);
334*12b65585SGordon Ross 		goto out;
335*12b65585SGordon Ross 	}
336*12b65585SGordon Ross 
337*12b65585SGordon Ross 	ctx->ctx_token->tkn_ssnkey.val = malloc(keylen);
338*12b65585SGordon Ross 	if (ctx->ctx_token->tkn_ssnkey.val == NULL) {
339*12b65585SGordon Ross 		status = NT_STATUS_NO_MEMORY;
340*12b65585SGordon Ross 		goto out;
341*12b65585SGordon Ross 	}
342*12b65585SGordon Ross 	ctx->ctx_token->tkn_ssnkey.len = keylen;
343*12b65585SGordon Ross 	bcopy(data_set->elements[0].value,
344*12b65585SGordon Ross 	    ctx->ctx_token->tkn_ssnkey.val, keylen);
345*12b65585SGordon Ross 	status = 0;
346*12b65585SGordon Ross 
347*12b65585SGordon Ross out:
348*12b65585SGordon Ross 	(void) gss_release_buffer_set(&minor, &data_set);
349*12b65585SGordon Ross 	return (status);
350*12b65585SGordon Ross }
351