1613a2f6bSGordon Ross /* 2613a2f6bSGordon Ross * Copyright (c) 2000, Boris Popov 3613a2f6bSGordon Ross * All rights reserved. 4613a2f6bSGordon Ross * 5613a2f6bSGordon Ross * Redistribution and use in source and binary forms, with or without 6613a2f6bSGordon Ross * modification, are permitted provided that the following conditions 7613a2f6bSGordon Ross * are met: 8613a2f6bSGordon Ross * 1. Redistributions of source code must retain the above copyright 9613a2f6bSGordon Ross * notice, this list of conditions and the following disclaimer. 10613a2f6bSGordon Ross * 2. Redistributions in binary form must reproduce the above copyright 11613a2f6bSGordon Ross * notice, this list of conditions and the following disclaimer in the 12613a2f6bSGordon Ross * documentation and/or other materials provided with the distribution. 13613a2f6bSGordon Ross * 3. All advertising materials mentioning features or use of this software 14613a2f6bSGordon Ross * must display the following acknowledgement: 15613a2f6bSGordon Ross * This product includes software developed by Boris Popov. 16613a2f6bSGordon Ross * 4. Neither the name of the author nor the names of any co-contributors 17613a2f6bSGordon Ross * may be used to endorse or promote products derived from this software 18613a2f6bSGordon Ross * without specific prior written permission. 19613a2f6bSGordon Ross * 20613a2f6bSGordon Ross * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21613a2f6bSGordon Ross * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22613a2f6bSGordon Ross * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23613a2f6bSGordon Ross * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24613a2f6bSGordon Ross * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25613a2f6bSGordon Ross * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26613a2f6bSGordon Ross * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27613a2f6bSGordon Ross * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28613a2f6bSGordon Ross * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29613a2f6bSGordon Ross * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30613a2f6bSGordon Ross * SUCH DAMAGE. 31613a2f6bSGordon Ross */ 32613a2f6bSGordon Ross 33613a2f6bSGordon Ross /* 34*15359501SGordon Ross * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 35613a2f6bSGordon Ross */ 36613a2f6bSGordon Ross 37613a2f6bSGordon Ross /* 38613a2f6bSGordon Ross * Kerberos V Security Support Provider 39613a2f6bSGordon Ross * 40613a2f6bSGordon Ross * Based on code previously in ctx.c (from Boris Popov?) 41613a2f6bSGordon Ross * but then mostly rewritten at Sun. 42613a2f6bSGordon Ross */ 43613a2f6bSGordon Ross 44613a2f6bSGordon Ross #include <errno.h> 45613a2f6bSGordon Ross #include <stdio.h> 46613a2f6bSGordon Ross #include <stddef.h> 47613a2f6bSGordon Ross #include <stdlib.h> 48613a2f6bSGordon Ross #include <unistd.h> 49613a2f6bSGordon Ross #include <strings.h> 50613a2f6bSGordon Ross #include <netdb.h> 51613a2f6bSGordon Ross #include <libintl.h> 52613a2f6bSGordon Ross #include <xti.h> 53613a2f6bSGordon Ross #include <assert.h> 54613a2f6bSGordon Ross 55613a2f6bSGordon Ross #include <sys/types.h> 56613a2f6bSGordon Ross #include <sys/time.h> 57613a2f6bSGordon Ross #include <sys/byteorder.h> 58613a2f6bSGordon Ross #include <sys/socket.h> 59613a2f6bSGordon Ross #include <sys/fcntl.h> 60613a2f6bSGordon Ross 61613a2f6bSGordon Ross #include <netinet/in.h> 62613a2f6bSGordon Ross #include <netinet/tcp.h> 63613a2f6bSGordon Ross #include <arpa/inet.h> 64613a2f6bSGordon Ross 65613a2f6bSGordon Ross #include <netsmb/smb.h> 66613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 67613a2f6bSGordon Ross #include <netsmb/mchain.h> 68613a2f6bSGordon Ross 69613a2f6bSGordon Ross #include "private.h" 70613a2f6bSGordon Ross #include "charsets.h" 71613a2f6bSGordon Ross #include "spnego.h" 72613a2f6bSGordon Ross #include "derparse.h" 73613a2f6bSGordon Ross #include "ssp.h" 74613a2f6bSGordon Ross 75613a2f6bSGordon Ross #include <kerberosv5/krb5.h> 76613a2f6bSGordon Ross #include <kerberosv5/com_err.h> 77*15359501SGordon Ross #include <gssapi/gssapi.h> 78*15359501SGordon Ross #include <gssapi/mechs/krb5/include/auth_con.h> 79*15359501SGordon Ross 80*15359501SGordon Ross /* RFC 4121 checksum type ID. */ 81*15359501SGordon Ross #define CKSUM_TYPE_RFC4121 0x8003 82613a2f6bSGordon Ross 83613a2f6bSGordon Ross /* RFC 1964 token ID codes */ 84613a2f6bSGordon Ross #define KRB_AP_REQ 1 85613a2f6bSGordon Ross #define KRB_AP_REP 2 86613a2f6bSGordon Ross #define KRB_ERROR 3 87613a2f6bSGordon Ross 88613a2f6bSGordon Ross extern MECH_OID g_stcMechOIDList []; 89613a2f6bSGordon Ross 90613a2f6bSGordon Ross typedef struct krb5ssp_state { 91613a2f6bSGordon Ross /* Filled in by krb5ssp_init_client */ 92613a2f6bSGordon Ross krb5_context ss_krb5ctx; /* krb5 context (ptr) */ 93613a2f6bSGordon Ross krb5_ccache ss_krb5cc; /* credentials cache (ptr) */ 94613a2f6bSGordon Ross krb5_principal ss_krb5clp; /* client principal (ptr) */ 95613a2f6bSGordon Ross /* Filled in by krb5ssp_get_tkt */ 96613a2f6bSGordon Ross krb5_auth_context ss_auth; /* auth ctx. w/ server (ptr) */ 97613a2f6bSGordon Ross } krb5ssp_state_t; 98613a2f6bSGordon Ross 99613a2f6bSGordon Ross 100613a2f6bSGordon Ross /* 101613a2f6bSGordon Ross * adds a GSSAPI wrapper 102613a2f6bSGordon Ross */ 103613a2f6bSGordon Ross int 104613a2f6bSGordon Ross krb5ssp_tkt2gtok(uchar_t *tkt, ulong_t tktlen, 105613a2f6bSGordon Ross uchar_t **gtokp, ulong_t *gtoklenp) 106613a2f6bSGordon Ross { 107613a2f6bSGordon Ross ulong_t len; 108613a2f6bSGordon Ross ulong_t bloblen = tktlen; 109613a2f6bSGordon Ross uchar_t krbapreq[2] = { KRB_AP_REQ, 0 }; 110613a2f6bSGordon Ross uchar_t *blob = NULL; /* result */ 111613a2f6bSGordon Ross uchar_t *b; 112613a2f6bSGordon Ross 113613a2f6bSGordon Ross bloblen += sizeof (krbapreq); 114613a2f6bSGordon Ross bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen; 115613a2f6bSGordon Ross len = bloblen; 116613a2f6bSGordon Ross bloblen = ASNDerCalcTokenLength(bloblen, bloblen); 117613a2f6bSGordon Ross if ((blob = malloc(bloblen)) == NULL) { 118613a2f6bSGordon Ross DPRINT("malloc"); 119613a2f6bSGordon Ross return (ENOMEM); 120613a2f6bSGordon Ross } 121613a2f6bSGordon Ross 122613a2f6bSGordon Ross b = blob; 123613a2f6bSGordon Ross b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len); 124613a2f6bSGordon Ross b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5); 125613a2f6bSGordon Ross memcpy(b, krbapreq, sizeof (krbapreq)); 126613a2f6bSGordon Ross b += sizeof (krbapreq); 127613a2f6bSGordon Ross 128613a2f6bSGordon Ross assert(b + tktlen == blob + bloblen); 129613a2f6bSGordon Ross memcpy(b, tkt, tktlen); 130613a2f6bSGordon Ross *gtoklenp = bloblen; 131613a2f6bSGordon Ross *gtokp = blob; 132613a2f6bSGordon Ross return (0); 133613a2f6bSGordon Ross } 134613a2f6bSGordon Ross 135613a2f6bSGordon Ross /* 136613a2f6bSGordon Ross * See "Windows 2000 Kerberos Interoperability" paper by 137613a2f6bSGordon Ross * Christopher Nebergall. RC4 HMAC is the W2K default but 138613a2f6bSGordon Ross * Samba support lagged (not due to Samba itself, but due to OS' 139613a2f6bSGordon Ross * Kerberos implementations.) 140613a2f6bSGordon Ross * 141613a2f6bSGordon Ross * Only session enc type should matter, not ticket enc type, 142613a2f6bSGordon Ross * per Sam Hartman on krbdev. 143613a2f6bSGordon Ross * 144613a2f6bSGordon Ross * Preauthentication failure topics in krb-protocol may help here... 145613a2f6bSGordon Ross * try "John Brezak" and/or "Clifford Neuman" too. 146613a2f6bSGordon Ross */ 147613a2f6bSGordon Ross static krb5_enctype kenctypes[] = { 148613a2f6bSGordon Ross ENCTYPE_ARCFOUR_HMAC, /* defined in krb5.h */ 149613a2f6bSGordon Ross ENCTYPE_DES_CBC_MD5, 150613a2f6bSGordon Ross ENCTYPE_DES_CBC_CRC, 151613a2f6bSGordon Ross ENCTYPE_NULL 152613a2f6bSGordon Ross }; 153613a2f6bSGordon Ross 154613a2f6bSGordon Ross static const int rq_opts = 155613a2f6bSGordon Ross AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED; 156613a2f6bSGordon Ross 157613a2f6bSGordon Ross /* 158613a2f6bSGordon Ross * Obtain a kerberos ticket for the host we're connecting to. 159613a2f6bSGordon Ross * (This does the KRB_TGS exchange.) 160613a2f6bSGordon Ross */ 161613a2f6bSGordon Ross static int 162613a2f6bSGordon Ross krb5ssp_get_tkt(krb5ssp_state_t *ss, char *server, 163613a2f6bSGordon Ross uchar_t **tktp, ulong_t *tktlenp) 164613a2f6bSGordon Ross { 165613a2f6bSGordon Ross krb5_context kctx = ss->ss_krb5ctx; 166613a2f6bSGordon Ross krb5_ccache kcc = ss->ss_krb5cc; 167613a2f6bSGordon Ross krb5_data indata = {0}; 168613a2f6bSGordon Ross krb5_data outdata = {0}; 169613a2f6bSGordon Ross krb5_error_code kerr = 0; 170613a2f6bSGordon Ross const char *fn = NULL; 171613a2f6bSGordon Ross uchar_t *tkt; 172613a2f6bSGordon Ross 173613a2f6bSGordon Ross /* Should have these from krb5ssp_init_client. */ 174613a2f6bSGordon Ross if (kctx == NULL || kcc == NULL) { 175613a2f6bSGordon Ross fn = "null kctx or kcc"; 176613a2f6bSGordon Ross kerr = EINVAL; 177613a2f6bSGordon Ross goto out; 178613a2f6bSGordon Ross } 179613a2f6bSGordon Ross 180613a2f6bSGordon Ross kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes); 181613a2f6bSGordon Ross if (kerr != 0) { 182613a2f6bSGordon Ross fn = "krb5_set_default_tgs_enctypes"; 183613a2f6bSGordon Ross goto out; 184613a2f6bSGordon Ross } 185613a2f6bSGordon Ross 186*15359501SGordon Ross /* Get ss_auth now so we can set req_chsumtype. */ 187*15359501SGordon Ross kerr = krb5_auth_con_init(kctx, &ss->ss_auth); 188*15359501SGordon Ross if (kerr != 0) { 189*15359501SGordon Ross fn = "krb5_auth_con_init"; 190*15359501SGordon Ross goto out; 191*15359501SGordon Ross } 192*15359501SGordon Ross /* Missing krb5_auth_con_set_req_cksumtype(), so inline. */ 193*15359501SGordon Ross ss->ss_auth->req_cksumtype = CKSUM_TYPE_RFC4121; 194*15359501SGordon Ross 195*15359501SGordon Ross /* 196*15359501SGordon Ross * Build an RFC 4121 "checksum" with NULL channel bindings, 197*15359501SGordon Ross * like make_gss_checksum(). Numbers here from the RFC. 198*15359501SGordon Ross */ 199*15359501SGordon Ross indata.length = 24; 200*15359501SGordon Ross if ((indata.data = calloc(1, indata.length)) == NULL) { 201*15359501SGordon Ross kerr = ENOMEM; 202*15359501SGordon Ross fn = "malloc checksum"; 203*15359501SGordon Ross goto out; 204*15359501SGordon Ross } 205*15359501SGordon Ross indata.data[0] = 16; /* length of "Bnd" field. */ 206*15359501SGordon Ross indata.data[20] = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG; 207*15359501SGordon Ross /* Done building the "checksum". */ 208613a2f6bSGordon Ross 209613a2f6bSGordon Ross kerr = krb5_mk_req(kctx, &ss->ss_auth, rq_opts, "cifs", server, 210613a2f6bSGordon Ross &indata, kcc, &outdata); 211613a2f6bSGordon Ross if (kerr != 0) { 212613a2f6bSGordon Ross fn = "krb5_mk_req"; 213613a2f6bSGordon Ross goto out; 214613a2f6bSGordon Ross } 215613a2f6bSGordon Ross if ((tkt = malloc(outdata.length)) == NULL) { 216613a2f6bSGordon Ross kerr = ENOMEM; 217613a2f6bSGordon Ross fn = "malloc signing key"; 218613a2f6bSGordon Ross goto out; 219613a2f6bSGordon Ross } 220613a2f6bSGordon Ross memcpy(tkt, outdata.data, outdata.length); 221613a2f6bSGordon Ross *tktp = tkt; 222613a2f6bSGordon Ross *tktlenp = outdata.length; 223613a2f6bSGordon Ross kerr = 0; 224613a2f6bSGordon Ross 225613a2f6bSGordon Ross out: 226613a2f6bSGordon Ross if (kerr) { 227613a2f6bSGordon Ross if (fn == NULL) 228613a2f6bSGordon Ross fn = "?"; 229613a2f6bSGordon Ross DPRINT("%s err 0x%x: %s", fn, kerr, error_message(kerr)); 230613a2f6bSGordon Ross if (kerr <= 0 || kerr > ESTALE) 231613a2f6bSGordon Ross kerr = EAUTH; 232613a2f6bSGordon Ross } 233613a2f6bSGordon Ross 234613a2f6bSGordon Ross if (outdata.data) 235613a2f6bSGordon Ross krb5_free_data_contents(kctx, &outdata); 236613a2f6bSGordon Ross 237*15359501SGordon Ross if (indata.data) 238*15359501SGordon Ross free(indata.data); 239*15359501SGordon Ross 240613a2f6bSGordon Ross /* Free kctx in krb5ssp_destroy */ 241613a2f6bSGordon Ross return (kerr); 242613a2f6bSGordon Ross } 243613a2f6bSGordon Ross 244613a2f6bSGordon Ross 245613a2f6bSGordon Ross /* 246613a2f6bSGordon Ross * Build an RFC 1964 KRB_AP_REQ message 247613a2f6bSGordon Ross * The caller puts on the SPNEGO wrapper. 248613a2f6bSGordon Ross */ 249613a2f6bSGordon Ross int 250613a2f6bSGordon Ross krb5ssp_put_request(struct ssp_ctx *sp, struct mbdata *out_mb) 251613a2f6bSGordon Ross { 252613a2f6bSGordon Ross int err; 253613a2f6bSGordon Ross struct smb_ctx *ctx = sp->smb_ctx; 254613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 255613a2f6bSGordon Ross uchar_t *tkt = NULL; 256613a2f6bSGordon Ross ulong_t tktlen; 257613a2f6bSGordon Ross uchar_t *gtok = NULL; /* gssapi token */ 258613a2f6bSGordon Ross ulong_t gtoklen; /* gssapi token length */ 259613a2f6bSGordon Ross char *prin = ctx->ct_srvname; 260613a2f6bSGordon Ross 261613a2f6bSGordon Ross if ((err = krb5ssp_get_tkt(ss, prin, &tkt, &tktlen)) != 0) 262613a2f6bSGordon Ross goto out; 263613a2f6bSGordon Ross if ((err = krb5ssp_tkt2gtok(tkt, tktlen, >ok, >oklen)) != 0) 264613a2f6bSGordon Ross goto out; 265613a2f6bSGordon Ross 26602d09e03SGordon Ross if ((err = mb_init_sz(out_mb, gtoklen)) != 0) 267613a2f6bSGordon Ross goto out; 26802d09e03SGordon Ross if ((err = mb_put_mem(out_mb, gtok, gtoklen, MB_MSYSTEM)) != 0) 269613a2f6bSGordon Ross goto out; 270613a2f6bSGordon Ross 271613a2f6bSGordon Ross if (ctx->ct_vcflags & SMBV_WILL_SIGN) 272613a2f6bSGordon Ross ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; 273613a2f6bSGordon Ross 274613a2f6bSGordon Ross out: 275613a2f6bSGordon Ross if (gtok) 276613a2f6bSGordon Ross free(gtok); 277613a2f6bSGordon Ross if (tkt) 278613a2f6bSGordon Ross free(tkt); 279613a2f6bSGordon Ross 280613a2f6bSGordon Ross return (err); 281613a2f6bSGordon Ross } 282613a2f6bSGordon Ross 283613a2f6bSGordon Ross /* 284613a2f6bSGordon Ross * Unwrap a GSS-API encapsulated RFC 1964 reply message, 285613a2f6bSGordon Ross * i.e. type KRB_AP_REP or KRB_ERROR. 286613a2f6bSGordon Ross */ 287613a2f6bSGordon Ross int 288613a2f6bSGordon Ross krb5ssp_get_reply(struct ssp_ctx *sp, struct mbdata *in_mb) 289613a2f6bSGordon Ross { 290613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 291613a2f6bSGordon Ross mbuf_t *m = in_mb->mb_top; 292613a2f6bSGordon Ross int err = EBADRPC; 293613a2f6bSGordon Ross int dlen, rc; 294613a2f6bSGordon Ross long actual_len, token_len; 295613a2f6bSGordon Ross uchar_t *data; 296613a2f6bSGordon Ross krb5_data ap = {0}; 297613a2f6bSGordon Ross krb5_ap_rep_enc_part *reply = NULL; 298613a2f6bSGordon Ross 299613a2f6bSGordon Ross /* cheating: this mbuf is contiguous */ 300613a2f6bSGordon Ross assert(m->m_data == in_mb->mb_pos); 301613a2f6bSGordon Ross data = (uchar_t *)m->m_data; 302613a2f6bSGordon Ross dlen = m->m_len; 303613a2f6bSGordon Ross 304613a2f6bSGordon Ross /* 305613a2f6bSGordon Ross * Peel off the GSS-API wrapper. Looks like: 306613a2f6bSGordon Ross * AppToken: 60 81 83 307613a2f6bSGordon Ross * OID(KRB5): 06 09 2a 86 48 86 f7 12 01 02 02 308613a2f6bSGordon Ross * KRB_AP_REP: 02 00 309613a2f6bSGordon Ross */ 310613a2f6bSGordon Ross rc = ASNDerCheckToken(data, SPNEGO_NEGINIT_APP_CONSTRUCT, 311613a2f6bSGordon Ross 0, dlen, &token_len, &actual_len); 312613a2f6bSGordon Ross if (rc != SPNEGO_E_SUCCESS) { 313613a2f6bSGordon Ross DPRINT("no AppToken? rc=0x%x", rc); 314613a2f6bSGordon Ross goto out; 315613a2f6bSGordon Ross } 316613a2f6bSGordon Ross if (dlen < actual_len) 317613a2f6bSGordon Ross goto out; 318613a2f6bSGordon Ross data += actual_len; 319613a2f6bSGordon Ross dlen -= actual_len; 320613a2f6bSGordon Ross 321613a2f6bSGordon Ross /* OID (KRB5) */ 322613a2f6bSGordon Ross rc = ASNDerCheckOID(data, spnego_mech_oid_Kerberos_V5, 323613a2f6bSGordon Ross dlen, &actual_len); 324613a2f6bSGordon Ross if (rc != SPNEGO_E_SUCCESS) { 325613a2f6bSGordon Ross DPRINT("no OID? rc=0x%x", rc); 326613a2f6bSGordon Ross goto out; 327613a2f6bSGordon Ross } 328613a2f6bSGordon Ross if (dlen < actual_len) 329613a2f6bSGordon Ross goto out; 330613a2f6bSGordon Ross data += actual_len; 331613a2f6bSGordon Ross dlen -= actual_len; 332613a2f6bSGordon Ross 333613a2f6bSGordon Ross /* KRB_AP_REP or KRB_ERROR */ 334613a2f6bSGordon Ross if (data[0] != KRB_AP_REP) { 335613a2f6bSGordon Ross DPRINT("KRB5 type: %d", data[1]); 336613a2f6bSGordon Ross goto out; 337613a2f6bSGordon Ross } 338613a2f6bSGordon Ross if (dlen < 2) 339613a2f6bSGordon Ross goto out; 340613a2f6bSGordon Ross data += 2; 341613a2f6bSGordon Ross dlen -= 2; 342613a2f6bSGordon Ross 343613a2f6bSGordon Ross /* 344613a2f6bSGordon Ross * Now what's left should be a krb5 reply 345613a2f6bSGordon Ross * NB: ap is NOT allocated, so don't free it. 346613a2f6bSGordon Ross */ 347613a2f6bSGordon Ross ap.length = dlen; 348613a2f6bSGordon Ross ap.data = (char *)data; 349613a2f6bSGordon Ross rc = krb5_rd_rep(ss->ss_krb5ctx, ss->ss_auth, &ap, &reply); 350613a2f6bSGordon Ross if (rc != 0) { 351613a2f6bSGordon Ross DPRINT("krb5_rd_rep: err 0x%x (%s)", 352613a2f6bSGordon Ross rc, error_message(rc)); 353613a2f6bSGordon Ross err = EAUTH; 354613a2f6bSGordon Ross goto out; 355613a2f6bSGordon Ross } 356613a2f6bSGordon Ross 357613a2f6bSGordon Ross /* 358613a2f6bSGordon Ross * Have the decoded reply. Save anything? 359613a2f6bSGordon Ross * 360613a2f6bSGordon Ross * NB: If this returns an error, we will get 361613a2f6bSGordon Ross * no more calls into this back-end module. 362613a2f6bSGordon Ross */ 363613a2f6bSGordon Ross err = 0; 364613a2f6bSGordon Ross 365613a2f6bSGordon Ross out: 366613a2f6bSGordon Ross if (reply != NULL) 367613a2f6bSGordon Ross krb5_free_ap_rep_enc_part(ss->ss_krb5ctx, reply); 368613a2f6bSGordon Ross if (err) 369613a2f6bSGordon Ross DPRINT("ret %d", err); 370613a2f6bSGordon Ross 371613a2f6bSGordon Ross return (err); 372613a2f6bSGordon Ross } 373613a2f6bSGordon Ross 374613a2f6bSGordon Ross /* 375613a2f6bSGordon Ross * krb5ssp_final 376613a2f6bSGordon Ross * 377613a2f6bSGordon Ross * Called after successful authentication. 378613a2f6bSGordon Ross * Setup the MAC key for signing. 379613a2f6bSGordon Ross */ 380613a2f6bSGordon Ross int 381613a2f6bSGordon Ross krb5ssp_final(struct ssp_ctx *sp) 382613a2f6bSGordon Ross { 383613a2f6bSGordon Ross struct smb_ctx *ctx = sp->smb_ctx; 384613a2f6bSGordon Ross krb5ssp_state_t *ss = sp->sp_private; 385613a2f6bSGordon Ross krb5_keyblock *ssn_key = NULL; 386613a2f6bSGordon Ross int err, len; 387613a2f6bSGordon Ross 388613a2f6bSGordon Ross /* 389613a2f6bSGordon Ross * Save the session key, used for SMB signing 390613a2f6bSGordon Ross * and possibly other consumers (RPC). 391613a2f6bSGordon Ross */ 392613a2f6bSGordon Ross err = krb5_auth_con_getlocalsubkey( 393613a2f6bSGordon Ross ss->ss_krb5ctx, ss->ss_auth, &ssn_key); 394613a2f6bSGordon Ross if (err != 0) { 395613a2f6bSGordon Ross DPRINT("_getlocalsubkey, err=0x%x (%s)", 396613a2f6bSGordon Ross err, error_message(err)); 397613a2f6bSGordon Ross if (err <= 0 || err > ESTALE) 398613a2f6bSGordon Ross err = EAUTH; 399613a2f6bSGordon Ross goto out; 400613a2f6bSGordon Ross } 401613a2f6bSGordon Ross memset(ctx->ct_ssn_key, 0, SMBIOC_HASH_SZ); 402613a2f6bSGordon Ross if ((len = ssn_key->length) > SMBIOC_HASH_SZ) 403613a2f6bSGordon Ross len = SMBIOC_HASH_SZ; 404613a2f6bSGordon Ross memcpy(ctx->ct_ssn_key, ssn_key->contents, len); 405613a2f6bSGordon Ross 406613a2f6bSGordon Ross /* 407613a2f6bSGordon Ross * Set the MAC key on the first successful auth. 408613a2f6bSGordon Ross */ 409613a2f6bSGordon Ross if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) && 410613a2f6bSGordon Ross (ctx->ct_mackey == NULL)) { 411613a2f6bSGordon Ross ctx->ct_mackeylen = ssn_key->length; 412613a2f6bSGordon Ross ctx->ct_mackey = malloc(ctx->ct_mackeylen); 413613a2f6bSGordon Ross if (ctx->ct_mackey == NULL) { 414613a2f6bSGordon Ross ctx->ct_mackeylen = 0; 415613a2f6bSGordon Ross err = ENOMEM; 416613a2f6bSGordon Ross goto out; 417613a2f6bSGordon Ross } 418613a2f6bSGordon Ross memcpy(ctx->ct_mackey, ssn_key->contents, 419613a2f6bSGordon Ross ctx->ct_mackeylen); 420613a2f6bSGordon Ross /* 421613a2f6bSGordon Ross * Apparently, the server used seq. no. zero 422613a2f6bSGordon Ross * for our previous message, so next is two. 423613a2f6bSGordon Ross */ 424613a2f6bSGordon Ross ctx->ct_mac_seqno = 2; 425613a2f6bSGordon Ross } 426613a2f6bSGordon Ross err = 0; 427613a2f6bSGordon Ross 428613a2f6bSGordon Ross out: 429613a2f6bSGordon Ross if (ssn_key) 430613a2f6bSGordon Ross krb5_free_keyblock(ss->ss_krb5ctx, ssn_key); 431613a2f6bSGordon Ross 432613a2f6bSGordon Ross return (err); 433613a2f6bSGordon Ross } 434613a2f6bSGordon Ross 435613a2f6bSGordon Ross /* 436613a2f6bSGordon Ross * krb5ssp_next_token 437613a2f6bSGordon Ross * 438613a2f6bSGordon Ross * See ssp.c: ssp_ctx_next_token 439613a2f6bSGordon Ross */ 440613a2f6bSGordon Ross int 441613a2f6bSGordon Ross krb5ssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb, 442613a2f6bSGordon Ross struct mbdata *out_mb) 443613a2f6bSGordon Ross { 444613a2f6bSGordon Ross int err; 445613a2f6bSGordon Ross 446613a2f6bSGordon Ross /* 447613a2f6bSGordon Ross * Note: in_mb == NULL on the first call. 448613a2f6bSGordon Ross */ 449613a2f6bSGordon Ross if (in_mb) { 450613a2f6bSGordon Ross err = krb5ssp_get_reply(sp, in_mb); 451613a2f6bSGordon Ross if (err) 452613a2f6bSGordon Ross goto out; 453613a2f6bSGordon Ross } 454613a2f6bSGordon Ross 455613a2f6bSGordon Ross if (out_mb) { 456613a2f6bSGordon Ross err = krb5ssp_put_request(sp, out_mb); 457613a2f6bSGordon Ross } else 458613a2f6bSGordon Ross err = krb5ssp_final(sp); 459613a2f6bSGordon Ross 460613a2f6bSGordon Ross out: 461613a2f6bSGordon Ross if (err) 462613a2f6bSGordon Ross DPRINT("ret: %d", err); 463613a2f6bSGordon Ross return (err); 464613a2f6bSGordon Ross } 465613a2f6bSGordon Ross 466613a2f6bSGordon Ross /* 467613a2f6bSGordon Ross * krb5ssp_ctx_destroy 468613a2f6bSGordon Ross * 469613a2f6bSGordon Ross * Destroy mechanism-specific data. 470613a2f6bSGordon Ross */ 471613a2f6bSGordon Ross void 472613a2f6bSGordon Ross krb5ssp_destroy(struct ssp_ctx *sp) 473613a2f6bSGordon Ross { 474613a2f6bSGordon Ross krb5ssp_state_t *ss; 475613a2f6bSGordon Ross krb5_context kctx; 476613a2f6bSGordon Ross 477613a2f6bSGordon Ross ss = sp->sp_private; 478613a2f6bSGordon Ross if (ss == NULL) 479613a2f6bSGordon Ross return; 480613a2f6bSGordon Ross sp->sp_private = NULL; 481613a2f6bSGordon Ross 482613a2f6bSGordon Ross if ((kctx = ss->ss_krb5ctx) != NULL) { 483613a2f6bSGordon Ross /* from krb5ssp_get_tkt */ 484613a2f6bSGordon Ross if (ss->ss_auth) 48505cffdd1SGordon Ross (void) krb5_auth_con_free(kctx, ss->ss_auth); 486613a2f6bSGordon Ross /* from krb5ssp_init_client */ 487613a2f6bSGordon Ross if (ss->ss_krb5clp) 488613a2f6bSGordon Ross krb5_free_principal(kctx, ss->ss_krb5clp); 489613a2f6bSGordon Ross if (ss->ss_krb5cc) 49005cffdd1SGordon Ross (void) krb5_cc_close(kctx, ss->ss_krb5cc); 491613a2f6bSGordon Ross krb5_free_context(kctx); 492613a2f6bSGordon Ross } 493613a2f6bSGordon Ross 494613a2f6bSGordon Ross free(ss); 495613a2f6bSGordon Ross } 496613a2f6bSGordon Ross 497613a2f6bSGordon Ross /* 498613a2f6bSGordon Ross * krb5ssp_init_clnt 499613a2f6bSGordon Ross * 500613a2f6bSGordon Ross * Initialize a new Kerberos SSP client context. 501613a2f6bSGordon Ross * 502613a2f6bSGordon Ross * The user must already have a TGT in their credential cache, 503613a2f6bSGordon Ross * as shown by the "klist" command. 504613a2f6bSGordon Ross */ 505613a2f6bSGordon Ross int 506613a2f6bSGordon Ross krb5ssp_init_client(struct ssp_ctx *sp) 507613a2f6bSGordon Ross { 508613a2f6bSGordon Ross krb5ssp_state_t *ss; 509613a2f6bSGordon Ross krb5_error_code kerr; 510613a2f6bSGordon Ross krb5_context kctx = NULL; 511613a2f6bSGordon Ross krb5_ccache kcc = NULL; 512613a2f6bSGordon Ross krb5_principal kprin = NULL; 513613a2f6bSGordon Ross 514613a2f6bSGordon Ross if ((sp->smb_ctx->ct_authflags & SMB_AT_KRB5) == 0) { 515613a2f6bSGordon Ross DPRINT("KRB5 not in authflags"); 516613a2f6bSGordon Ross return (ENOTSUP); 517613a2f6bSGordon Ross } 518613a2f6bSGordon Ross 519613a2f6bSGordon Ross ss = calloc(1, sizeof (*ss)); 520613a2f6bSGordon Ross if (ss == NULL) 521613a2f6bSGordon Ross return (ENOMEM); 522613a2f6bSGordon Ross 523613a2f6bSGordon Ross sp->sp_nexttok = krb5ssp_next_token; 524613a2f6bSGordon Ross sp->sp_destroy = krb5ssp_destroy; 525613a2f6bSGordon Ross sp->sp_private = ss; 526613a2f6bSGordon Ross 527613a2f6bSGordon Ross kerr = krb5_init_context(&kctx); 528613a2f6bSGordon Ross if (kerr) { 529613a2f6bSGordon Ross DPRINT("krb5_init_context, kerr 0x%x", kerr); 530613a2f6bSGordon Ross goto errout; 531613a2f6bSGordon Ross } 532613a2f6bSGordon Ross ss->ss_krb5ctx = kctx; 533613a2f6bSGordon Ross 534613a2f6bSGordon Ross /* non-default would instead use krb5_cc_resolve */ 535613a2f6bSGordon Ross kerr = krb5_cc_default(kctx, &kcc); 536613a2f6bSGordon Ross if (kerr) { 537613a2f6bSGordon Ross DPRINT("krb5_cc_default, kerr 0x%x", kerr); 538613a2f6bSGordon Ross goto errout; 539613a2f6bSGordon Ross } 540613a2f6bSGordon Ross ss->ss_krb5cc = kcc; 541613a2f6bSGordon Ross 542613a2f6bSGordon Ross /* 543613a2f6bSGordon Ross * Get the client principal (ticket), 544613a2f6bSGordon Ross * or discover that we don't have one. 545613a2f6bSGordon Ross */ 546613a2f6bSGordon Ross kerr = krb5_cc_get_principal(kctx, kcc, &kprin); 547613a2f6bSGordon Ross if (kerr) { 548613a2f6bSGordon Ross DPRINT("krb5_cc_get_principal, kerr 0x%x", kerr); 549613a2f6bSGordon Ross goto errout; 550613a2f6bSGordon Ross } 551613a2f6bSGordon Ross ss->ss_krb5clp = kprin; 552613a2f6bSGordon Ross 553613a2f6bSGordon Ross /* Success! */ 554613a2f6bSGordon Ross DPRINT("Ticket cache: %s:%s", 555613a2f6bSGordon Ross krb5_cc_get_type(kctx, kcc), 556613a2f6bSGordon Ross krb5_cc_get_name(kctx, kcc)); 557613a2f6bSGordon Ross return (0); 558613a2f6bSGordon Ross 559613a2f6bSGordon Ross errout: 560613a2f6bSGordon Ross krb5ssp_destroy(sp); 561613a2f6bSGordon Ross return (ENOTSUP); 562613a2f6bSGordon Ross } 563