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
krb5ssp_tkt2gtok(uchar_t * tkt,ulong_t tktlen,uchar_t ** gtokp,ulong_t * gtoklenp)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
krb5ssp_get_tkt(krb5ssp_state_t * ss,char * server,uchar_t ** tktp,ulong_t * tktlenp)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
krb5ssp_put_request(struct ssp_ctx * sp,struct mbdata * out_mb)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
krb5ssp_get_reply(struct ssp_ctx * sp,struct mbdata * in_mb)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
krb5ssp_final(struct ssp_ctx * sp)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
krb5ssp_next_token(struct ssp_ctx * sp,struct mbdata * in_mb,struct mbdata * out_mb)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
krb5ssp_destroy(struct ssp_ctx * sp)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
krb5ssp_init_client(struct ssp_ctx * sp)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