xref: /onnv-gate/usr/src/lib/libsmbfs/smb/krb5ssp.c (revision 11332:ed3411181494)
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, &gtok, &gtoklen)) != 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