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 /*
34*12140SGordon.Ross@Sun.COM * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3510023SGordon.Ross@Sun.COM */
3610023SGordon.Ross@Sun.COM
3710023SGordon.Ross@Sun.COM /*
3810023SGordon.Ross@Sun.COM * Kerberos V Security Support Provider
3910023SGordon.Ross@Sun.COM *
4010023SGordon.Ross@Sun.COM * Based on code previously in ctx.c (from Boris Popov?)
4110023SGordon.Ross@Sun.COM * but then mostly rewritten at Sun.
4210023SGordon.Ross@Sun.COM */
4310023SGordon.Ross@Sun.COM
4410023SGordon.Ross@Sun.COM #include <errno.h>
4510023SGordon.Ross@Sun.COM #include <stdio.h>
4610023SGordon.Ross@Sun.COM #include <stddef.h>
4710023SGordon.Ross@Sun.COM #include <stdlib.h>
4810023SGordon.Ross@Sun.COM #include <unistd.h>
4910023SGordon.Ross@Sun.COM #include <strings.h>
5010023SGordon.Ross@Sun.COM #include <netdb.h>
5110023SGordon.Ross@Sun.COM #include <libintl.h>
5210023SGordon.Ross@Sun.COM #include <xti.h>
5310023SGordon.Ross@Sun.COM #include <assert.h>
5410023SGordon.Ross@Sun.COM
5510023SGordon.Ross@Sun.COM #include <sys/types.h>
5610023SGordon.Ross@Sun.COM #include <sys/time.h>
5710023SGordon.Ross@Sun.COM #include <sys/byteorder.h>
5810023SGordon.Ross@Sun.COM #include <sys/socket.h>
5910023SGordon.Ross@Sun.COM #include <sys/fcntl.h>
6010023SGordon.Ross@Sun.COM
6110023SGordon.Ross@Sun.COM #include <netinet/in.h>
6210023SGordon.Ross@Sun.COM #include <netinet/tcp.h>
6310023SGordon.Ross@Sun.COM #include <arpa/inet.h>
6410023SGordon.Ross@Sun.COM
6510023SGordon.Ross@Sun.COM #include <netsmb/smb.h>
6610023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
6710023SGordon.Ross@Sun.COM #include <netsmb/mchain.h>
6810023SGordon.Ross@Sun.COM
6910023SGordon.Ross@Sun.COM #include "private.h"
7010023SGordon.Ross@Sun.COM #include "charsets.h"
7110023SGordon.Ross@Sun.COM #include "spnego.h"
7210023SGordon.Ross@Sun.COM #include "derparse.h"
7310023SGordon.Ross@Sun.COM #include "ssp.h"
7410023SGordon.Ross@Sun.COM
7510023SGordon.Ross@Sun.COM #include <kerberosv5/krb5.h>
7610023SGordon.Ross@Sun.COM #include <kerberosv5/com_err.h>
77*12140SGordon.Ross@Sun.COM #include <gssapi/gssapi.h>
78*12140SGordon.Ross@Sun.COM #include <gssapi/mechs/krb5/include/auth_con.h>
79*12140SGordon.Ross@Sun.COM
80*12140SGordon.Ross@Sun.COM /* RFC 4121 checksum type ID. */
81*12140SGordon.Ross@Sun.COM #define CKSUM_TYPE_RFC4121 0x8003
8210023SGordon.Ross@Sun.COM
8310023SGordon.Ross@Sun.COM /* RFC 1964 token ID codes */
8410023SGordon.Ross@Sun.COM #define KRB_AP_REQ 1
8510023SGordon.Ross@Sun.COM #define KRB_AP_REP 2
8610023SGordon.Ross@Sun.COM #define KRB_ERROR 3
8710023SGordon.Ross@Sun.COM
8810023SGordon.Ross@Sun.COM extern MECH_OID g_stcMechOIDList [];
8910023SGordon.Ross@Sun.COM
9010023SGordon.Ross@Sun.COM typedef struct krb5ssp_state {
9110023SGordon.Ross@Sun.COM /* Filled in by krb5ssp_init_client */
9210023SGordon.Ross@Sun.COM krb5_context ss_krb5ctx; /* krb5 context (ptr) */
9310023SGordon.Ross@Sun.COM krb5_ccache ss_krb5cc; /* credentials cache (ptr) */
9410023SGordon.Ross@Sun.COM krb5_principal ss_krb5clp; /* client principal (ptr) */
9510023SGordon.Ross@Sun.COM /* Filled in by krb5ssp_get_tkt */
9610023SGordon.Ross@Sun.COM krb5_auth_context ss_auth; /* auth ctx. w/ server (ptr) */
9710023SGordon.Ross@Sun.COM } krb5ssp_state_t;
9810023SGordon.Ross@Sun.COM
9910023SGordon.Ross@Sun.COM
10010023SGordon.Ross@Sun.COM /*
10110023SGordon.Ross@Sun.COM * adds a GSSAPI wrapper
10210023SGordon.Ross@Sun.COM */
10310023SGordon.Ross@Sun.COM int
krb5ssp_tkt2gtok(uchar_t * tkt,ulong_t tktlen,uchar_t ** gtokp,ulong_t * gtoklenp)10410023SGordon.Ross@Sun.COM krb5ssp_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
10510023SGordon.Ross@Sun.COM uchar_t **gtokp, ulong_t *gtoklenp)
10610023SGordon.Ross@Sun.COM {
10710023SGordon.Ross@Sun.COM ulong_t len;
10810023SGordon.Ross@Sun.COM ulong_t bloblen = tktlen;
10910023SGordon.Ross@Sun.COM uchar_t krbapreq[2] = { KRB_AP_REQ, 0 };
11010023SGordon.Ross@Sun.COM uchar_t *blob = NULL; /* result */
11110023SGordon.Ross@Sun.COM uchar_t *b;
11210023SGordon.Ross@Sun.COM
11310023SGordon.Ross@Sun.COM bloblen += sizeof (krbapreq);
11410023SGordon.Ross@Sun.COM bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
11510023SGordon.Ross@Sun.COM len = bloblen;
11610023SGordon.Ross@Sun.COM bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
11710023SGordon.Ross@Sun.COM if ((blob = malloc(bloblen)) == NULL) {
11810023SGordon.Ross@Sun.COM DPRINT("malloc");
11910023SGordon.Ross@Sun.COM return (ENOMEM);
12010023SGordon.Ross@Sun.COM }
12110023SGordon.Ross@Sun.COM
12210023SGordon.Ross@Sun.COM b = blob;
12310023SGordon.Ross@Sun.COM b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
12410023SGordon.Ross@Sun.COM b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
12510023SGordon.Ross@Sun.COM memcpy(b, krbapreq, sizeof (krbapreq));
12610023SGordon.Ross@Sun.COM b += sizeof (krbapreq);
12710023SGordon.Ross@Sun.COM
12810023SGordon.Ross@Sun.COM assert(b + tktlen == blob + bloblen);
12910023SGordon.Ross@Sun.COM memcpy(b, tkt, tktlen);
13010023SGordon.Ross@Sun.COM *gtoklenp = bloblen;
13110023SGordon.Ross@Sun.COM *gtokp = blob;
13210023SGordon.Ross@Sun.COM return (0);
13310023SGordon.Ross@Sun.COM }
13410023SGordon.Ross@Sun.COM
13510023SGordon.Ross@Sun.COM /*
13610023SGordon.Ross@Sun.COM * See "Windows 2000 Kerberos Interoperability" paper by
13710023SGordon.Ross@Sun.COM * Christopher Nebergall. RC4 HMAC is the W2K default but
13810023SGordon.Ross@Sun.COM * Samba support lagged (not due to Samba itself, but due to OS'
13910023SGordon.Ross@Sun.COM * Kerberos implementations.)
14010023SGordon.Ross@Sun.COM *
14110023SGordon.Ross@Sun.COM * Only session enc type should matter, not ticket enc type,
14210023SGordon.Ross@Sun.COM * per Sam Hartman on krbdev.
14310023SGordon.Ross@Sun.COM *
14410023SGordon.Ross@Sun.COM * Preauthentication failure topics in krb-protocol may help here...
14510023SGordon.Ross@Sun.COM * try "John Brezak" and/or "Clifford Neuman" too.
14610023SGordon.Ross@Sun.COM */
14710023SGordon.Ross@Sun.COM static krb5_enctype kenctypes[] = {
14810023SGordon.Ross@Sun.COM ENCTYPE_ARCFOUR_HMAC, /* defined in krb5.h */
14910023SGordon.Ross@Sun.COM ENCTYPE_DES_CBC_MD5,
15010023SGordon.Ross@Sun.COM ENCTYPE_DES_CBC_CRC,
15110023SGordon.Ross@Sun.COM ENCTYPE_NULL
15210023SGordon.Ross@Sun.COM };
15310023SGordon.Ross@Sun.COM
15410023SGordon.Ross@Sun.COM static const int rq_opts =
15510023SGordon.Ross@Sun.COM AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
15610023SGordon.Ross@Sun.COM
15710023SGordon.Ross@Sun.COM /*
15810023SGordon.Ross@Sun.COM * Obtain a kerberos ticket for the host we're connecting to.
15910023SGordon.Ross@Sun.COM * (This does the KRB_TGS exchange.)
16010023SGordon.Ross@Sun.COM */
16110023SGordon.Ross@Sun.COM static int
krb5ssp_get_tkt(krb5ssp_state_t * ss,char * server,uchar_t ** tktp,ulong_t * tktlenp)16210023SGordon.Ross@Sun.COM krb5ssp_get_tkt(krb5ssp_state_t *ss, char *server,
16310023SGordon.Ross@Sun.COM uchar_t **tktp, ulong_t *tktlenp)
16410023SGordon.Ross@Sun.COM {
16510023SGordon.Ross@Sun.COM krb5_context kctx = ss->ss_krb5ctx;
16610023SGordon.Ross@Sun.COM krb5_ccache kcc = ss->ss_krb5cc;
16710023SGordon.Ross@Sun.COM krb5_data indata = {0};
16810023SGordon.Ross@Sun.COM krb5_data outdata = {0};
16910023SGordon.Ross@Sun.COM krb5_error_code kerr = 0;
17010023SGordon.Ross@Sun.COM const char *fn = NULL;
17110023SGordon.Ross@Sun.COM uchar_t *tkt;
17210023SGordon.Ross@Sun.COM
17310023SGordon.Ross@Sun.COM /* Should have these from krb5ssp_init_client. */
17410023SGordon.Ross@Sun.COM if (kctx == NULL || kcc == NULL) {
17510023SGordon.Ross@Sun.COM fn = "null kctx or kcc";
17610023SGordon.Ross@Sun.COM kerr = EINVAL;
17710023SGordon.Ross@Sun.COM goto out;
17810023SGordon.Ross@Sun.COM }
17910023SGordon.Ross@Sun.COM
18010023SGordon.Ross@Sun.COM kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes);
18110023SGordon.Ross@Sun.COM if (kerr != 0) {
18210023SGordon.Ross@Sun.COM fn = "krb5_set_default_tgs_enctypes";
18310023SGordon.Ross@Sun.COM goto out;
18410023SGordon.Ross@Sun.COM }
18510023SGordon.Ross@Sun.COM
186*12140SGordon.Ross@Sun.COM /* Get ss_auth now so we can set req_chsumtype. */
187*12140SGordon.Ross@Sun.COM kerr = krb5_auth_con_init(kctx, &ss->ss_auth);
188*12140SGordon.Ross@Sun.COM if (kerr != 0) {
189*12140SGordon.Ross@Sun.COM fn = "krb5_auth_con_init";
190*12140SGordon.Ross@Sun.COM goto out;
191*12140SGordon.Ross@Sun.COM }
192*12140SGordon.Ross@Sun.COM /* Missing krb5_auth_con_set_req_cksumtype(), so inline. */
193*12140SGordon.Ross@Sun.COM ss->ss_auth->req_cksumtype = CKSUM_TYPE_RFC4121;
194*12140SGordon.Ross@Sun.COM
195*12140SGordon.Ross@Sun.COM /*
196*12140SGordon.Ross@Sun.COM * Build an RFC 4121 "checksum" with NULL channel bindings,
197*12140SGordon.Ross@Sun.COM * like make_gss_checksum(). Numbers here from the RFC.
198*12140SGordon.Ross@Sun.COM */
199*12140SGordon.Ross@Sun.COM indata.length = 24;
200*12140SGordon.Ross@Sun.COM if ((indata.data = calloc(1, indata.length)) == NULL) {
201*12140SGordon.Ross@Sun.COM kerr = ENOMEM;
202*12140SGordon.Ross@Sun.COM fn = "malloc checksum";
203*12140SGordon.Ross@Sun.COM goto out;
204*12140SGordon.Ross@Sun.COM }
205*12140SGordon.Ross@Sun.COM indata.data[0] = 16; /* length of "Bnd" field. */
206*12140SGordon.Ross@Sun.COM indata.data[20] = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
207*12140SGordon.Ross@Sun.COM /* Done building the "checksum". */
20810023SGordon.Ross@Sun.COM
20910023SGordon.Ross@Sun.COM kerr = krb5_mk_req(kctx, &ss->ss_auth, rq_opts, "cifs", server,
21010023SGordon.Ross@Sun.COM &indata, kcc, &outdata);
21110023SGordon.Ross@Sun.COM if (kerr != 0) {
21210023SGordon.Ross@Sun.COM fn = "krb5_mk_req";
21310023SGordon.Ross@Sun.COM goto out;
21410023SGordon.Ross@Sun.COM }
21510023SGordon.Ross@Sun.COM if ((tkt = malloc(outdata.length)) == NULL) {
21610023SGordon.Ross@Sun.COM kerr = ENOMEM;
21710023SGordon.Ross@Sun.COM fn = "malloc signing key";
21810023SGordon.Ross@Sun.COM goto out;
21910023SGordon.Ross@Sun.COM }
22010023SGordon.Ross@Sun.COM memcpy(tkt, outdata.data, outdata.length);
22110023SGordon.Ross@Sun.COM *tktp = tkt;
22210023SGordon.Ross@Sun.COM *tktlenp = outdata.length;
22310023SGordon.Ross@Sun.COM kerr = 0;
22410023SGordon.Ross@Sun.COM
22510023SGordon.Ross@Sun.COM out:
22610023SGordon.Ross@Sun.COM if (kerr) {
22710023SGordon.Ross@Sun.COM if (fn == NULL)
22810023SGordon.Ross@Sun.COM fn = "?";
22910023SGordon.Ross@Sun.COM DPRINT("%s err 0x%x: %s", fn, kerr, error_message(kerr));
23010023SGordon.Ross@Sun.COM if (kerr <= 0 || kerr > ESTALE)
23110023SGordon.Ross@Sun.COM kerr = EAUTH;
23210023SGordon.Ross@Sun.COM }
23310023SGordon.Ross@Sun.COM
23410023SGordon.Ross@Sun.COM if (outdata.data)
23510023SGordon.Ross@Sun.COM krb5_free_data_contents(kctx, &outdata);
23610023SGordon.Ross@Sun.COM
237*12140SGordon.Ross@Sun.COM if (indata.data)
238*12140SGordon.Ross@Sun.COM free(indata.data);
239*12140SGordon.Ross@Sun.COM
24010023SGordon.Ross@Sun.COM /* Free kctx in krb5ssp_destroy */
24110023SGordon.Ross@Sun.COM return (kerr);
24210023SGordon.Ross@Sun.COM }
24310023SGordon.Ross@Sun.COM
24410023SGordon.Ross@Sun.COM
24510023SGordon.Ross@Sun.COM /*
24610023SGordon.Ross@Sun.COM * Build an RFC 1964 KRB_AP_REQ message
24710023SGordon.Ross@Sun.COM * The caller puts on the SPNEGO wrapper.
24810023SGordon.Ross@Sun.COM */
24910023SGordon.Ross@Sun.COM int
krb5ssp_put_request(struct ssp_ctx * sp,struct mbdata * out_mb)25010023SGordon.Ross@Sun.COM krb5ssp_put_request(struct ssp_ctx *sp, struct mbdata *out_mb)
25110023SGordon.Ross@Sun.COM {
25210023SGordon.Ross@Sun.COM int err;
25310023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx;
25410023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private;
25510023SGordon.Ross@Sun.COM uchar_t *tkt = NULL;
25610023SGordon.Ross@Sun.COM ulong_t tktlen;
25710023SGordon.Ross@Sun.COM uchar_t *gtok = NULL; /* gssapi token */
25810023SGordon.Ross@Sun.COM ulong_t gtoklen; /* gssapi token length */
25910023SGordon.Ross@Sun.COM char *prin = ctx->ct_srvname;
26010023SGordon.Ross@Sun.COM
26110023SGordon.Ross@Sun.COM if ((err = krb5ssp_get_tkt(ss, prin, &tkt, &tktlen)) != 0)
26210023SGordon.Ross@Sun.COM goto out;
26310023SGordon.Ross@Sun.COM if ((err = krb5ssp_tkt2gtok(tkt, tktlen, >ok, >oklen)) != 0)
26410023SGordon.Ross@Sun.COM goto out;
26510023SGordon.Ross@Sun.COM
26611332SGordon.Ross@Sun.COM if ((err = mb_init_sz(out_mb, gtoklen)) != 0)
26710023SGordon.Ross@Sun.COM goto out;
26811332SGordon.Ross@Sun.COM if ((err = mb_put_mem(out_mb, gtok, gtoklen, MB_MSYSTEM)) != 0)
26910023SGordon.Ross@Sun.COM goto out;
27010023SGordon.Ross@Sun.COM
27110023SGordon.Ross@Sun.COM if (ctx->ct_vcflags & SMBV_WILL_SIGN)
27210023SGordon.Ross@Sun.COM ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
27310023SGordon.Ross@Sun.COM
27410023SGordon.Ross@Sun.COM out:
27510023SGordon.Ross@Sun.COM if (gtok)
27610023SGordon.Ross@Sun.COM free(gtok);
27710023SGordon.Ross@Sun.COM if (tkt)
27810023SGordon.Ross@Sun.COM free(tkt);
27910023SGordon.Ross@Sun.COM
28010023SGordon.Ross@Sun.COM return (err);
28110023SGordon.Ross@Sun.COM }
28210023SGordon.Ross@Sun.COM
28310023SGordon.Ross@Sun.COM /*
28410023SGordon.Ross@Sun.COM * Unwrap a GSS-API encapsulated RFC 1964 reply message,
28510023SGordon.Ross@Sun.COM * i.e. type KRB_AP_REP or KRB_ERROR.
28610023SGordon.Ross@Sun.COM */
28710023SGordon.Ross@Sun.COM int
krb5ssp_get_reply(struct ssp_ctx * sp,struct mbdata * in_mb)28810023SGordon.Ross@Sun.COM krb5ssp_get_reply(struct ssp_ctx *sp, struct mbdata *in_mb)
28910023SGordon.Ross@Sun.COM {
29010023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private;
29110023SGordon.Ross@Sun.COM mbuf_t *m = in_mb->mb_top;
29210023SGordon.Ross@Sun.COM int err = EBADRPC;
29310023SGordon.Ross@Sun.COM int dlen, rc;
29410023SGordon.Ross@Sun.COM long actual_len, token_len;
29510023SGordon.Ross@Sun.COM uchar_t *data;
29610023SGordon.Ross@Sun.COM krb5_data ap = {0};
29710023SGordon.Ross@Sun.COM krb5_ap_rep_enc_part *reply = NULL;
29810023SGordon.Ross@Sun.COM
29910023SGordon.Ross@Sun.COM /* cheating: this mbuf is contiguous */
30010023SGordon.Ross@Sun.COM assert(m->m_data == in_mb->mb_pos);
30110023SGordon.Ross@Sun.COM data = (uchar_t *)m->m_data;
30210023SGordon.Ross@Sun.COM dlen = m->m_len;
30310023SGordon.Ross@Sun.COM
30410023SGordon.Ross@Sun.COM /*
30510023SGordon.Ross@Sun.COM * Peel off the GSS-API wrapper. Looks like:
30610023SGordon.Ross@Sun.COM * AppToken: 60 81 83
30710023SGordon.Ross@Sun.COM * OID(KRB5): 06 09 2a 86 48 86 f7 12 01 02 02
30810023SGordon.Ross@Sun.COM * KRB_AP_REP: 02 00
30910023SGordon.Ross@Sun.COM */
31010023SGordon.Ross@Sun.COM rc = ASNDerCheckToken(data, SPNEGO_NEGINIT_APP_CONSTRUCT,
31110023SGordon.Ross@Sun.COM 0, dlen, &token_len, &actual_len);
31210023SGordon.Ross@Sun.COM if (rc != SPNEGO_E_SUCCESS) {
31310023SGordon.Ross@Sun.COM DPRINT("no AppToken? rc=0x%x", rc);
31410023SGordon.Ross@Sun.COM goto out;
31510023SGordon.Ross@Sun.COM }
31610023SGordon.Ross@Sun.COM if (dlen < actual_len)
31710023SGordon.Ross@Sun.COM goto out;
31810023SGordon.Ross@Sun.COM data += actual_len;
31910023SGordon.Ross@Sun.COM dlen -= actual_len;
32010023SGordon.Ross@Sun.COM
32110023SGordon.Ross@Sun.COM /* OID (KRB5) */
32210023SGordon.Ross@Sun.COM rc = ASNDerCheckOID(data, spnego_mech_oid_Kerberos_V5,
32310023SGordon.Ross@Sun.COM dlen, &actual_len);
32410023SGordon.Ross@Sun.COM if (rc != SPNEGO_E_SUCCESS) {
32510023SGordon.Ross@Sun.COM DPRINT("no OID? rc=0x%x", rc);
32610023SGordon.Ross@Sun.COM goto out;
32710023SGordon.Ross@Sun.COM }
32810023SGordon.Ross@Sun.COM if (dlen < actual_len)
32910023SGordon.Ross@Sun.COM goto out;
33010023SGordon.Ross@Sun.COM data += actual_len;
33110023SGordon.Ross@Sun.COM dlen -= actual_len;
33210023SGordon.Ross@Sun.COM
33310023SGordon.Ross@Sun.COM /* KRB_AP_REP or KRB_ERROR */
33410023SGordon.Ross@Sun.COM if (data[0] != KRB_AP_REP) {
33510023SGordon.Ross@Sun.COM DPRINT("KRB5 type: %d", data[1]);
33610023SGordon.Ross@Sun.COM goto out;
33710023SGordon.Ross@Sun.COM }
33810023SGordon.Ross@Sun.COM if (dlen < 2)
33910023SGordon.Ross@Sun.COM goto out;
34010023SGordon.Ross@Sun.COM data += 2;
34110023SGordon.Ross@Sun.COM dlen -= 2;
34210023SGordon.Ross@Sun.COM
34310023SGordon.Ross@Sun.COM /*
34410023SGordon.Ross@Sun.COM * Now what's left should be a krb5 reply
34510023SGordon.Ross@Sun.COM * NB: ap is NOT allocated, so don't free it.
34610023SGordon.Ross@Sun.COM */
34710023SGordon.Ross@Sun.COM ap.length = dlen;
34810023SGordon.Ross@Sun.COM ap.data = (char *)data;
34910023SGordon.Ross@Sun.COM rc = krb5_rd_rep(ss->ss_krb5ctx, ss->ss_auth, &ap, &reply);
35010023SGordon.Ross@Sun.COM if (rc != 0) {
35110023SGordon.Ross@Sun.COM DPRINT("krb5_rd_rep: err 0x%x (%s)",
35210023SGordon.Ross@Sun.COM rc, error_message(rc));
35310023SGordon.Ross@Sun.COM err = EAUTH;
35410023SGordon.Ross@Sun.COM goto out;
35510023SGordon.Ross@Sun.COM }
35610023SGordon.Ross@Sun.COM
35710023SGordon.Ross@Sun.COM /*
35810023SGordon.Ross@Sun.COM * Have the decoded reply. Save anything?
35910023SGordon.Ross@Sun.COM *
36010023SGordon.Ross@Sun.COM * NB: If this returns an error, we will get
36110023SGordon.Ross@Sun.COM * no more calls into this back-end module.
36210023SGordon.Ross@Sun.COM */
36310023SGordon.Ross@Sun.COM err = 0;
36410023SGordon.Ross@Sun.COM
36510023SGordon.Ross@Sun.COM out:
36610023SGordon.Ross@Sun.COM if (reply != NULL)
36710023SGordon.Ross@Sun.COM krb5_free_ap_rep_enc_part(ss->ss_krb5ctx, reply);
36810023SGordon.Ross@Sun.COM if (err)
36910023SGordon.Ross@Sun.COM DPRINT("ret %d", err);
37010023SGordon.Ross@Sun.COM
37110023SGordon.Ross@Sun.COM return (err);
37210023SGordon.Ross@Sun.COM }
37310023SGordon.Ross@Sun.COM
37410023SGordon.Ross@Sun.COM /*
37510023SGordon.Ross@Sun.COM * krb5ssp_final
37610023SGordon.Ross@Sun.COM *
37710023SGordon.Ross@Sun.COM * Called after successful authentication.
37810023SGordon.Ross@Sun.COM * Setup the MAC key for signing.
37910023SGordon.Ross@Sun.COM */
38010023SGordon.Ross@Sun.COM int
krb5ssp_final(struct ssp_ctx * sp)38110023SGordon.Ross@Sun.COM krb5ssp_final(struct ssp_ctx *sp)
38210023SGordon.Ross@Sun.COM {
38310023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx;
38410023SGordon.Ross@Sun.COM krb5ssp_state_t *ss = sp->sp_private;
38510023SGordon.Ross@Sun.COM krb5_keyblock *ssn_key = NULL;
38610023SGordon.Ross@Sun.COM int err, len;
38710023SGordon.Ross@Sun.COM
38810023SGordon.Ross@Sun.COM /*
38910023SGordon.Ross@Sun.COM * Save the session key, used for SMB signing
39010023SGordon.Ross@Sun.COM * and possibly other consumers (RPC).
39110023SGordon.Ross@Sun.COM */
39210023SGordon.Ross@Sun.COM err = krb5_auth_con_getlocalsubkey(
39310023SGordon.Ross@Sun.COM ss->ss_krb5ctx, ss->ss_auth, &ssn_key);
39410023SGordon.Ross@Sun.COM if (err != 0) {
39510023SGordon.Ross@Sun.COM DPRINT("_getlocalsubkey, err=0x%x (%s)",
39610023SGordon.Ross@Sun.COM err, error_message(err));
39710023SGordon.Ross@Sun.COM if (err <= 0 || err > ESTALE)
39810023SGordon.Ross@Sun.COM err = EAUTH;
39910023SGordon.Ross@Sun.COM goto out;
40010023SGordon.Ross@Sun.COM }
40110023SGordon.Ross@Sun.COM memset(ctx->ct_ssn_key, 0, SMBIOC_HASH_SZ);
40210023SGordon.Ross@Sun.COM if ((len = ssn_key->length) > SMBIOC_HASH_SZ)
40310023SGordon.Ross@Sun.COM len = SMBIOC_HASH_SZ;
40410023SGordon.Ross@Sun.COM memcpy(ctx->ct_ssn_key, ssn_key->contents, len);
40510023SGordon.Ross@Sun.COM
40610023SGordon.Ross@Sun.COM /*
40710023SGordon.Ross@Sun.COM * Set the MAC key on the first successful auth.
40810023SGordon.Ross@Sun.COM */
40910023SGordon.Ross@Sun.COM if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
41010023SGordon.Ross@Sun.COM (ctx->ct_mackey == NULL)) {
41110023SGordon.Ross@Sun.COM ctx->ct_mackeylen = ssn_key->length;
41210023SGordon.Ross@Sun.COM ctx->ct_mackey = malloc(ctx->ct_mackeylen);
41310023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) {
41410023SGordon.Ross@Sun.COM ctx->ct_mackeylen = 0;
41510023SGordon.Ross@Sun.COM err = ENOMEM;
41610023SGordon.Ross@Sun.COM goto out;
41710023SGordon.Ross@Sun.COM }
41810023SGordon.Ross@Sun.COM memcpy(ctx->ct_mackey, ssn_key->contents,
41910023SGordon.Ross@Sun.COM ctx->ct_mackeylen);
42010023SGordon.Ross@Sun.COM /*
42110023SGordon.Ross@Sun.COM * Apparently, the server used seq. no. zero
42210023SGordon.Ross@Sun.COM * for our previous message, so next is two.
42310023SGordon.Ross@Sun.COM */
42410023SGordon.Ross@Sun.COM ctx->ct_mac_seqno = 2;
42510023SGordon.Ross@Sun.COM }
42610023SGordon.Ross@Sun.COM err = 0;
42710023SGordon.Ross@Sun.COM
42810023SGordon.Ross@Sun.COM out:
42910023SGordon.Ross@Sun.COM if (ssn_key)
43010023SGordon.Ross@Sun.COM krb5_free_keyblock(ss->ss_krb5ctx, ssn_key);
43110023SGordon.Ross@Sun.COM
43210023SGordon.Ross@Sun.COM return (err);
43310023SGordon.Ross@Sun.COM }
43410023SGordon.Ross@Sun.COM
43510023SGordon.Ross@Sun.COM /*
43610023SGordon.Ross@Sun.COM * krb5ssp_next_token
43710023SGordon.Ross@Sun.COM *
43810023SGordon.Ross@Sun.COM * See ssp.c: ssp_ctx_next_token
43910023SGordon.Ross@Sun.COM */
44010023SGordon.Ross@Sun.COM int
krb5ssp_next_token(struct ssp_ctx * sp,struct mbdata * in_mb,struct mbdata * out_mb)44110023SGordon.Ross@Sun.COM krb5ssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
44210023SGordon.Ross@Sun.COM struct mbdata *out_mb)
44310023SGordon.Ross@Sun.COM {
44410023SGordon.Ross@Sun.COM int err;
44510023SGordon.Ross@Sun.COM
44610023SGordon.Ross@Sun.COM /*
44710023SGordon.Ross@Sun.COM * Note: in_mb == NULL on the first call.
44810023SGordon.Ross@Sun.COM */
44910023SGordon.Ross@Sun.COM if (in_mb) {
45010023SGordon.Ross@Sun.COM err = krb5ssp_get_reply(sp, in_mb);
45110023SGordon.Ross@Sun.COM if (err)
45210023SGordon.Ross@Sun.COM goto out;
45310023SGordon.Ross@Sun.COM }
45410023SGordon.Ross@Sun.COM
45510023SGordon.Ross@Sun.COM if (out_mb) {
45610023SGordon.Ross@Sun.COM err = krb5ssp_put_request(sp, out_mb);
45710023SGordon.Ross@Sun.COM } else
45810023SGordon.Ross@Sun.COM err = krb5ssp_final(sp);
45910023SGordon.Ross@Sun.COM
46010023SGordon.Ross@Sun.COM out:
46110023SGordon.Ross@Sun.COM if (err)
46210023SGordon.Ross@Sun.COM DPRINT("ret: %d", err);
46310023SGordon.Ross@Sun.COM return (err);
46410023SGordon.Ross@Sun.COM }
46510023SGordon.Ross@Sun.COM
46610023SGordon.Ross@Sun.COM /*
46710023SGordon.Ross@Sun.COM * krb5ssp_ctx_destroy
46810023SGordon.Ross@Sun.COM *
46910023SGordon.Ross@Sun.COM * Destroy mechanism-specific data.
47010023SGordon.Ross@Sun.COM */
47110023SGordon.Ross@Sun.COM void
krb5ssp_destroy(struct ssp_ctx * sp)47210023SGordon.Ross@Sun.COM krb5ssp_destroy(struct ssp_ctx *sp)
47310023SGordon.Ross@Sun.COM {
47410023SGordon.Ross@Sun.COM krb5ssp_state_t *ss;
47510023SGordon.Ross@Sun.COM krb5_context kctx;
47610023SGordon.Ross@Sun.COM
47710023SGordon.Ross@Sun.COM ss = sp->sp_private;
47810023SGordon.Ross@Sun.COM if (ss == NULL)
47910023SGordon.Ross@Sun.COM return;
48010023SGordon.Ross@Sun.COM sp->sp_private = NULL;
48110023SGordon.Ross@Sun.COM
48210023SGordon.Ross@Sun.COM if ((kctx = ss->ss_krb5ctx) != NULL) {
48310023SGordon.Ross@Sun.COM /* from krb5ssp_get_tkt */
48410023SGordon.Ross@Sun.COM if (ss->ss_auth)
48511364SGordon.Ross@Sun.COM (void) krb5_auth_con_free(kctx, ss->ss_auth);
48610023SGordon.Ross@Sun.COM /* from krb5ssp_init_client */
48710023SGordon.Ross@Sun.COM if (ss->ss_krb5clp)
48810023SGordon.Ross@Sun.COM krb5_free_principal(kctx, ss->ss_krb5clp);
48910023SGordon.Ross@Sun.COM if (ss->ss_krb5cc)
49011364SGordon.Ross@Sun.COM (void) krb5_cc_close(kctx, ss->ss_krb5cc);
49110023SGordon.Ross@Sun.COM krb5_free_context(kctx);
49210023SGordon.Ross@Sun.COM }
49310023SGordon.Ross@Sun.COM
49410023SGordon.Ross@Sun.COM free(ss);
49510023SGordon.Ross@Sun.COM }
49610023SGordon.Ross@Sun.COM
49710023SGordon.Ross@Sun.COM /*
49810023SGordon.Ross@Sun.COM * krb5ssp_init_clnt
49910023SGordon.Ross@Sun.COM *
50010023SGordon.Ross@Sun.COM * Initialize a new Kerberos SSP client context.
50110023SGordon.Ross@Sun.COM *
50210023SGordon.Ross@Sun.COM * The user must already have a TGT in their credential cache,
50310023SGordon.Ross@Sun.COM * as shown by the "klist" command.
50410023SGordon.Ross@Sun.COM */
50510023SGordon.Ross@Sun.COM int
krb5ssp_init_client(struct ssp_ctx * sp)50610023SGordon.Ross@Sun.COM krb5ssp_init_client(struct ssp_ctx *sp)
50710023SGordon.Ross@Sun.COM {
50810023SGordon.Ross@Sun.COM krb5ssp_state_t *ss;
50910023SGordon.Ross@Sun.COM krb5_error_code kerr;
51010023SGordon.Ross@Sun.COM krb5_context kctx = NULL;
51110023SGordon.Ross@Sun.COM krb5_ccache kcc = NULL;
51210023SGordon.Ross@Sun.COM krb5_principal kprin = NULL;
51310023SGordon.Ross@Sun.COM
51410023SGordon.Ross@Sun.COM if ((sp->smb_ctx->ct_authflags & SMB_AT_KRB5) == 0) {
51510023SGordon.Ross@Sun.COM DPRINT("KRB5 not in authflags");
51610023SGordon.Ross@Sun.COM return (ENOTSUP);
51710023SGordon.Ross@Sun.COM }
51810023SGordon.Ross@Sun.COM
51910023SGordon.Ross@Sun.COM ss = calloc(1, sizeof (*ss));
52010023SGordon.Ross@Sun.COM if (ss == NULL)
52110023SGordon.Ross@Sun.COM return (ENOMEM);
52210023SGordon.Ross@Sun.COM
52310023SGordon.Ross@Sun.COM sp->sp_nexttok = krb5ssp_next_token;
52410023SGordon.Ross@Sun.COM sp->sp_destroy = krb5ssp_destroy;
52510023SGordon.Ross@Sun.COM sp->sp_private = ss;
52610023SGordon.Ross@Sun.COM
52710023SGordon.Ross@Sun.COM kerr = krb5_init_context(&kctx);
52810023SGordon.Ross@Sun.COM if (kerr) {
52910023SGordon.Ross@Sun.COM DPRINT("krb5_init_context, kerr 0x%x", kerr);
53010023SGordon.Ross@Sun.COM goto errout;
53110023SGordon.Ross@Sun.COM }
53210023SGordon.Ross@Sun.COM ss->ss_krb5ctx = kctx;
53310023SGordon.Ross@Sun.COM
53410023SGordon.Ross@Sun.COM /* non-default would instead use krb5_cc_resolve */
53510023SGordon.Ross@Sun.COM kerr = krb5_cc_default(kctx, &kcc);
53610023SGordon.Ross@Sun.COM if (kerr) {
53710023SGordon.Ross@Sun.COM DPRINT("krb5_cc_default, kerr 0x%x", kerr);
53810023SGordon.Ross@Sun.COM goto errout;
53910023SGordon.Ross@Sun.COM }
54010023SGordon.Ross@Sun.COM ss->ss_krb5cc = kcc;
54110023SGordon.Ross@Sun.COM
54210023SGordon.Ross@Sun.COM /*
54310023SGordon.Ross@Sun.COM * Get the client principal (ticket),
54410023SGordon.Ross@Sun.COM * or discover that we don't have one.
54510023SGordon.Ross@Sun.COM */
54610023SGordon.Ross@Sun.COM kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
54710023SGordon.Ross@Sun.COM if (kerr) {
54810023SGordon.Ross@Sun.COM DPRINT("krb5_cc_get_principal, kerr 0x%x", kerr);
54910023SGordon.Ross@Sun.COM goto errout;
55010023SGordon.Ross@Sun.COM }
55110023SGordon.Ross@Sun.COM ss->ss_krb5clp = kprin;
55210023SGordon.Ross@Sun.COM
55310023SGordon.Ross@Sun.COM /* Success! */
55410023SGordon.Ross@Sun.COM DPRINT("Ticket cache: %s:%s",
55510023SGordon.Ross@Sun.COM krb5_cc_get_type(kctx, kcc),
55610023SGordon.Ross@Sun.COM krb5_cc_get_name(kctx, kcc));
55710023SGordon.Ross@Sun.COM return (0);
55810023SGordon.Ross@Sun.COM
55910023SGordon.Ross@Sun.COM errout:
56010023SGordon.Ross@Sun.COM krb5ssp_destroy(sp);
56110023SGordon.Ross@Sun.COM return (ENOTSUP);
56210023SGordon.Ross@Sun.COM }
563