1*613a2f6bSGordon Ross /* 2*613a2f6bSGordon Ross * Copyright (c) 2000, Boris Popov 3*613a2f6bSGordon Ross * All rights reserved. 4*613a2f6bSGordon Ross * 5*613a2f6bSGordon Ross * Redistribution and use in source and binary forms, with or without 6*613a2f6bSGordon Ross * modification, are permitted provided that the following conditions 7*613a2f6bSGordon Ross * are met: 8*613a2f6bSGordon Ross * 1. Redistributions of source code must retain the above copyright 9*613a2f6bSGordon Ross * notice, this list of conditions and the following disclaimer. 10*613a2f6bSGordon Ross * 2. Redistributions in binary form must reproduce the above copyright 11*613a2f6bSGordon Ross * notice, this list of conditions and the following disclaimer in the 12*613a2f6bSGordon Ross * documentation and/or other materials provided with the distribution. 13*613a2f6bSGordon Ross * 3. All advertising materials mentioning features or use of this software 14*613a2f6bSGordon Ross * must display the following acknowledgement: 15*613a2f6bSGordon Ross * This product includes software developed by Boris Popov. 16*613a2f6bSGordon Ross * 4. Neither the name of the author nor the names of any co-contributors 17*613a2f6bSGordon Ross * may be used to endorse or promote products derived from this software 18*613a2f6bSGordon Ross * without specific prior written permission. 19*613a2f6bSGordon Ross * 20*613a2f6bSGordon Ross * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21*613a2f6bSGordon Ross * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22*613a2f6bSGordon Ross * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23*613a2f6bSGordon Ross * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24*613a2f6bSGordon Ross * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25*613a2f6bSGordon Ross * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26*613a2f6bSGordon Ross * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27*613a2f6bSGordon Ross * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28*613a2f6bSGordon Ross * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29*613a2f6bSGordon Ross * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30*613a2f6bSGordon Ross * SUCH DAMAGE. 31*613a2f6bSGordon Ross */ 32*613a2f6bSGordon Ross 33*613a2f6bSGordon Ross /* 34*613a2f6bSGordon Ross * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 35*613a2f6bSGordon Ross * Use is subject to license terms. 36*613a2f6bSGordon Ross */ 37*613a2f6bSGordon Ross 38*613a2f6bSGordon Ross /* 39*613a2f6bSGordon Ross * Kerberos V Security Support Provider 40*613a2f6bSGordon Ross * 41*613a2f6bSGordon Ross * Based on code previously in ctx.c (from Boris Popov?) 42*613a2f6bSGordon Ross * but then mostly rewritten at Sun. 43*613a2f6bSGordon Ross */ 44*613a2f6bSGordon Ross 45*613a2f6bSGordon Ross #include <errno.h> 46*613a2f6bSGordon Ross #include <stdio.h> 47*613a2f6bSGordon Ross #include <stddef.h> 48*613a2f6bSGordon Ross #include <stdlib.h> 49*613a2f6bSGordon Ross #include <unistd.h> 50*613a2f6bSGordon Ross #include <strings.h> 51*613a2f6bSGordon Ross #include <netdb.h> 52*613a2f6bSGordon Ross #include <libintl.h> 53*613a2f6bSGordon Ross #include <xti.h> 54*613a2f6bSGordon Ross #include <assert.h> 55*613a2f6bSGordon Ross 56*613a2f6bSGordon Ross #include <sys/types.h> 57*613a2f6bSGordon Ross #include <sys/time.h> 58*613a2f6bSGordon Ross #include <sys/byteorder.h> 59*613a2f6bSGordon Ross #include <sys/socket.h> 60*613a2f6bSGordon Ross #include <sys/fcntl.h> 61*613a2f6bSGordon Ross 62*613a2f6bSGordon Ross #include <netinet/in.h> 63*613a2f6bSGordon Ross #include <netinet/tcp.h> 64*613a2f6bSGordon Ross #include <arpa/inet.h> 65*613a2f6bSGordon Ross 66*613a2f6bSGordon Ross #include <netsmb/smb.h> 67*613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 68*613a2f6bSGordon Ross #include <netsmb/mchain.h> 69*613a2f6bSGordon Ross 70*613a2f6bSGordon Ross #include "private.h" 71*613a2f6bSGordon Ross #include "charsets.h" 72*613a2f6bSGordon Ross #include "spnego.h" 73*613a2f6bSGordon Ross #include "derparse.h" 74*613a2f6bSGordon Ross #include "ssp.h" 75*613a2f6bSGordon Ross 76*613a2f6bSGordon Ross #include <kerberosv5/krb5.h> 77*613a2f6bSGordon Ross #include <kerberosv5/com_err.h> 78*613a2f6bSGordon Ross 79*613a2f6bSGordon Ross /* RFC 1964 token ID codes */ 80*613a2f6bSGordon Ross #define KRB_AP_REQ 1 81*613a2f6bSGordon Ross #define KRB_AP_REP 2 82*613a2f6bSGordon Ross #define KRB_ERROR 3 83*613a2f6bSGordon Ross 84*613a2f6bSGordon Ross extern MECH_OID g_stcMechOIDList []; 85*613a2f6bSGordon Ross 86*613a2f6bSGordon Ross typedef struct krb5ssp_state { 87*613a2f6bSGordon Ross /* Filled in by krb5ssp_init_client */ 88*613a2f6bSGordon Ross krb5_context ss_krb5ctx; /* krb5 context (ptr) */ 89*613a2f6bSGordon Ross krb5_ccache ss_krb5cc; /* credentials cache (ptr) */ 90*613a2f6bSGordon Ross krb5_principal ss_krb5clp; /* client principal (ptr) */ 91*613a2f6bSGordon Ross /* Filled in by krb5ssp_get_tkt */ 92*613a2f6bSGordon Ross krb5_auth_context ss_auth; /* auth ctx. w/ server (ptr) */ 93*613a2f6bSGordon Ross } krb5ssp_state_t; 94*613a2f6bSGordon Ross 95*613a2f6bSGordon Ross 96*613a2f6bSGordon Ross /* 97*613a2f6bSGordon Ross * adds a GSSAPI wrapper 98*613a2f6bSGordon Ross */ 99*613a2f6bSGordon Ross int 100*613a2f6bSGordon Ross krb5ssp_tkt2gtok(uchar_t *tkt, ulong_t tktlen, 101*613a2f6bSGordon Ross uchar_t **gtokp, ulong_t *gtoklenp) 102*613a2f6bSGordon Ross { 103*613a2f6bSGordon Ross ulong_t len; 104*613a2f6bSGordon Ross ulong_t bloblen = tktlen; 105*613a2f6bSGordon Ross uchar_t krbapreq[2] = { KRB_AP_REQ, 0 }; 106*613a2f6bSGordon Ross uchar_t *blob = NULL; /* result */ 107*613a2f6bSGordon Ross uchar_t *b; 108*613a2f6bSGordon Ross 109*613a2f6bSGordon Ross bloblen += sizeof (krbapreq); 110*613a2f6bSGordon Ross bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen; 111*613a2f6bSGordon Ross len = bloblen; 112*613a2f6bSGordon Ross bloblen = ASNDerCalcTokenLength(bloblen, bloblen); 113*613a2f6bSGordon Ross if ((blob = malloc(bloblen)) == NULL) { 114*613a2f6bSGordon Ross DPRINT("malloc"); 115*613a2f6bSGordon Ross return (ENOMEM); 116*613a2f6bSGordon Ross } 117*613a2f6bSGordon Ross 118*613a2f6bSGordon Ross b = blob; 119*613a2f6bSGordon Ross b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len); 120*613a2f6bSGordon Ross b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5); 121*613a2f6bSGordon Ross memcpy(b, krbapreq, sizeof (krbapreq)); 122*613a2f6bSGordon Ross b += sizeof (krbapreq); 123*613a2f6bSGordon Ross 124*613a2f6bSGordon Ross assert(b + tktlen == blob + bloblen); 125*613a2f6bSGordon Ross memcpy(b, tkt, tktlen); 126*613a2f6bSGordon Ross *gtoklenp = bloblen; 127*613a2f6bSGordon Ross *gtokp = blob; 128*613a2f6bSGordon Ross return (0); 129*613a2f6bSGordon Ross } 130*613a2f6bSGordon Ross 131*613a2f6bSGordon Ross /* 132*613a2f6bSGordon Ross * See "Windows 2000 Kerberos Interoperability" paper by 133*613a2f6bSGordon Ross * Christopher Nebergall. RC4 HMAC is the W2K default but 134*613a2f6bSGordon Ross * Samba support lagged (not due to Samba itself, but due to OS' 135*613a2f6bSGordon Ross * Kerberos implementations.) 136*613a2f6bSGordon Ross * 137*613a2f6bSGordon Ross * Only session enc type should matter, not ticket enc type, 138*613a2f6bSGordon Ross * per Sam Hartman on krbdev. 139*613a2f6bSGordon Ross * 140*613a2f6bSGordon Ross * Preauthentication failure topics in krb-protocol may help here... 141*613a2f6bSGordon Ross * try "John Brezak" and/or "Clifford Neuman" too. 142*613a2f6bSGordon Ross */ 143*613a2f6bSGordon Ross static krb5_enctype kenctypes[] = { 144*613a2f6bSGordon Ross ENCTYPE_ARCFOUR_HMAC, /* defined in krb5.h */ 145*613a2f6bSGordon Ross ENCTYPE_DES_CBC_MD5, 146*613a2f6bSGordon Ross ENCTYPE_DES_CBC_CRC, 147*613a2f6bSGordon Ross ENCTYPE_NULL 148*613a2f6bSGordon Ross }; 149*613a2f6bSGordon Ross 150*613a2f6bSGordon Ross static const int rq_opts = 151*613a2f6bSGordon Ross AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED; 152*613a2f6bSGordon Ross 153*613a2f6bSGordon Ross /* 154*613a2f6bSGordon Ross * Obtain a kerberos ticket for the host we're connecting to. 155*613a2f6bSGordon Ross * (This does the KRB_TGS exchange.) 156*613a2f6bSGordon Ross */ 157*613a2f6bSGordon Ross static int 158*613a2f6bSGordon Ross krb5ssp_get_tkt(krb5ssp_state_t *ss, char *server, 159*613a2f6bSGordon Ross uchar_t **tktp, ulong_t *tktlenp) 160*613a2f6bSGordon Ross { 161*613a2f6bSGordon Ross krb5_context kctx = ss->ss_krb5ctx; 162*613a2f6bSGordon Ross krb5_ccache kcc = ss->ss_krb5cc; 163*613a2f6bSGordon Ross krb5_data indata = {0}; 164*613a2f6bSGordon Ross krb5_data outdata = {0}; 165*613a2f6bSGordon Ross krb5_error_code kerr = 0; 166*613a2f6bSGordon Ross const char *fn = NULL; 167*613a2f6bSGordon Ross uchar_t *tkt; 168*613a2f6bSGordon Ross 169*613a2f6bSGordon Ross /* Should have these from krb5ssp_init_client. */ 170*613a2f6bSGordon Ross if (kctx == NULL || kcc == NULL) { 171*613a2f6bSGordon Ross fn = "null kctx or kcc"; 172*613a2f6bSGordon Ross kerr = EINVAL; 173*613a2f6bSGordon Ross goto out; 174*613a2f6bSGordon Ross } 175*613a2f6bSGordon Ross 176*613a2f6bSGordon Ross kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes); 177*613a2f6bSGordon Ross if (kerr != 0) { 178*613a2f6bSGordon Ross fn = "krb5_set_default_tgs_enctypes"; 179*613a2f6bSGordon Ross goto out; 180*613a2f6bSGordon Ross } 181*613a2f6bSGordon Ross 182*613a2f6bSGordon Ross /* Override the krb5 library default. */ 183*613a2f6bSGordon Ross indata.data = ""; 184*613a2f6bSGordon Ross 185*613a2f6bSGordon Ross kerr = krb5_mk_req(kctx, &ss->ss_auth, rq_opts, "cifs", server, 186*613a2f6bSGordon Ross &indata, kcc, &outdata); 187*613a2f6bSGordon Ross if (kerr != 0) { 188*613a2f6bSGordon Ross fn = "krb5_mk_req"; 189*613a2f6bSGordon Ross goto out; 190*613a2f6bSGordon Ross } 191*613a2f6bSGordon Ross if ((tkt = malloc(outdata.length)) == NULL) { 192*613a2f6bSGordon Ross kerr = ENOMEM; 193*613a2f6bSGordon Ross fn = "malloc signing key"; 194*613a2f6bSGordon Ross goto out; 195*613a2f6bSGordon Ross } 196*613a2f6bSGordon Ross memcpy(tkt, outdata.data, outdata.length); 197*613a2f6bSGordon Ross *tktp = tkt; 198*613a2f6bSGordon Ross *tktlenp = outdata.length; 199*613a2f6bSGordon Ross kerr = 0; 200*613a2f6bSGordon Ross 201*613a2f6bSGordon Ross out: 202*613a2f6bSGordon Ross if (kerr) { 203*613a2f6bSGordon Ross if (fn == NULL) 204*613a2f6bSGordon Ross fn = "?"; 205*613a2f6bSGordon Ross DPRINT("%s err 0x%x: %s", fn, kerr, error_message(kerr)); 206*613a2f6bSGordon Ross if (kerr <= 0 || kerr > ESTALE) 207*613a2f6bSGordon Ross kerr = EAUTH; 208*613a2f6bSGordon Ross } 209*613a2f6bSGordon Ross 210*613a2f6bSGordon Ross if (outdata.data) 211*613a2f6bSGordon Ross krb5_free_data_contents(kctx, &outdata); 212*613a2f6bSGordon Ross 213*613a2f6bSGordon Ross /* Free kctx in krb5ssp_destroy */ 214*613a2f6bSGordon Ross return (kerr); 215*613a2f6bSGordon Ross } 216*613a2f6bSGordon Ross 217*613a2f6bSGordon Ross 218*613a2f6bSGordon Ross /* 219*613a2f6bSGordon Ross * Build an RFC 1964 KRB_AP_REQ message 220*613a2f6bSGordon Ross * The caller puts on the SPNEGO wrapper. 221*613a2f6bSGordon Ross */ 222*613a2f6bSGordon Ross int 223*613a2f6bSGordon Ross krb5ssp_put_request(struct ssp_ctx *sp, struct mbdata *out_mb) 224*613a2f6bSGordon Ross { 225*613a2f6bSGordon Ross int err; 226*613a2f6bSGordon Ross struct smb_ctx *ctx = sp->smb_ctx; 227*613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 228*613a2f6bSGordon Ross uchar_t *tkt = NULL; 229*613a2f6bSGordon Ross ulong_t tktlen; 230*613a2f6bSGordon Ross uchar_t *gtok = NULL; /* gssapi token */ 231*613a2f6bSGordon Ross ulong_t gtoklen; /* gssapi token length */ 232*613a2f6bSGordon Ross char *prin = ctx->ct_srvname; 233*613a2f6bSGordon Ross 234*613a2f6bSGordon Ross if ((err = krb5ssp_get_tkt(ss, prin, &tkt, &tktlen)) != 0) 235*613a2f6bSGordon Ross goto out; 236*613a2f6bSGordon Ross if ((err = krb5ssp_tkt2gtok(tkt, tktlen, >ok, >oklen)) != 0) 237*613a2f6bSGordon Ross goto out; 238*613a2f6bSGordon Ross 239*613a2f6bSGordon Ross if ((err = mb_init(out_mb, gtoklen)) != 0) 240*613a2f6bSGordon Ross goto out; 241*613a2f6bSGordon Ross if ((err = mb_put_mem(out_mb, gtok, gtoklen)) != 0) 242*613a2f6bSGordon Ross goto out; 243*613a2f6bSGordon Ross 244*613a2f6bSGordon Ross if (ctx->ct_vcflags & SMBV_WILL_SIGN) 245*613a2f6bSGordon Ross ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; 246*613a2f6bSGordon Ross 247*613a2f6bSGordon Ross out: 248*613a2f6bSGordon Ross if (gtok) 249*613a2f6bSGordon Ross free(gtok); 250*613a2f6bSGordon Ross if (tkt) 251*613a2f6bSGordon Ross free(tkt); 252*613a2f6bSGordon Ross 253*613a2f6bSGordon Ross return (err); 254*613a2f6bSGordon Ross } 255*613a2f6bSGordon Ross 256*613a2f6bSGordon Ross /* 257*613a2f6bSGordon Ross * Unwrap a GSS-API encapsulated RFC 1964 reply message, 258*613a2f6bSGordon Ross * i.e. type KRB_AP_REP or KRB_ERROR. 259*613a2f6bSGordon Ross */ 260*613a2f6bSGordon Ross int 261*613a2f6bSGordon Ross krb5ssp_get_reply(struct ssp_ctx *sp, struct mbdata *in_mb) 262*613a2f6bSGordon Ross { 263*613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 264*613a2f6bSGordon Ross mbuf_t *m = in_mb->mb_top; 265*613a2f6bSGordon Ross int err = EBADRPC; 266*613a2f6bSGordon Ross int dlen, rc; 267*613a2f6bSGordon Ross long actual_len, token_len; 268*613a2f6bSGordon Ross uchar_t *data; 269*613a2f6bSGordon Ross krb5_data ap = {0}; 270*613a2f6bSGordon Ross krb5_ap_rep_enc_part *reply = NULL; 271*613a2f6bSGordon Ross 272*613a2f6bSGordon Ross /* cheating: this mbuf is contiguous */ 273*613a2f6bSGordon Ross assert(m->m_data == in_mb->mb_pos); 274*613a2f6bSGordon Ross data = (uchar_t *)m->m_data; 275*613a2f6bSGordon Ross dlen = m->m_len; 276*613a2f6bSGordon Ross 277*613a2f6bSGordon Ross /* 278*613a2f6bSGordon Ross * Peel off the GSS-API wrapper. Looks like: 279*613a2f6bSGordon Ross * AppToken: 60 81 83 280*613a2f6bSGordon Ross * OID(KRB5): 06 09 2a 86 48 86 f7 12 01 02 02 281*613a2f6bSGordon Ross * KRB_AP_REP: 02 00 282*613a2f6bSGordon Ross */ 283*613a2f6bSGordon Ross rc = ASNDerCheckToken(data, SPNEGO_NEGINIT_APP_CONSTRUCT, 284*613a2f6bSGordon Ross 0, dlen, &token_len, &actual_len); 285*613a2f6bSGordon Ross if (rc != SPNEGO_E_SUCCESS) { 286*613a2f6bSGordon Ross DPRINT("no AppToken? rc=0x%x", rc); 287*613a2f6bSGordon Ross goto out; 288*613a2f6bSGordon Ross } 289*613a2f6bSGordon Ross if (dlen < actual_len) 290*613a2f6bSGordon Ross goto out; 291*613a2f6bSGordon Ross data += actual_len; 292*613a2f6bSGordon Ross dlen -= actual_len; 293*613a2f6bSGordon Ross 294*613a2f6bSGordon Ross /* OID (KRB5) */ 295*613a2f6bSGordon Ross rc = ASNDerCheckOID(data, spnego_mech_oid_Kerberos_V5, 296*613a2f6bSGordon Ross dlen, &actual_len); 297*613a2f6bSGordon Ross if (rc != SPNEGO_E_SUCCESS) { 298*613a2f6bSGordon Ross DPRINT("no OID? rc=0x%x", rc); 299*613a2f6bSGordon Ross goto out; 300*613a2f6bSGordon Ross } 301*613a2f6bSGordon Ross if (dlen < actual_len) 302*613a2f6bSGordon Ross goto out; 303*613a2f6bSGordon Ross data += actual_len; 304*613a2f6bSGordon Ross dlen -= actual_len; 305*613a2f6bSGordon Ross 306*613a2f6bSGordon Ross /* KRB_AP_REP or KRB_ERROR */ 307*613a2f6bSGordon Ross if (data[0] != KRB_AP_REP) { 308*613a2f6bSGordon Ross DPRINT("KRB5 type: %d", data[1]); 309*613a2f6bSGordon Ross goto out; 310*613a2f6bSGordon Ross } 311*613a2f6bSGordon Ross if (dlen < 2) 312*613a2f6bSGordon Ross goto out; 313*613a2f6bSGordon Ross data += 2; 314*613a2f6bSGordon Ross dlen -= 2; 315*613a2f6bSGordon Ross 316*613a2f6bSGordon Ross /* 317*613a2f6bSGordon Ross * Now what's left should be a krb5 reply 318*613a2f6bSGordon Ross * NB: ap is NOT allocated, so don't free it. 319*613a2f6bSGordon Ross */ 320*613a2f6bSGordon Ross ap.length = dlen; 321*613a2f6bSGordon Ross ap.data = (char *)data; 322*613a2f6bSGordon Ross rc = krb5_rd_rep(ss->ss_krb5ctx, ss->ss_auth, &ap, &reply); 323*613a2f6bSGordon Ross if (rc != 0) { 324*613a2f6bSGordon Ross DPRINT("krb5_rd_rep: err 0x%x (%s)", 325*613a2f6bSGordon Ross rc, error_message(rc)); 326*613a2f6bSGordon Ross err = EAUTH; 327*613a2f6bSGordon Ross goto out; 328*613a2f6bSGordon Ross } 329*613a2f6bSGordon Ross 330*613a2f6bSGordon Ross /* 331*613a2f6bSGordon Ross * Have the decoded reply. Save anything? 332*613a2f6bSGordon Ross * 333*613a2f6bSGordon Ross * NB: If this returns an error, we will get 334*613a2f6bSGordon Ross * no more calls into this back-end module. 335*613a2f6bSGordon Ross */ 336*613a2f6bSGordon Ross err = 0; 337*613a2f6bSGordon Ross 338*613a2f6bSGordon Ross out: 339*613a2f6bSGordon Ross if (reply != NULL) 340*613a2f6bSGordon Ross krb5_free_ap_rep_enc_part(ss->ss_krb5ctx, reply); 341*613a2f6bSGordon Ross if (err) 342*613a2f6bSGordon Ross DPRINT("ret %d", err); 343*613a2f6bSGordon Ross 344*613a2f6bSGordon Ross return (err); 345*613a2f6bSGordon Ross } 346*613a2f6bSGordon Ross 347*613a2f6bSGordon Ross /* 348*613a2f6bSGordon Ross * krb5ssp_final 349*613a2f6bSGordon Ross * 350*613a2f6bSGordon Ross * Called after successful authentication. 351*613a2f6bSGordon Ross * Setup the MAC key for signing. 352*613a2f6bSGordon Ross */ 353*613a2f6bSGordon Ross int 354*613a2f6bSGordon Ross krb5ssp_final(struct ssp_ctx *sp) 355*613a2f6bSGordon Ross { 356*613a2f6bSGordon Ross struct smb_ctx *ctx = sp->smb_ctx; 357*613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 358*613a2f6bSGordon Ross krb5_keyblock *ssn_key = NULL; 359*613a2f6bSGordon Ross int err, len; 360*613a2f6bSGordon Ross 361*613a2f6bSGordon Ross /* 362*613a2f6bSGordon Ross * Save the session key, used for SMB signing 363*613a2f6bSGordon Ross * and possibly other consumers (RPC). 364*613a2f6bSGordon Ross */ 365*613a2f6bSGordon Ross err = krb5_auth_con_getlocalsubkey( 366*613a2f6bSGordon Ross ss->ss_krb5ctx, ss->ss_auth, &ssn_key); 367*613a2f6bSGordon Ross if (err != 0) { 368*613a2f6bSGordon Ross DPRINT("_getlocalsubkey, err=0x%x (%s)", 369*613a2f6bSGordon Ross err, error_message(err)); 370*613a2f6bSGordon Ross if (err <= 0 || err > ESTALE) 371*613a2f6bSGordon Ross err = EAUTH; 372*613a2f6bSGordon Ross goto out; 373*613a2f6bSGordon Ross } 374*613a2f6bSGordon Ross memset(ctx->ct_ssn_key, 0, SMBIOC_HASH_SZ); 375*613a2f6bSGordon Ross if ((len = ssn_key->length) > SMBIOC_HASH_SZ) 376*613a2f6bSGordon Ross len = SMBIOC_HASH_SZ; 377*613a2f6bSGordon Ross memcpy(ctx->ct_ssn_key, ssn_key->contents, len); 378*613a2f6bSGordon Ross 379*613a2f6bSGordon Ross /* 380*613a2f6bSGordon Ross * Set the MAC key on the first successful auth. 381*613a2f6bSGordon Ross */ 382*613a2f6bSGordon Ross if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) && 383*613a2f6bSGordon Ross (ctx->ct_mackey == NULL)) { 384*613a2f6bSGordon Ross ctx->ct_mackeylen = ssn_key->length; 385*613a2f6bSGordon Ross ctx->ct_mackey = malloc(ctx->ct_mackeylen); 386*613a2f6bSGordon Ross if (ctx->ct_mackey == NULL) { 387*613a2f6bSGordon Ross ctx->ct_mackeylen = 0; 388*613a2f6bSGordon Ross err = ENOMEM; 389*613a2f6bSGordon Ross goto out; 390*613a2f6bSGordon Ross } 391*613a2f6bSGordon Ross memcpy(ctx->ct_mackey, ssn_key->contents, 392*613a2f6bSGordon Ross ctx->ct_mackeylen); 393*613a2f6bSGordon Ross /* 394*613a2f6bSGordon Ross * Apparently, the server used seq. no. zero 395*613a2f6bSGordon Ross * for our previous message, so next is two. 396*613a2f6bSGordon Ross */ 397*613a2f6bSGordon Ross ctx->ct_mac_seqno = 2; 398*613a2f6bSGordon Ross } 399*613a2f6bSGordon Ross err = 0; 400*613a2f6bSGordon Ross 401*613a2f6bSGordon Ross out: 402*613a2f6bSGordon Ross if (ssn_key) 403*613a2f6bSGordon Ross krb5_free_keyblock(ss->ss_krb5ctx, ssn_key); 404*613a2f6bSGordon Ross 405*613a2f6bSGordon Ross return (err); 406*613a2f6bSGordon Ross } 407*613a2f6bSGordon Ross 408*613a2f6bSGordon Ross /* 409*613a2f6bSGordon Ross * krb5ssp_next_token 410*613a2f6bSGordon Ross * 411*613a2f6bSGordon Ross * See ssp.c: ssp_ctx_next_token 412*613a2f6bSGordon Ross */ 413*613a2f6bSGordon Ross int 414*613a2f6bSGordon Ross krb5ssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb, 415*613a2f6bSGordon Ross struct mbdata *out_mb) 416*613a2f6bSGordon Ross { 417*613a2f6bSGordon Ross int err; 418*613a2f6bSGordon Ross 419*613a2f6bSGordon Ross /* 420*613a2f6bSGordon Ross * Note: in_mb == NULL on the first call. 421*613a2f6bSGordon Ross */ 422*613a2f6bSGordon Ross if (in_mb) { 423*613a2f6bSGordon Ross err = krb5ssp_get_reply(sp, in_mb); 424*613a2f6bSGordon Ross if (err) 425*613a2f6bSGordon Ross goto out; 426*613a2f6bSGordon Ross } 427*613a2f6bSGordon Ross 428*613a2f6bSGordon Ross if (out_mb) { 429*613a2f6bSGordon Ross err = krb5ssp_put_request(sp, out_mb); 430*613a2f6bSGordon Ross } else 431*613a2f6bSGordon Ross err = krb5ssp_final(sp); 432*613a2f6bSGordon Ross 433*613a2f6bSGordon Ross out: 434*613a2f6bSGordon Ross if (err) 435*613a2f6bSGordon Ross DPRINT("ret: %d", err); 436*613a2f6bSGordon Ross return (err); 437*613a2f6bSGordon Ross } 438*613a2f6bSGordon Ross 439*613a2f6bSGordon Ross /* 440*613a2f6bSGordon Ross * krb5ssp_ctx_destroy 441*613a2f6bSGordon Ross * 442*613a2f6bSGordon Ross * Destroy mechanism-specific data. 443*613a2f6bSGordon Ross */ 444*613a2f6bSGordon Ross void 445*613a2f6bSGordon Ross krb5ssp_destroy(struct ssp_ctx *sp) 446*613a2f6bSGordon Ross { 447*613a2f6bSGordon Ross krb5ssp_state_t *ss; 448*613a2f6bSGordon Ross krb5_context kctx; 449*613a2f6bSGordon Ross 450*613a2f6bSGordon Ross ss = sp->sp_private; 451*613a2f6bSGordon Ross if (ss == NULL) 452*613a2f6bSGordon Ross return; 453*613a2f6bSGordon Ross sp->sp_private = NULL; 454*613a2f6bSGordon Ross 455*613a2f6bSGordon Ross if ((kctx = ss->ss_krb5ctx) != NULL) { 456*613a2f6bSGordon Ross /* from krb5ssp_get_tkt */ 457*613a2f6bSGordon Ross if (ss->ss_auth) 458*613a2f6bSGordon Ross krb5_auth_con_free(kctx, ss->ss_auth); 459*613a2f6bSGordon Ross /* from krb5ssp_init_client */ 460*613a2f6bSGordon Ross if (ss->ss_krb5clp) 461*613a2f6bSGordon Ross krb5_free_principal(kctx, ss->ss_krb5clp); 462*613a2f6bSGordon Ross if (ss->ss_krb5cc) 463*613a2f6bSGordon Ross krb5_cc_close(kctx, ss->ss_krb5cc); 464*613a2f6bSGordon Ross krb5_free_context(kctx); 465*613a2f6bSGordon Ross } 466*613a2f6bSGordon Ross 467*613a2f6bSGordon Ross free(ss); 468*613a2f6bSGordon Ross } 469*613a2f6bSGordon Ross 470*613a2f6bSGordon Ross /* 471*613a2f6bSGordon Ross * krb5ssp_init_clnt 472*613a2f6bSGordon Ross * 473*613a2f6bSGordon Ross * Initialize a new Kerberos SSP client context. 474*613a2f6bSGordon Ross * 475*613a2f6bSGordon Ross * The user must already have a TGT in their credential cache, 476*613a2f6bSGordon Ross * as shown by the "klist" command. 477*613a2f6bSGordon Ross */ 478*613a2f6bSGordon Ross int 479*613a2f6bSGordon Ross krb5ssp_init_client(struct ssp_ctx *sp) 480*613a2f6bSGordon Ross { 481*613a2f6bSGordon Ross krb5ssp_state_t *ss; 482*613a2f6bSGordon Ross krb5_error_code kerr; 483*613a2f6bSGordon Ross krb5_context kctx = NULL; 484*613a2f6bSGordon Ross krb5_ccache kcc = NULL; 485*613a2f6bSGordon Ross krb5_principal kprin = NULL; 486*613a2f6bSGordon Ross 487*613a2f6bSGordon Ross if ((sp->smb_ctx->ct_authflags & SMB_AT_KRB5) == 0) { 488*613a2f6bSGordon Ross DPRINT("KRB5 not in authflags"); 489*613a2f6bSGordon Ross return (ENOTSUP); 490*613a2f6bSGordon Ross } 491*613a2f6bSGordon Ross 492*613a2f6bSGordon Ross ss = calloc(1, sizeof (*ss)); 493*613a2f6bSGordon Ross if (ss == NULL) 494*613a2f6bSGordon Ross return (ENOMEM); 495*613a2f6bSGordon Ross 496*613a2f6bSGordon Ross sp->sp_nexttok = krb5ssp_next_token; 497*613a2f6bSGordon Ross sp->sp_destroy = krb5ssp_destroy; 498*613a2f6bSGordon Ross sp->sp_private = ss; 499*613a2f6bSGordon Ross 500*613a2f6bSGordon Ross kerr = krb5_init_context(&kctx); 501*613a2f6bSGordon Ross if (kerr) { 502*613a2f6bSGordon Ross DPRINT("krb5_init_context, kerr 0x%x", kerr); 503*613a2f6bSGordon Ross goto errout; 504*613a2f6bSGordon Ross } 505*613a2f6bSGordon Ross ss->ss_krb5ctx = kctx; 506*613a2f6bSGordon Ross 507*613a2f6bSGordon Ross /* non-default would instead use krb5_cc_resolve */ 508*613a2f6bSGordon Ross kerr = krb5_cc_default(kctx, &kcc); 509*613a2f6bSGordon Ross if (kerr) { 510*613a2f6bSGordon Ross DPRINT("krb5_cc_default, kerr 0x%x", kerr); 511*613a2f6bSGordon Ross goto errout; 512*613a2f6bSGordon Ross } 513*613a2f6bSGordon Ross ss->ss_krb5cc = kcc; 514*613a2f6bSGordon Ross 515*613a2f6bSGordon Ross /* 516*613a2f6bSGordon Ross * Get the client principal (ticket), 517*613a2f6bSGordon Ross * or discover that we don't have one. 518*613a2f6bSGordon Ross */ 519*613a2f6bSGordon Ross kerr = krb5_cc_get_principal(kctx, kcc, &kprin); 520*613a2f6bSGordon Ross if (kerr) { 521*613a2f6bSGordon Ross DPRINT("krb5_cc_get_principal, kerr 0x%x", kerr); 522*613a2f6bSGordon Ross goto errout; 523*613a2f6bSGordon Ross } 524*613a2f6bSGordon Ross ss->ss_krb5clp = kprin; 525*613a2f6bSGordon Ross 526*613a2f6bSGordon Ross /* Success! */ 527*613a2f6bSGordon Ross DPRINT("Ticket cache: %s:%s", 528*613a2f6bSGordon Ross krb5_cc_get_type(kctx, kcc), 529*613a2f6bSGordon Ross krb5_cc_get_name(kctx, kcc)); 530*613a2f6bSGordon Ross return (0); 531*613a2f6bSGordon Ross 532*613a2f6bSGordon Ross errout: 533*613a2f6bSGordon Ross krb5ssp_destroy(sp); 534*613a2f6bSGordon Ross return (ENOTSUP); 535*613a2f6bSGordon Ross } 536