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