110023SGordon.Ross@Sun.COM /* 210023SGordon.Ross@Sun.COM * Copyright (c) 2000, Boris Popov 310023SGordon.Ross@Sun.COM * All rights reserved. 410023SGordon.Ross@Sun.COM * 510023SGordon.Ross@Sun.COM * Redistribution and use in source and binary forms, with or without 610023SGordon.Ross@Sun.COM * modification, are permitted provided that the following conditions 710023SGordon.Ross@Sun.COM * are met: 810023SGordon.Ross@Sun.COM * 1. Redistributions of source code must retain the above copyright 910023SGordon.Ross@Sun.COM * notice, this list of conditions and the following disclaimer. 1010023SGordon.Ross@Sun.COM * 2. Redistributions in binary form must reproduce the above copyright 1110023SGordon.Ross@Sun.COM * notice, this list of conditions and the following disclaimer in the 1210023SGordon.Ross@Sun.COM * documentation and/or other materials provided with the distribution. 1310023SGordon.Ross@Sun.COM * 3. All advertising materials mentioning features or use of this software 1410023SGordon.Ross@Sun.COM * must display the following acknowledgement: 1510023SGordon.Ross@Sun.COM * This product includes software developed by Boris Popov. 1610023SGordon.Ross@Sun.COM * 4. Neither the name of the author nor the names of any co-contributors 1710023SGordon.Ross@Sun.COM * may be used to endorse or promote products derived from this software 1810023SGordon.Ross@Sun.COM * without specific prior written permission. 1910023SGordon.Ross@Sun.COM * 2010023SGordon.Ross@Sun.COM * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2110023SGordon.Ross@Sun.COM * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2210023SGordon.Ross@Sun.COM * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2310023SGordon.Ross@Sun.COM * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2410023SGordon.Ross@Sun.COM * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2510023SGordon.Ross@Sun.COM * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2610023SGordon.Ross@Sun.COM * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2710023SGordon.Ross@Sun.COM * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2810023SGordon.Ross@Sun.COM * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2910023SGordon.Ross@Sun.COM * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3010023SGordon.Ross@Sun.COM * SUCH DAMAGE. 3110023SGordon.Ross@Sun.COM */ 3210023SGordon.Ross@Sun.COM 3310023SGordon.Ross@Sun.COM /* 3410023SGordon.Ross@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3510023SGordon.Ross@Sun.COM * Use is subject to license terms. 3610023SGordon.Ross@Sun.COM */ 3710023SGordon.Ross@Sun.COM 3810023SGordon.Ross@Sun.COM /* 3910023SGordon.Ross@Sun.COM * Kerberos V Security Support Provider 4010023SGordon.Ross@Sun.COM * 4110023SGordon.Ross@Sun.COM * Based on code previously in ctx.c (from Boris Popov?) 4210023SGordon.Ross@Sun.COM * but then mostly rewritten at Sun. 4310023SGordon.Ross@Sun.COM */ 4410023SGordon.Ross@Sun.COM 4510023SGordon.Ross@Sun.COM #include <errno.h> 4610023SGordon.Ross@Sun.COM #include <stdio.h> 4710023SGordon.Ross@Sun.COM #include <stddef.h> 4810023SGordon.Ross@Sun.COM #include <stdlib.h> 4910023SGordon.Ross@Sun.COM #include <unistd.h> 5010023SGordon.Ross@Sun.COM #include <strings.h> 5110023SGordon.Ross@Sun.COM #include <netdb.h> 5210023SGordon.Ross@Sun.COM #include <libintl.h> 5310023SGordon.Ross@Sun.COM #include <xti.h> 5410023SGordon.Ross@Sun.COM #include <assert.h> 5510023SGordon.Ross@Sun.COM 5610023SGordon.Ross@Sun.COM #include <sys/types.h> 5710023SGordon.Ross@Sun.COM #include <sys/time.h> 5810023SGordon.Ross@Sun.COM #include <sys/byteorder.h> 5910023SGordon.Ross@Sun.COM #include <sys/socket.h> 6010023SGordon.Ross@Sun.COM #include <sys/fcntl.h> 6110023SGordon.Ross@Sun.COM 6210023SGordon.Ross@Sun.COM #include <netinet/in.h> 6310023SGordon.Ross@Sun.COM #include <netinet/tcp.h> 6410023SGordon.Ross@Sun.COM #include <arpa/inet.h> 6510023SGordon.Ross@Sun.COM 6610023SGordon.Ross@Sun.COM #include <netsmb/smb.h> 6710023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h> 6810023SGordon.Ross@Sun.COM #include <netsmb/mchain.h> 6910023SGordon.Ross@Sun.COM 7010023SGordon.Ross@Sun.COM #include "private.h" 7110023SGordon.Ross@Sun.COM #include "charsets.h" 7210023SGordon.Ross@Sun.COM #include "spnego.h" 7310023SGordon.Ross@Sun.COM #include "derparse.h" 7410023SGordon.Ross@Sun.COM #include "ssp.h" 7510023SGordon.Ross@Sun.COM 7610023SGordon.Ross@Sun.COM #include <kerberosv5/krb5.h> 7710023SGordon.Ross@Sun.COM #include <kerberosv5/com_err.h> 7810023SGordon.Ross@Sun.COM 7910023SGordon.Ross@Sun.COM /* RFC 1964 token ID codes */ 8010023SGordon.Ross@Sun.COM #define KRB_AP_REQ 1 8110023SGordon.Ross@Sun.COM #define KRB_AP_REP 2 8210023SGordon.Ross@Sun.COM #define KRB_ERROR 3 8310023SGordon.Ross@Sun.COM 8410023SGordon.Ross@Sun.COM extern MECH_OID g_stcMechOIDList []; 8510023SGordon.Ross@Sun.COM 8610023SGordon.Ross@Sun.COM typedef struct krb5ssp_state { 8710023SGordon.Ross@Sun.COM /* Filled in by krb5ssp_init_client */ 8810023SGordon.Ross@Sun.COM krb5_context ss_krb5ctx; /* krb5 context (ptr) */ 8910023SGordon.Ross@Sun.COM krb5_ccache ss_krb5cc; /* credentials cache (ptr) */ 9010023SGordon.Ross@Sun.COM krb5_principal ss_krb5clp; /* client principal (ptr) */ 9110023SGordon.Ross@Sun.COM /* Filled in by krb5ssp_get_tkt */ 9210023SGordon.Ross@Sun.COM krb5_auth_context ss_auth; /* auth ctx. w/ server (ptr) */ 9310023SGordon.Ross@Sun.COM } krb5ssp_state_t; 9410023SGordon.Ross@Sun.COM 9510023SGordon.Ross@Sun.COM 9610023SGordon.Ross@Sun.COM /* 9710023SGordon.Ross@Sun.COM * adds a GSSAPI wrapper 9810023SGordon.Ross@Sun.COM */ 9910023SGordon.Ross@Sun.COM int 10010023SGordon.Ross@Sun.COM krb5ssp_tkt2gtok(uchar_t *tkt, ulong_t tktlen, 10110023SGordon.Ross@Sun.COM uchar_t **gtokp, ulong_t *gtoklenp) 10210023SGordon.Ross@Sun.COM { 10310023SGordon.Ross@Sun.COM ulong_t len; 10410023SGordon.Ross@Sun.COM ulong_t bloblen = tktlen; 10510023SGordon.Ross@Sun.COM uchar_t krbapreq[2] = { KRB_AP_REQ, 0 }; 10610023SGordon.Ross@Sun.COM uchar_t *blob = NULL; /* result */ 10710023SGordon.Ross@Sun.COM uchar_t *b; 10810023SGordon.Ross@Sun.COM 10910023SGordon.Ross@Sun.COM bloblen += sizeof (krbapreq); 11010023SGordon.Ross@Sun.COM bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen; 11110023SGordon.Ross@Sun.COM len = bloblen; 11210023SGordon.Ross@Sun.COM bloblen = ASNDerCalcTokenLength(bloblen, bloblen); 11310023SGordon.Ross@Sun.COM if ((blob = malloc(bloblen)) == NULL) { 11410023SGordon.Ross@Sun.COM DPRINT("malloc"); 11510023SGordon.Ross@Sun.COM return (ENOMEM); 11610023SGordon.Ross@Sun.COM } 11710023SGordon.Ross@Sun.COM 11810023SGordon.Ross@Sun.COM b = blob; 11910023SGordon.Ross@Sun.COM b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len); 12010023SGordon.Ross@Sun.COM b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5); 12110023SGordon.Ross@Sun.COM memcpy(b, krbapreq, sizeof (krbapreq)); 12210023SGordon.Ross@Sun.COM b += sizeof (krbapreq); 12310023SGordon.Ross@Sun.COM 12410023SGordon.Ross@Sun.COM assert(b + tktlen == blob + bloblen); 12510023SGordon.Ross@Sun.COM memcpy(b, tkt, tktlen); 12610023SGordon.Ross@Sun.COM *gtoklenp = bloblen; 12710023SGordon.Ross@Sun.COM *gtokp = blob; 12810023SGordon.Ross@Sun.COM return (0); 12910023SGordon.Ross@Sun.COM } 13010023SGordon.Ross@Sun.COM 13110023SGordon.Ross@Sun.COM /* 13210023SGordon.Ross@Sun.COM * See "Windows 2000 Kerberos Interoperability" paper by 13310023SGordon.Ross@Sun.COM * Christopher Nebergall. RC4 HMAC is the W2K default but 13410023SGordon.Ross@Sun.COM * Samba support lagged (not due to Samba itself, but due to OS' 13510023SGordon.Ross@Sun.COM * Kerberos implementations.) 13610023SGordon.Ross@Sun.COM * 13710023SGordon.Ross@Sun.COM * Only session enc type should matter, not ticket enc type, 13810023SGordon.Ross@Sun.COM * per Sam Hartman on krbdev. 13910023SGordon.Ross@Sun.COM * 14010023SGordon.Ross@Sun.COM * Preauthentication failure topics in krb-protocol may help here... 14110023SGordon.Ross@Sun.COM * try "John Brezak" and/or "Clifford Neuman" too. 14210023SGordon.Ross@Sun.COM */ 14310023SGordon.Ross@Sun.COM static krb5_enctype kenctypes[] = { 14410023SGordon.Ross@Sun.COM ENCTYPE_ARCFOUR_HMAC, /* defined in krb5.h */ 14510023SGordon.Ross@Sun.COM ENCTYPE_DES_CBC_MD5, 14610023SGordon.Ross@Sun.COM ENCTYPE_DES_CBC_CRC, 14710023SGordon.Ross@Sun.COM ENCTYPE_NULL 14810023SGordon.Ross@Sun.COM }; 14910023SGordon.Ross@Sun.COM 15010023SGordon.Ross@Sun.COM static const int rq_opts = 15110023SGordon.Ross@Sun.COM AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED; 15210023SGordon.Ross@Sun.COM 15310023SGordon.Ross@Sun.COM /* 15410023SGordon.Ross@Sun.COM * Obtain a kerberos ticket for the host we're connecting to. 15510023SGordon.Ross@Sun.COM * (This does the KRB_TGS exchange.) 15610023SGordon.Ross@Sun.COM */ 15710023SGordon.Ross@Sun.COM static int 15810023SGordon.Ross@Sun.COM krb5ssp_get_tkt(krb5ssp_state_t *ss, char *server, 15910023SGordon.Ross@Sun.COM uchar_t **tktp, ulong_t *tktlenp) 16010023SGordon.Ross@Sun.COM { 16110023SGordon.Ross@Sun.COM krb5_context kctx = ss->ss_krb5ctx; 16210023SGordon.Ross@Sun.COM krb5_ccache kcc = ss->ss_krb5cc; 16310023SGordon.Ross@Sun.COM krb5_data indata = {0}; 16410023SGordon.Ross@Sun.COM krb5_data outdata = {0}; 16510023SGordon.Ross@Sun.COM krb5_error_code kerr = 0; 16610023SGordon.Ross@Sun.COM const char *fn = NULL; 16710023SGordon.Ross@Sun.COM uchar_t *tkt; 16810023SGordon.Ross@Sun.COM 16910023SGordon.Ross@Sun.COM /* Should have these from krb5ssp_init_client. */ 17010023SGordon.Ross@Sun.COM if (kctx == NULL || kcc == NULL) { 17110023SGordon.Ross@Sun.COM fn = "null kctx or kcc"; 17210023SGordon.Ross@Sun.COM kerr = EINVAL; 17310023SGordon.Ross@Sun.COM goto out; 17410023SGordon.Ross@Sun.COM } 17510023SGordon.Ross@Sun.COM 17610023SGordon.Ross@Sun.COM kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes); 17710023SGordon.Ross@Sun.COM if (kerr != 0) { 17810023SGordon.Ross@Sun.COM fn = "krb5_set_default_tgs_enctypes"; 17910023SGordon.Ross@Sun.COM goto out; 18010023SGordon.Ross@Sun.COM } 18110023SGordon.Ross@Sun.COM 18210023SGordon.Ross@Sun.COM /* Override the krb5 library default. */ 18310023SGordon.Ross@Sun.COM indata.data = ""; 18410023SGordon.Ross@Sun.COM 18510023SGordon.Ross@Sun.COM kerr = krb5_mk_req(kctx, &ss->ss_auth, rq_opts, "cifs", server, 18610023SGordon.Ross@Sun.COM &indata, kcc, &outdata); 18710023SGordon.Ross@Sun.COM if (kerr != 0) { 18810023SGordon.Ross@Sun.COM fn = "krb5_mk_req"; 18910023SGordon.Ross@Sun.COM goto out; 19010023SGordon.Ross@Sun.COM } 19110023SGordon.Ross@Sun.COM if ((tkt = malloc(outdata.length)) == NULL) { 19210023SGordon.Ross@Sun.COM kerr = ENOMEM; 19310023SGordon.Ross@Sun.COM fn = "malloc signing key"; 19410023SGordon.Ross@Sun.COM goto out; 19510023SGordon.Ross@Sun.COM } 19610023SGordon.Ross@Sun.COM memcpy(tkt, outdata.data, outdata.length); 19710023SGordon.Ross@Sun.COM *tktp = tkt; 19810023SGordon.Ross@Sun.COM *tktlenp = outdata.length; 19910023SGordon.Ross@Sun.COM kerr = 0; 20010023SGordon.Ross@Sun.COM 20110023SGordon.Ross@Sun.COM out: 20210023SGordon.Ross@Sun.COM if (kerr) { 20310023SGordon.Ross@Sun.COM if (fn == NULL) 20410023SGordon.Ross@Sun.COM fn = "?"; 20510023SGordon.Ross@Sun.COM DPRINT("%s err 0x%x: %s", fn, kerr, error_message(kerr)); 20610023SGordon.Ross@Sun.COM if (kerr <= 0 || kerr > ESTALE) 20710023SGordon.Ross@Sun.COM kerr = EAUTH; 20810023SGordon.Ross@Sun.COM } 20910023SGordon.Ross@Sun.COM 21010023SGordon.Ross@Sun.COM if (outdata.data) 21110023SGordon.Ross@Sun.COM krb5_free_data_contents(kctx, &outdata); 21210023SGordon.Ross@Sun.COM 21310023SGordon.Ross@Sun.COM /* Free kctx in krb5ssp_destroy */ 21410023SGordon.Ross@Sun.COM return (kerr); 21510023SGordon.Ross@Sun.COM } 21610023SGordon.Ross@Sun.COM 21710023SGordon.Ross@Sun.COM 21810023SGordon.Ross@Sun.COM /* 21910023SGordon.Ross@Sun.COM * Build an RFC 1964 KRB_AP_REQ message 22010023SGordon.Ross@Sun.COM * The caller puts on the SPNEGO wrapper. 22110023SGordon.Ross@Sun.COM */ 22210023SGordon.Ross@Sun.COM int 22310023SGordon.Ross@Sun.COM krb5ssp_put_request(struct ssp_ctx *sp, struct mbdata *out_mb) 22410023SGordon.Ross@Sun.COM { 22510023SGordon.Ross@Sun.COM int err; 22610023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 22710023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private; 22810023SGordon.Ross@Sun.COM uchar_t *tkt = NULL; 22910023SGordon.Ross@Sun.COM ulong_t tktlen; 23010023SGordon.Ross@Sun.COM uchar_t *gtok = NULL; /* gssapi token */ 23110023SGordon.Ross@Sun.COM ulong_t gtoklen; /* gssapi token length */ 23210023SGordon.Ross@Sun.COM char *prin = ctx->ct_srvname; 23310023SGordon.Ross@Sun.COM 23410023SGordon.Ross@Sun.COM if ((err = krb5ssp_get_tkt(ss, prin, &tkt, &tktlen)) != 0) 23510023SGordon.Ross@Sun.COM goto out; 23610023SGordon.Ross@Sun.COM if ((err = krb5ssp_tkt2gtok(tkt, tktlen, >ok, >oklen)) != 0) 23710023SGordon.Ross@Sun.COM goto out; 23810023SGordon.Ross@Sun.COM 239*11332SGordon.Ross@Sun.COM if ((err = mb_init_sz(out_mb, gtoklen)) != 0) 24010023SGordon.Ross@Sun.COM goto out; 241*11332SGordon.Ross@Sun.COM if ((err = mb_put_mem(out_mb, gtok, gtoklen, MB_MSYSTEM)) != 0) 24210023SGordon.Ross@Sun.COM goto out; 24310023SGordon.Ross@Sun.COM 24410023SGordon.Ross@Sun.COM if (ctx->ct_vcflags & SMBV_WILL_SIGN) 24510023SGordon.Ross@Sun.COM ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; 24610023SGordon.Ross@Sun.COM 24710023SGordon.Ross@Sun.COM out: 24810023SGordon.Ross@Sun.COM if (gtok) 24910023SGordon.Ross@Sun.COM free(gtok); 25010023SGordon.Ross@Sun.COM if (tkt) 25110023SGordon.Ross@Sun.COM free(tkt); 25210023SGordon.Ross@Sun.COM 25310023SGordon.Ross@Sun.COM return (err); 25410023SGordon.Ross@Sun.COM } 25510023SGordon.Ross@Sun.COM 25610023SGordon.Ross@Sun.COM /* 25710023SGordon.Ross@Sun.COM * Unwrap a GSS-API encapsulated RFC 1964 reply message, 25810023SGordon.Ross@Sun.COM * i.e. type KRB_AP_REP or KRB_ERROR. 25910023SGordon.Ross@Sun.COM */ 26010023SGordon.Ross@Sun.COM int 26110023SGordon.Ross@Sun.COM krb5ssp_get_reply(struct ssp_ctx *sp, struct mbdata *in_mb) 26210023SGordon.Ross@Sun.COM { 26310023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private; 26410023SGordon.Ross@Sun.COM mbuf_t *m = in_mb->mb_top; 26510023SGordon.Ross@Sun.COM int err = EBADRPC; 26610023SGordon.Ross@Sun.COM int dlen, rc; 26710023SGordon.Ross@Sun.COM long actual_len, token_len; 26810023SGordon.Ross@Sun.COM uchar_t *data; 26910023SGordon.Ross@Sun.COM krb5_data ap = {0}; 27010023SGordon.Ross@Sun.COM krb5_ap_rep_enc_part *reply = NULL; 27110023SGordon.Ross@Sun.COM 27210023SGordon.Ross@Sun.COM /* cheating: this mbuf is contiguous */ 27310023SGordon.Ross@Sun.COM assert(m->m_data == in_mb->mb_pos); 27410023SGordon.Ross@Sun.COM data = (uchar_t *)m->m_data; 27510023SGordon.Ross@Sun.COM dlen = m->m_len; 27610023SGordon.Ross@Sun.COM 27710023SGordon.Ross@Sun.COM /* 27810023SGordon.Ross@Sun.COM * Peel off the GSS-API wrapper. Looks like: 27910023SGordon.Ross@Sun.COM * AppToken: 60 81 83 28010023SGordon.Ross@Sun.COM * OID(KRB5): 06 09 2a 86 48 86 f7 12 01 02 02 28110023SGordon.Ross@Sun.COM * KRB_AP_REP: 02 00 28210023SGordon.Ross@Sun.COM */ 28310023SGordon.Ross@Sun.COM rc = ASNDerCheckToken(data, SPNEGO_NEGINIT_APP_CONSTRUCT, 28410023SGordon.Ross@Sun.COM 0, dlen, &token_len, &actual_len); 28510023SGordon.Ross@Sun.COM if (rc != SPNEGO_E_SUCCESS) { 28610023SGordon.Ross@Sun.COM DPRINT("no AppToken? rc=0x%x", rc); 28710023SGordon.Ross@Sun.COM goto out; 28810023SGordon.Ross@Sun.COM } 28910023SGordon.Ross@Sun.COM if (dlen < actual_len) 29010023SGordon.Ross@Sun.COM goto out; 29110023SGordon.Ross@Sun.COM data += actual_len; 29210023SGordon.Ross@Sun.COM dlen -= actual_len; 29310023SGordon.Ross@Sun.COM 29410023SGordon.Ross@Sun.COM /* OID (KRB5) */ 29510023SGordon.Ross@Sun.COM rc = ASNDerCheckOID(data, spnego_mech_oid_Kerberos_V5, 29610023SGordon.Ross@Sun.COM dlen, &actual_len); 29710023SGordon.Ross@Sun.COM if (rc != SPNEGO_E_SUCCESS) { 29810023SGordon.Ross@Sun.COM DPRINT("no OID? rc=0x%x", rc); 29910023SGordon.Ross@Sun.COM goto out; 30010023SGordon.Ross@Sun.COM } 30110023SGordon.Ross@Sun.COM if (dlen < actual_len) 30210023SGordon.Ross@Sun.COM goto out; 30310023SGordon.Ross@Sun.COM data += actual_len; 30410023SGordon.Ross@Sun.COM dlen -= actual_len; 30510023SGordon.Ross@Sun.COM 30610023SGordon.Ross@Sun.COM /* KRB_AP_REP or KRB_ERROR */ 30710023SGordon.Ross@Sun.COM if (data[0] != KRB_AP_REP) { 30810023SGordon.Ross@Sun.COM DPRINT("KRB5 type: %d", data[1]); 30910023SGordon.Ross@Sun.COM goto out; 31010023SGordon.Ross@Sun.COM } 31110023SGordon.Ross@Sun.COM if (dlen < 2) 31210023SGordon.Ross@Sun.COM goto out; 31310023SGordon.Ross@Sun.COM data += 2; 31410023SGordon.Ross@Sun.COM dlen -= 2; 31510023SGordon.Ross@Sun.COM 31610023SGordon.Ross@Sun.COM /* 31710023SGordon.Ross@Sun.COM * Now what's left should be a krb5 reply 31810023SGordon.Ross@Sun.COM * NB: ap is NOT allocated, so don't free it. 31910023SGordon.Ross@Sun.COM */ 32010023SGordon.Ross@Sun.COM ap.length = dlen; 32110023SGordon.Ross@Sun.COM ap.data = (char *)data; 32210023SGordon.Ross@Sun.COM rc = krb5_rd_rep(ss->ss_krb5ctx, ss->ss_auth, &ap, &reply); 32310023SGordon.Ross@Sun.COM if (rc != 0) { 32410023SGordon.Ross@Sun.COM DPRINT("krb5_rd_rep: err 0x%x (%s)", 32510023SGordon.Ross@Sun.COM rc, error_message(rc)); 32610023SGordon.Ross@Sun.COM err = EAUTH; 32710023SGordon.Ross@Sun.COM goto out; 32810023SGordon.Ross@Sun.COM } 32910023SGordon.Ross@Sun.COM 33010023SGordon.Ross@Sun.COM /* 33110023SGordon.Ross@Sun.COM * Have the decoded reply. Save anything? 33210023SGordon.Ross@Sun.COM * 33310023SGordon.Ross@Sun.COM * NB: If this returns an error, we will get 33410023SGordon.Ross@Sun.COM * no more calls into this back-end module. 33510023SGordon.Ross@Sun.COM */ 33610023SGordon.Ross@Sun.COM err = 0; 33710023SGordon.Ross@Sun.COM 33810023SGordon.Ross@Sun.COM out: 33910023SGordon.Ross@Sun.COM if (reply != NULL) 34010023SGordon.Ross@Sun.COM krb5_free_ap_rep_enc_part(ss->ss_krb5ctx, reply); 34110023SGordon.Ross@Sun.COM if (err) 34210023SGordon.Ross@Sun.COM DPRINT("ret %d", err); 34310023SGordon.Ross@Sun.COM 34410023SGordon.Ross@Sun.COM return (err); 34510023SGordon.Ross@Sun.COM } 34610023SGordon.Ross@Sun.COM 34710023SGordon.Ross@Sun.COM /* 34810023SGordon.Ross@Sun.COM * krb5ssp_final 34910023SGordon.Ross@Sun.COM * 35010023SGordon.Ross@Sun.COM * Called after successful authentication. 35110023SGordon.Ross@Sun.COM * Setup the MAC key for signing. 35210023SGordon.Ross@Sun.COM */ 35310023SGordon.Ross@Sun.COM int 35410023SGordon.Ross@Sun.COM krb5ssp_final(struct ssp_ctx *sp) 35510023SGordon.Ross@Sun.COM { 35610023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 35710023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private; 35810023SGordon.Ross@Sun.COM krb5_keyblock *ssn_key = NULL; 35910023SGordon.Ross@Sun.COM int err, len; 36010023SGordon.Ross@Sun.COM 36110023SGordon.Ross@Sun.COM /* 36210023SGordon.Ross@Sun.COM * Save the session key, used for SMB signing 36310023SGordon.Ross@Sun.COM * and possibly other consumers (RPC). 36410023SGordon.Ross@Sun.COM */ 36510023SGordon.Ross@Sun.COM err = krb5_auth_con_getlocalsubkey( 36610023SGordon.Ross@Sun.COM ss->ss_krb5ctx, ss->ss_auth, &ssn_key); 36710023SGordon.Ross@Sun.COM if (err != 0) { 36810023SGordon.Ross@Sun.COM DPRINT("_getlocalsubkey, err=0x%x (%s)", 36910023SGordon.Ross@Sun.COM err, error_message(err)); 37010023SGordon.Ross@Sun.COM if (err <= 0 || err > ESTALE) 37110023SGordon.Ross@Sun.COM err = EAUTH; 37210023SGordon.Ross@Sun.COM goto out; 37310023SGordon.Ross@Sun.COM } 37410023SGordon.Ross@Sun.COM memset(ctx->ct_ssn_key, 0, SMBIOC_HASH_SZ); 37510023SGordon.Ross@Sun.COM if ((len = ssn_key->length) > SMBIOC_HASH_SZ) 37610023SGordon.Ross@Sun.COM len = SMBIOC_HASH_SZ; 37710023SGordon.Ross@Sun.COM memcpy(ctx->ct_ssn_key, ssn_key->contents, len); 37810023SGordon.Ross@Sun.COM 37910023SGordon.Ross@Sun.COM /* 38010023SGordon.Ross@Sun.COM * Set the MAC key on the first successful auth. 38110023SGordon.Ross@Sun.COM */ 38210023SGordon.Ross@Sun.COM if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) && 38310023SGordon.Ross@Sun.COM (ctx->ct_mackey == NULL)) { 38410023SGordon.Ross@Sun.COM ctx->ct_mackeylen = ssn_key->length; 38510023SGordon.Ross@Sun.COM ctx->ct_mackey = malloc(ctx->ct_mackeylen); 38610023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) { 38710023SGordon.Ross@Sun.COM ctx->ct_mackeylen = 0; 38810023SGordon.Ross@Sun.COM err = ENOMEM; 38910023SGordon.Ross@Sun.COM goto out; 39010023SGordon.Ross@Sun.COM } 39110023SGordon.Ross@Sun.COM memcpy(ctx->ct_mackey, ssn_key->contents, 39210023SGordon.Ross@Sun.COM ctx->ct_mackeylen); 39310023SGordon.Ross@Sun.COM /* 39410023SGordon.Ross@Sun.COM * Apparently, the server used seq. no. zero 39510023SGordon.Ross@Sun.COM * for our previous message, so next is two. 39610023SGordon.Ross@Sun.COM */ 39710023SGordon.Ross@Sun.COM ctx->ct_mac_seqno = 2; 39810023SGordon.Ross@Sun.COM } 39910023SGordon.Ross@Sun.COM err = 0; 40010023SGordon.Ross@Sun.COM 40110023SGordon.Ross@Sun.COM out: 40210023SGordon.Ross@Sun.COM if (ssn_key) 40310023SGordon.Ross@Sun.COM krb5_free_keyblock(ss->ss_krb5ctx, ssn_key); 40410023SGordon.Ross@Sun.COM 40510023SGordon.Ross@Sun.COM return (err); 40610023SGordon.Ross@Sun.COM } 40710023SGordon.Ross@Sun.COM 40810023SGordon.Ross@Sun.COM /* 40910023SGordon.Ross@Sun.COM * krb5ssp_next_token 41010023SGordon.Ross@Sun.COM * 41110023SGordon.Ross@Sun.COM * See ssp.c: ssp_ctx_next_token 41210023SGordon.Ross@Sun.COM */ 41310023SGordon.Ross@Sun.COM int 41410023SGordon.Ross@Sun.COM krb5ssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb, 41510023SGordon.Ross@Sun.COM struct mbdata *out_mb) 41610023SGordon.Ross@Sun.COM { 41710023SGordon.Ross@Sun.COM int err; 41810023SGordon.Ross@Sun.COM 41910023SGordon.Ross@Sun.COM /* 42010023SGordon.Ross@Sun.COM * Note: in_mb == NULL on the first call. 42110023SGordon.Ross@Sun.COM */ 42210023SGordon.Ross@Sun.COM if (in_mb) { 42310023SGordon.Ross@Sun.COM err = krb5ssp_get_reply(sp, in_mb); 42410023SGordon.Ross@Sun.COM if (err) 42510023SGordon.Ross@Sun.COM goto out; 42610023SGordon.Ross@Sun.COM } 42710023SGordon.Ross@Sun.COM 42810023SGordon.Ross@Sun.COM if (out_mb) { 42910023SGordon.Ross@Sun.COM err = krb5ssp_put_request(sp, out_mb); 43010023SGordon.Ross@Sun.COM } else 43110023SGordon.Ross@Sun.COM err = krb5ssp_final(sp); 43210023SGordon.Ross@Sun.COM 43310023SGordon.Ross@Sun.COM out: 43410023SGordon.Ross@Sun.COM if (err) 43510023SGordon.Ross@Sun.COM DPRINT("ret: %d", err); 43610023SGordon.Ross@Sun.COM return (err); 43710023SGordon.Ross@Sun.COM } 43810023SGordon.Ross@Sun.COM 43910023SGordon.Ross@Sun.COM /* 44010023SGordon.Ross@Sun.COM * krb5ssp_ctx_destroy 44110023SGordon.Ross@Sun.COM * 44210023SGordon.Ross@Sun.COM * Destroy mechanism-specific data. 44310023SGordon.Ross@Sun.COM */ 44410023SGordon.Ross@Sun.COM void 44510023SGordon.Ross@Sun.COM krb5ssp_destroy(struct ssp_ctx *sp) 44610023SGordon.Ross@Sun.COM { 44710023SGordon.Ross@Sun.COM krb5ssp_state_t *ss; 44810023SGordon.Ross@Sun.COM krb5_context kctx; 44910023SGordon.Ross@Sun.COM 45010023SGordon.Ross@Sun.COM ss = sp->sp_private; 45110023SGordon.Ross@Sun.COM if (ss == NULL) 45210023SGordon.Ross@Sun.COM return; 45310023SGordon.Ross@Sun.COM sp->sp_private = NULL; 45410023SGordon.Ross@Sun.COM 45510023SGordon.Ross@Sun.COM if ((kctx = ss->ss_krb5ctx) != NULL) { 45610023SGordon.Ross@Sun.COM /* from krb5ssp_get_tkt */ 45710023SGordon.Ross@Sun.COM if (ss->ss_auth) 45810023SGordon.Ross@Sun.COM krb5_auth_con_free(kctx, ss->ss_auth); 45910023SGordon.Ross@Sun.COM /* from krb5ssp_init_client */ 46010023SGordon.Ross@Sun.COM if (ss->ss_krb5clp) 46110023SGordon.Ross@Sun.COM krb5_free_principal(kctx, ss->ss_krb5clp); 46210023SGordon.Ross@Sun.COM if (ss->ss_krb5cc) 46310023SGordon.Ross@Sun.COM krb5_cc_close(kctx, ss->ss_krb5cc); 46410023SGordon.Ross@Sun.COM krb5_free_context(kctx); 46510023SGordon.Ross@Sun.COM } 46610023SGordon.Ross@Sun.COM 46710023SGordon.Ross@Sun.COM free(ss); 46810023SGordon.Ross@Sun.COM } 46910023SGordon.Ross@Sun.COM 47010023SGordon.Ross@Sun.COM /* 47110023SGordon.Ross@Sun.COM * krb5ssp_init_clnt 47210023SGordon.Ross@Sun.COM * 47310023SGordon.Ross@Sun.COM * Initialize a new Kerberos SSP client context. 47410023SGordon.Ross@Sun.COM * 47510023SGordon.Ross@Sun.COM * The user must already have a TGT in their credential cache, 47610023SGordon.Ross@Sun.COM * as shown by the "klist" command. 47710023SGordon.Ross@Sun.COM */ 47810023SGordon.Ross@Sun.COM int 47910023SGordon.Ross@Sun.COM krb5ssp_init_client(struct ssp_ctx *sp) 48010023SGordon.Ross@Sun.COM { 48110023SGordon.Ross@Sun.COM krb5ssp_state_t *ss; 48210023SGordon.Ross@Sun.COM krb5_error_code kerr; 48310023SGordon.Ross@Sun.COM krb5_context kctx = NULL; 48410023SGordon.Ross@Sun.COM krb5_ccache kcc = NULL; 48510023SGordon.Ross@Sun.COM krb5_principal kprin = NULL; 48610023SGordon.Ross@Sun.COM 48710023SGordon.Ross@Sun.COM if ((sp->smb_ctx->ct_authflags & SMB_AT_KRB5) == 0) { 48810023SGordon.Ross@Sun.COM DPRINT("KRB5 not in authflags"); 48910023SGordon.Ross@Sun.COM return (ENOTSUP); 49010023SGordon.Ross@Sun.COM } 49110023SGordon.Ross@Sun.COM 49210023SGordon.Ross@Sun.COM ss = calloc(1, sizeof (*ss)); 49310023SGordon.Ross@Sun.COM if (ss == NULL) 49410023SGordon.Ross@Sun.COM return (ENOMEM); 49510023SGordon.Ross@Sun.COM 49610023SGordon.Ross@Sun.COM sp->sp_nexttok = krb5ssp_next_token; 49710023SGordon.Ross@Sun.COM sp->sp_destroy = krb5ssp_destroy; 49810023SGordon.Ross@Sun.COM sp->sp_private = ss; 49910023SGordon.Ross@Sun.COM 50010023SGordon.Ross@Sun.COM kerr = krb5_init_context(&kctx); 50110023SGordon.Ross@Sun.COM if (kerr) { 50210023SGordon.Ross@Sun.COM DPRINT("krb5_init_context, kerr 0x%x", kerr); 50310023SGordon.Ross@Sun.COM goto errout; 50410023SGordon.Ross@Sun.COM } 50510023SGordon.Ross@Sun.COM ss->ss_krb5ctx = kctx; 50610023SGordon.Ross@Sun.COM 50710023SGordon.Ross@Sun.COM /* non-default would instead use krb5_cc_resolve */ 50810023SGordon.Ross@Sun.COM kerr = krb5_cc_default(kctx, &kcc); 50910023SGordon.Ross@Sun.COM if (kerr) { 51010023SGordon.Ross@Sun.COM DPRINT("krb5_cc_default, kerr 0x%x", kerr); 51110023SGordon.Ross@Sun.COM goto errout; 51210023SGordon.Ross@Sun.COM } 51310023SGordon.Ross@Sun.COM ss->ss_krb5cc = kcc; 51410023SGordon.Ross@Sun.COM 51510023SGordon.Ross@Sun.COM /* 51610023SGordon.Ross@Sun.COM * Get the client principal (ticket), 51710023SGordon.Ross@Sun.COM * or discover that we don't have one. 51810023SGordon.Ross@Sun.COM */ 51910023SGordon.Ross@Sun.COM kerr = krb5_cc_get_principal(kctx, kcc, &kprin); 52010023SGordon.Ross@Sun.COM if (kerr) { 52110023SGordon.Ross@Sun.COM DPRINT("krb5_cc_get_principal, kerr 0x%x", kerr); 52210023SGordon.Ross@Sun.COM goto errout; 52310023SGordon.Ross@Sun.COM } 52410023SGordon.Ross@Sun.COM ss->ss_krb5clp = kprin; 52510023SGordon.Ross@Sun.COM 52610023SGordon.Ross@Sun.COM /* Success! */ 52710023SGordon.Ross@Sun.COM DPRINT("Ticket cache: %s:%s", 52810023SGordon.Ross@Sun.COM krb5_cc_get_type(kctx, kcc), 52910023SGordon.Ross@Sun.COM krb5_cc_get_name(kctx, kcc)); 53010023SGordon.Ross@Sun.COM return (0); 53110023SGordon.Ross@Sun.COM 53210023SGordon.Ross@Sun.COM errout: 53310023SGordon.Ross@Sun.COM krb5ssp_destroy(sp); 53410023SGordon.Ross@Sun.COM return (ENOTSUP); 53510023SGordon.Ross@Sun.COM } 536