Print this page
NEX-18907 File Access Auditing does not work with SMB Kerberos authentication
Review by: Gordon Ross <gordon.ross@nexenta.com>
Review by: Evan Layton <evan.layton@nexenta.com>
NEX-3080 SMB1 signing problem with Kerberos auth.
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-1810 extended security Kerberos (inbound)
SMB-56 extended security NTLMSSP, inbound
   1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * SPNEGO back-end for Kerberos.  See [MS-KILE]
  18  */
  19 
  20 #include <sys/types.h>
  21 #include <gssapi/gssapi_ext.h>
  22 #include <gssapi/gssapi_krb5.h>
  23 #include <krb5.h>
  24 #include "smbd.h"
  25 #include "smbd_authsvc.h"
  26 
  27 /* From krb5/krb/pac.c (should have been exported) */
  28 #define PAC_LOGON_INFO          1
  29 
  30 typedef struct krb5ssp_backend {
  31         gss_ctx_id_t            be_gssctx;
  32         char                    *be_username;
  33         gss_buffer_desc         be_authz_pac;


 102         free(be);
 103 }
 104 
 105 /*
 106  * Handle a Kerberos auth message.
 107  *
 108  * State across messages is in ctx->ctx_backend
 109  */
 110 int
 111 smbd_krb5ssp_work(authsvc_context_t *ctx)
 112 {
 113         gss_buffer_desc intok, outtok;
 114         gss_buffer_desc namebuf;
 115         krb5ssp_backend_t *be = ctx->ctx_backend;
 116         gss_name_t gname = NULL;
 117         OM_uint32 major, minor, ret_flags;
 118         gss_OID name_type = GSS_C_NULL_OID;
 119         gss_OID mech_type = GSS_C_NULL_OID;
 120         krb5_error_code kerr;
 121         uint32_t status;

 122 
 123         intok.length = ctx->ctx_ibodylen;
 124         intok.value  = ctx->ctx_ibodybuf;
 125         bzero(&outtok, sizeof (gss_buffer_desc));
 126         bzero(&namebuf, sizeof (gss_buffer_desc));
 127 
 128         /* Do this early, for error message support. */
 129         kerr = krb5_init_context(&be->be_kctx);
 130         if (kerr != 0) {
 131                 smbd_report("krb5ssp, krb5_init_ctx: %s",
 132                     krb5_get_error_message(be->be_kctx, kerr));
 133                 return (NT_STATUS_INTERNAL_ERROR);
 134         }
 135 



 136         major = gss_accept_sec_context(&minor, &be->be_gssctx,
 137             GSS_C_NO_CREDENTIAL, &intok,
 138             GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
 139             &ret_flags, NULL, NULL);
 140 
 141         if (outtok.length == 0)
 142                 ctx->ctx_obodylen = 0;
 143         else if (outtok.length <= ctx->ctx_obodylen) {
 144                 ctx->ctx_obodylen = outtok.length;
 145                 (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
 146                 free(outtok.value);
 147                 outtok.value = NULL;
 148         } else {
 149                 free(ctx->ctx_obodybuf);
 150                 ctx->ctx_obodybuf = outtok.value;
 151                 ctx->ctx_obodylen = outtok.length;
 152                 outtok.value = NULL;
 153         }
 154 
 155         if (GSS_ERROR(major)) {
 156                 smbd_report("krb5ssp: gss_accept_sec_context, "
 157                     "mech=0x%x, major=0x%x, minor=0x%x",
 158                     (int)mech_type, major, minor);
 159                 smbd_report(" krb5: %s",
 160                     krb5_get_error_message(be->be_kctx, minor));
 161                 return (NT_STATUS_WRONG_PASSWORD);

 162         }
 163 
 164         switch (major) {
 165         case GSS_S_COMPLETE:
 166                 break;
 167         case GSS_S_CONTINUE_NEEDED:
 168                 if (outtok.length > 0) {
 169                         ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
 170                         /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
 171                         return (0);
 172                 }
 173                 return (NT_STATUS_WRONG_PASSWORD);

 174         default:
 175                 return (NT_STATUS_WRONG_PASSWORD);

 176         }
 177 
 178         /*
 179          * OK, we got GSS_S_COMPLETE.  Get the name so we can use it
 180          * in log messages if we get failures decoding the PAC etc.
 181          * Then get the PAC, decode it, build the logon token.
 182          */
 183 
 184         if (gname != NULL && GSS_S_COMPLETE ==
 185             gss_display_name(&minor, gname, &namebuf, &name_type)) {
 186                 /* Save the user name. */
 187                 be->be_username = strdup(namebuf.value);
 188                 (void) gss_release_buffer(&minor, &namebuf);
 189                 (void) gss_release_name(&minor, &gname);
 190                 if (be->be_username == NULL) {
 191                         return (NT_STATUS_NO_MEMORY);
 192                 }
 193         }
 194 
 195         /*
 196          * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
 197          */
 198         status = get_authz_data_pac(be->be_gssctx,
 199             &be->be_authz_pac);
 200         if (status)
 201                 return (status);
 202 
 203         kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
 204             be->be_authz_pac.length, &be->be_kpac);
 205         if (kerr) {
 206                 smbd_report("krb5ssp, krb5_pac_parse: %s",
 207                     krb5_get_error_message(be->be_kctx, kerr));
 208                 return (NT_STATUS_UNSUCCESSFUL);

 209         }
 210 
 211         kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
 212             PAC_LOGON_INFO, &be->be_pac);
 213         if (kerr) {
 214                 smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
 215                     krb5_get_error_message(be->be_kctx, kerr));
 216                 return (NT_STATUS_UNSUCCESSFUL);

 217         }
 218 
 219         ctx->ctx_token = calloc(1, sizeof (smb_token_t));
 220         if (ctx->ctx_token == NULL)
 221                 return (NT_STATUS_NO_MEMORY);
 222 

 223         status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
 224             be->be_pac.length);
 225         if (status)
 226                 return (status);
 227 
 228         status = get_ssnkey(ctx);
 229         if (status)
 230                 return (status);
 231 
 232         if (!smb_token_setup_common(ctx->ctx_token))
 233                 return (NT_STATUS_UNSUCCESSFUL);


 234 
 235         /* Success! */
 236         ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
 237 
 238         return (0);












 239 }
 240 
 241 /*
 242  * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
 243  * and: KRB5_AUTHDATA_WIN2K_PAC
 244  */
 245 static const gss_OID_desc
 246 oid_ex_authz_data_pac = {
 247         13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
 248 
 249 /*
 250  * See: krb5_gss_inquire_sec_context_by_oid()
 251  * and krb5_gss_inquire_sec_context_by_oid_ops[],
 252  * gss_krb5int_extract_authz_data_from_sec_context()
 253  */
 254 static uint32_t
 255 get_authz_data_pac(
 256         gss_ctx_id_t context_handle,
 257         gss_buffer_t ad_data)
 258 {


   1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * SPNEGO back-end for Kerberos.  See [MS-KILE]
  18  */
  19 
  20 #include <sys/types.h>
  21 #include <gssapi/gssapi_ext.h>
  22 #include <gssapi/gssapi_krb5.h>
  23 #include <krb5.h>
  24 #include "smbd.h"
  25 #include "smbd_authsvc.h"
  26 
  27 /* From krb5/krb/pac.c (should have been exported) */
  28 #define PAC_LOGON_INFO          1
  29 
  30 typedef struct krb5ssp_backend {
  31         gss_ctx_id_t            be_gssctx;
  32         char                    *be_username;
  33         gss_buffer_desc         be_authz_pac;


 102         free(be);
 103 }
 104 
 105 /*
 106  * Handle a Kerberos auth message.
 107  *
 108  * State across messages is in ctx->ctx_backend
 109  */
 110 int
 111 smbd_krb5ssp_work(authsvc_context_t *ctx)
 112 {
 113         gss_buffer_desc intok, outtok;
 114         gss_buffer_desc namebuf;
 115         krb5ssp_backend_t *be = ctx->ctx_backend;
 116         gss_name_t gname = NULL;
 117         OM_uint32 major, minor, ret_flags;
 118         gss_OID name_type = GSS_C_NULL_OID;
 119         gss_OID mech_type = GSS_C_NULL_OID;
 120         krb5_error_code kerr;
 121         uint32_t status;
 122         smb_token_t *token = NULL;
 123 
 124         intok.length = ctx->ctx_ibodylen;
 125         intok.value  = ctx->ctx_ibodybuf;
 126         bzero(&outtok, sizeof (gss_buffer_desc));
 127         bzero(&namebuf, sizeof (gss_buffer_desc));
 128 
 129         /* Do this early, for error message support. */
 130         kerr = krb5_init_context(&be->be_kctx);
 131         if (kerr != 0) {
 132                 smbd_report("krb5ssp, krb5_init_ctx: %s",
 133                     krb5_get_error_message(be->be_kctx, kerr));
 134                 return (NT_STATUS_INTERNAL_ERROR);
 135         }
 136 
 137         free(be->be_username);
 138         be->be_username = NULL;
 139 
 140         major = gss_accept_sec_context(&minor, &be->be_gssctx,
 141             GSS_C_NO_CREDENTIAL, &intok,
 142             GSS_C_NO_CHANNEL_BINDINGS, &gname, &mech_type, &outtok,
 143             &ret_flags, NULL, NULL);
 144 
 145         if (outtok.length == 0)
 146                 ctx->ctx_obodylen = 0;
 147         else if (outtok.length <= ctx->ctx_obodylen) {
 148                 ctx->ctx_obodylen = outtok.length;
 149                 (void) memcpy(ctx->ctx_obodybuf, outtok.value, outtok.length);
 150                 free(outtok.value);
 151                 outtok.value = NULL;
 152         } else {
 153                 free(ctx->ctx_obodybuf);
 154                 ctx->ctx_obodybuf = outtok.value;
 155                 ctx->ctx_obodylen = outtok.length;
 156                 outtok.value = NULL;
 157         }
 158 
 159         if (GSS_ERROR(major)) {
 160                 smbd_report("krb5ssp: gss_accept_sec_context, "
 161                     "mech=0x%x, major=0x%x, minor=0x%x",
 162                     (int)mech_type, major, minor);
 163                 smbd_report(" krb5: %s",
 164                     krb5_get_error_message(be->be_kctx, minor));
 165                 status = NT_STATUS_WRONG_PASSWORD;
 166                 goto out;
 167         }
 168 
 169         switch (major) {
 170         case GSS_S_COMPLETE:
 171                 break;
 172         case GSS_S_CONTINUE_NEEDED:
 173                 if (outtok.length > 0) {
 174                         ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
 175                         /* becomes NT_STATUS_MORE_PROCESSING_REQUIRED */
 176                         return (0);
 177                 }
 178                 status = NT_STATUS_WRONG_PASSWORD;
 179                 goto out;
 180         default:
 181                 status = NT_STATUS_WRONG_PASSWORD;
 182                 goto out;
 183         }
 184 
 185         /*
 186          * OK, we got GSS_S_COMPLETE.  Get the name so we can use it
 187          * in log messages if we get failures decoding the PAC etc.
 188          * Then get the PAC, decode it, build the logon token.
 189          */
 190 
 191         if (gname != NULL && GSS_S_COMPLETE ==
 192             gss_display_name(&minor, gname, &namebuf, &name_type)) {
 193                 /* Save the user name. */
 194                 be->be_username = strdup(namebuf.value);
 195                 (void) gss_release_buffer(&minor, &namebuf);
 196                 (void) gss_release_name(&minor, &gname);
 197                 if (be->be_username == NULL) {
 198                         return (NT_STATUS_NO_MEMORY);
 199                 }
 200         }
 201 
 202         /*
 203          * Extract the KRB5_AUTHDATA_WIN2K_PAC data.
 204          */
 205         status = get_authz_data_pac(be->be_gssctx,
 206             &be->be_authz_pac);
 207         if (status)
 208                 goto out;
 209 
 210         kerr = krb5_pac_parse(be->be_kctx, be->be_authz_pac.value,
 211             be->be_authz_pac.length, &be->be_kpac);
 212         if (kerr) {
 213                 smbd_report("krb5ssp, krb5_pac_parse: %s",
 214                     krb5_get_error_message(be->be_kctx, kerr));
 215                 status = NT_STATUS_UNSUCCESSFUL;
 216                 goto out;
 217         }
 218 
 219         kerr = krb5_pac_get_buffer(be->be_kctx, be->be_kpac,
 220             PAC_LOGON_INFO, &be->be_pac);
 221         if (kerr) {
 222                 smbd_report("krb5ssp, krb5_pac_get_buffer: %s",
 223                     krb5_get_error_message(be->be_kctx, kerr));
 224                 status = NT_STATUS_UNSUCCESSFUL;
 225                 goto out;
 226         }
 227 
 228         ctx->ctx_token = calloc(1, sizeof (smb_token_t));
 229         if (ctx->ctx_token == NULL) {
 230                 status = NT_STATUS_NO_MEMORY;
 231                 goto out;
 232         }
 233         status = smb_decode_krb5_pac(ctx->ctx_token, be->be_pac.data,
 234             be->be_pac.length);
 235         if (status)
 236                 goto out;
 237 
 238         status = get_ssnkey(ctx);
 239         if (status)
 240                 goto out;
 241 
 242         if (!smb_token_setup_common(ctx->ctx_token)) {
 243                 status = NT_STATUS_UNSUCCESSFUL;
 244                 goto out;
 245         }
 246 
 247         /* Success! */
 248         ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
 249 
 250         status = 0;
 251         token = ctx->ctx_token;
 252 
 253         /*
 254          * Before we return, audit successful and failed logons.
 255          * We only audit logons where we should have a username.
 256          */
 257 out:
 258         if (!smbd_logon_audit(token, &ctx->ctx_clinfo.lci_clnt_ipaddr,
 259             be->be_username, "") && status == 0)
 260                 return (NT_STATUS_AUDIT_FAILED);
 261 
 262         return (status);
 263 }
 264 
 265 /*
 266  * See: GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID
 267  * and: KRB5_AUTHDATA_WIN2K_PAC
 268  */
 269 static const gss_OID_desc
 270 oid_ex_authz_data_pac = {
 271         13, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0a\x81\x00" };
 272 
 273 /*
 274  * See: krb5_gss_inquire_sec_context_by_oid()
 275  * and krb5_gss_inquire_sec_context_by_oid_ops[],
 276  * gss_krb5int_extract_authz_data_from_sec_context()
 277  */
 278 static uint32_t
 279 get_authz_data_pac(
 280         gss_ctx_id_t context_handle,
 281         gss_buffer_t ad_data)
 282 {