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
smbd_krb5ssp_init(authsvc_context_t * ctx)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
smbd_krb5ssp_fini(authsvc_context_t * ctx)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
smbd_krb5ssp_work(authsvc_context_t * ctx)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
get_authz_data_pac(gss_ctx_id_t context_handle,gss_buffer_t ad_data)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
get_ssnkey(authsvc_context_t * ctx)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