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