110023SGordon.Ross@Sun.COM /*
210023SGordon.Ross@Sun.COM * CDDL HEADER START
310023SGordon.Ross@Sun.COM *
410023SGordon.Ross@Sun.COM * The contents of this file are subject to the terms of the
510023SGordon.Ross@Sun.COM * Common Development and Distribution License (the "License").
610023SGordon.Ross@Sun.COM * You may not use this file except in compliance with the License.
710023SGordon.Ross@Sun.COM *
810023SGordon.Ross@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910023SGordon.Ross@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010023SGordon.Ross@Sun.COM * See the License for the specific language governing permissions
1110023SGordon.Ross@Sun.COM * and limitations under the License.
1210023SGordon.Ross@Sun.COM *
1310023SGordon.Ross@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410023SGordon.Ross@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510023SGordon.Ross@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610023SGordon.Ross@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710023SGordon.Ross@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810023SGordon.Ross@Sun.COM *
1910023SGordon.Ross@Sun.COM * CDDL HEADER END
2010023SGordon.Ross@Sun.COM */
2110023SGordon.Ross@Sun.COM
2210023SGordon.Ross@Sun.COM /*
2310023SGordon.Ross@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2410023SGordon.Ross@Sun.COM * Use is subject to license terms.
2510023SGordon.Ross@Sun.COM */
2610023SGordon.Ross@Sun.COM
2710023SGordon.Ross@Sun.COM /*
2810023SGordon.Ross@Sun.COM * Signing support, using libmd
2910023SGordon.Ross@Sun.COM */
3010023SGordon.Ross@Sun.COM
3110023SGordon.Ross@Sun.COM #include <errno.h>
3210023SGordon.Ross@Sun.COM #include <stdio.h>
3310023SGordon.Ross@Sun.COM #include <stdlib.h>
3410023SGordon.Ross@Sun.COM #include <unistd.h>
3510023SGordon.Ross@Sun.COM #include <strings.h>
3610023SGordon.Ross@Sun.COM
3710023SGordon.Ross@Sun.COM #include <sys/types.h>
3810023SGordon.Ross@Sun.COM #include <sys/md5.h>
3910023SGordon.Ross@Sun.COM
4010023SGordon.Ross@Sun.COM #include <netsmb/mchain.h>
4110023SGordon.Ross@Sun.COM #include <netsmb/smb.h>
4210023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
4310023SGordon.Ross@Sun.COM
4410023SGordon.Ross@Sun.COM #include "private.h"
4510023SGordon.Ross@Sun.COM
4610023SGordon.Ross@Sun.COM #define SMBSIGOFF 14 /* SMB signature offset */
4710023SGordon.Ross@Sun.COM #define SMBSIGLEN 8 /* SMB signature length */
4810023SGordon.Ross@Sun.COM
4910023SGordon.Ross@Sun.COM /*
5010023SGordon.Ross@Sun.COM * Set this to a small number to debug sequence numbers
5110023SGordon.Ross@Sun.COM * that seem to get out of step.
5210023SGordon.Ross@Sun.COM */
5310023SGordon.Ross@Sun.COM #ifdef DEBUG
5410023SGordon.Ross@Sun.COM int nsmb_signing_fudge = 4;
5510023SGordon.Ross@Sun.COM #endif
5610023SGordon.Ross@Sun.COM
5710023SGordon.Ross@Sun.COM /*
5810023SGordon.Ross@Sun.COM * Compute MD5 digest of packet data, using the stored MAC key.
5910023SGordon.Ross@Sun.COM *
6010023SGordon.Ross@Sun.COM * See similar code in the driver:
6110023SGordon.Ross@Sun.COM * uts/common/fs/smbclnt/netsmb/smb_signing.c
6210023SGordon.Ross@Sun.COM * and on the server side:
6310023SGordon.Ross@Sun.COM * uts/common/fs/smbsrv/smb_signing.c
6410023SGordon.Ross@Sun.COM */
6510023SGordon.Ross@Sun.COM static int
smb_compute_MAC(struct smb_ctx * ctx,mbuf_t * m,uint32_t seqno,uchar_t * signature)6610023SGordon.Ross@Sun.COM smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m,
6710023SGordon.Ross@Sun.COM uint32_t seqno, uchar_t *signature)
6810023SGordon.Ross@Sun.COM {
6910023SGordon.Ross@Sun.COM MD5_CTX md5;
7010023SGordon.Ross@Sun.COM uchar_t digest[MD5_DIGEST_LENGTH];
7110023SGordon.Ross@Sun.COM
7210023SGordon.Ross@Sun.COM /*
7310023SGordon.Ross@Sun.COM * This union is a little bit of trickery to:
7410023SGordon.Ross@Sun.COM * (1) get the sequence number int aligned, and
7510023SGordon.Ross@Sun.COM * (2) reduce the number of digest calls, at the
7610023SGordon.Ross@Sun.COM * cost of a copying 32 bytes instead of 8.
7710023SGordon.Ross@Sun.COM * Both sides of this union are 2+32 bytes.
7810023SGordon.Ross@Sun.COM */
7910023SGordon.Ross@Sun.COM union {
8010023SGordon.Ross@Sun.COM struct {
8110023SGordon.Ross@Sun.COM uint8_t skip[2]; /* not used - just alignment */
8210023SGordon.Ross@Sun.COM uint8_t raw[SMB_HDRLEN]; /* header length (32) */
8310023SGordon.Ross@Sun.COM } r;
8410023SGordon.Ross@Sun.COM struct {
8510023SGordon.Ross@Sun.COM uint8_t skip[2]; /* not used - just alignment */
8610023SGordon.Ross@Sun.COM uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
8710023SGordon.Ross@Sun.COM uint32_t sig[2]; /* MAC signature, aligned! */
8810023SGordon.Ross@Sun.COM uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
8910023SGordon.Ross@Sun.COM } s;
9010023SGordon.Ross@Sun.COM } smbhdr;
9110023SGordon.Ross@Sun.COM
9210023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN)
9310023SGordon.Ross@Sun.COM return (EIO);
9410023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL)
9510023SGordon.Ross@Sun.COM return (EINVAL);
9610023SGordon.Ross@Sun.COM
9710023SGordon.Ross@Sun.COM /*
9810023SGordon.Ross@Sun.COM * Make an aligned copy of the SMB header
9910023SGordon.Ross@Sun.COM * and fill in the sequence number.
10010023SGordon.Ross@Sun.COM */
10110023SGordon.Ross@Sun.COM bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN);
10210023SGordon.Ross@Sun.COM smbhdr.s.sig[0] = htolel(seqno);
10310023SGordon.Ross@Sun.COM smbhdr.s.sig[1] = 0;
10410023SGordon.Ross@Sun.COM
10510023SGordon.Ross@Sun.COM /*
10610023SGordon.Ross@Sun.COM * Compute the MAC: MD5(concat(Key, message))
10710023SGordon.Ross@Sun.COM */
10810023SGordon.Ross@Sun.COM MD5Init(&md5);
10910023SGordon.Ross@Sun.COM
11010023SGordon.Ross@Sun.COM /* Digest the MAC Key */
11110023SGordon.Ross@Sun.COM MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen);
11210023SGordon.Ross@Sun.COM
11310023SGordon.Ross@Sun.COM /* Digest the (copied) SMB header */
11410023SGordon.Ross@Sun.COM MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN);
11510023SGordon.Ross@Sun.COM
11610023SGordon.Ross@Sun.COM /* Digest the rest of the first mbuf */
11710023SGordon.Ross@Sun.COM if (m->m_len > SMB_HDRLEN) {
11810023SGordon.Ross@Sun.COM MD5Update(&md5, m->m_data + SMB_HDRLEN,
11910023SGordon.Ross@Sun.COM m->m_len - SMB_HDRLEN);
12010023SGordon.Ross@Sun.COM }
12110023SGordon.Ross@Sun.COM m = m->m_next;
12210023SGordon.Ross@Sun.COM
12310023SGordon.Ross@Sun.COM /* Digest rest of the SMB message. */
12410023SGordon.Ross@Sun.COM while (m) {
12510023SGordon.Ross@Sun.COM MD5Update(&md5, m->m_data, m->m_len);
12610023SGordon.Ross@Sun.COM m = m->m_next;
12710023SGordon.Ross@Sun.COM }
12810023SGordon.Ross@Sun.COM
12910023SGordon.Ross@Sun.COM /* Final */
13010023SGordon.Ross@Sun.COM MD5Final(digest, &md5);
13110023SGordon.Ross@Sun.COM
13210023SGordon.Ross@Sun.COM /*
13310023SGordon.Ross@Sun.COM * Finally, store the signature.
13410023SGordon.Ross@Sun.COM * (first 8 bytes of the digest)
13510023SGordon.Ross@Sun.COM */
13610023SGordon.Ross@Sun.COM if (signature)
13710023SGordon.Ross@Sun.COM bcopy(digest, signature, SMBSIGLEN);
13810023SGordon.Ross@Sun.COM
13910023SGordon.Ross@Sun.COM return (0);
14010023SGordon.Ross@Sun.COM }
14110023SGordon.Ross@Sun.COM
14210023SGordon.Ross@Sun.COM /*
14310023SGordon.Ross@Sun.COM * Sign a request with HMAC-MD5.
14410023SGordon.Ross@Sun.COM */
145*11332SGordon.Ross@Sun.COM void
smb_rq_sign(struct smb_rq * rqp)14610023SGordon.Ross@Sun.COM smb_rq_sign(struct smb_rq *rqp)
14710023SGordon.Ross@Sun.COM {
14810023SGordon.Ross@Sun.COM struct smb_ctx *ctx = rqp->rq_ctx;
14910023SGordon.Ross@Sun.COM mbuf_t *m = rqp->rq_rq.mb_top;
15010023SGordon.Ross@Sun.COM uint8_t *sigloc;
15110023SGordon.Ross@Sun.COM int err;
15210023SGordon.Ross@Sun.COM
15310023SGordon.Ross@Sun.COM /*
15410023SGordon.Ross@Sun.COM * Our mblk allocation ensures this,
15510023SGordon.Ross@Sun.COM * but just in case...
15610023SGordon.Ross@Sun.COM */
15710023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN)
158*11332SGordon.Ross@Sun.COM return;
15910023SGordon.Ross@Sun.COM sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
16010023SGordon.Ross@Sun.COM
16110023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) {
16210023SGordon.Ross@Sun.COM /*
16310023SGordon.Ross@Sun.COM * Signing is required, but we have no key yet
16410023SGordon.Ross@Sun.COM * fill in with the magic fake signing value.
16510023SGordon.Ross@Sun.COM * This happens with SPNEGO, NTLMSSP, ...
16610023SGordon.Ross@Sun.COM */
16710023SGordon.Ross@Sun.COM bcopy("BSRSPLY", sigloc, 8);
168*11332SGordon.Ross@Sun.COM return;
16910023SGordon.Ross@Sun.COM }
17010023SGordon.Ross@Sun.COM
17110023SGordon.Ross@Sun.COM /*
17210023SGordon.Ross@Sun.COM * This will compute the MAC and store it
17310023SGordon.Ross@Sun.COM * directly into the message at sigloc.
17410023SGordon.Ross@Sun.COM */
17510023SGordon.Ross@Sun.COM rqp->rq_seqno = ctx->ct_mac_seqno;
17610023SGordon.Ross@Sun.COM ctx->ct_mac_seqno += 2;
17710023SGordon.Ross@Sun.COM err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc);
17810023SGordon.Ross@Sun.COM if (err) {
17910023SGordon.Ross@Sun.COM DPRINT("compute MAC, err %d", err);
18010023SGordon.Ross@Sun.COM bzero(sigloc, SMBSIGLEN);
18110023SGordon.Ross@Sun.COM }
18210023SGordon.Ross@Sun.COM }
18310023SGordon.Ross@Sun.COM
18410023SGordon.Ross@Sun.COM /*
18510023SGordon.Ross@Sun.COM * Verify reply signature.
18610023SGordon.Ross@Sun.COM */
18710023SGordon.Ross@Sun.COM int
smb_rq_verify(struct smb_rq * rqp)18810023SGordon.Ross@Sun.COM smb_rq_verify(struct smb_rq *rqp)
18910023SGordon.Ross@Sun.COM {
19010023SGordon.Ross@Sun.COM struct smb_ctx *ctx = rqp->rq_ctx;
19110023SGordon.Ross@Sun.COM mbuf_t *m = rqp->rq_rp.mb_top;
19210023SGordon.Ross@Sun.COM uint8_t sigbuf[SMBSIGLEN];
19310023SGordon.Ross@Sun.COM uint8_t *sigloc;
19410023SGordon.Ross@Sun.COM uint32_t rseqno;
19510023SGordon.Ross@Sun.COM int err, fudge;
19610023SGordon.Ross@Sun.COM
19710023SGordon.Ross@Sun.COM /*
19810023SGordon.Ross@Sun.COM * Note ct_mackey and ct_mackeylen gets initialized by
19910023SGordon.Ross@Sun.COM * smb_smb_ssnsetup. It's normal to have a null MAC key
20010023SGordon.Ross@Sun.COM * during extended security session setup.
20110023SGordon.Ross@Sun.COM */
20210023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL)
20310023SGordon.Ross@Sun.COM return (0);
20410023SGordon.Ross@Sun.COM
20510023SGordon.Ross@Sun.COM /*
20610023SGordon.Ross@Sun.COM * Let caller deal with empty reply or short messages by
20710023SGordon.Ross@Sun.COM * returning zero. Caller will fail later, in parsing.
20810023SGordon.Ross@Sun.COM */
20910023SGordon.Ross@Sun.COM if (m == NULL) {
21010023SGordon.Ross@Sun.COM DPRINT("empty reply");
21110023SGordon.Ross@Sun.COM return (0);
21210023SGordon.Ross@Sun.COM }
21310023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN) {
21410023SGordon.Ross@Sun.COM DPRINT("short reply");
21510023SGordon.Ross@Sun.COM return (0);
21610023SGordon.Ross@Sun.COM }
21710023SGordon.Ross@Sun.COM
21810023SGordon.Ross@Sun.COM sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
21910023SGordon.Ross@Sun.COM rseqno = rqp->rq_seqno + 1;
22010023SGordon.Ross@Sun.COM
22110023SGordon.Ross@Sun.COM DPRINT("rq_rseqno = 0x%x", rseqno);
22210023SGordon.Ross@Sun.COM
22310023SGordon.Ross@Sun.COM err = smb_compute_MAC(ctx, m, rseqno, sigbuf);
22410023SGordon.Ross@Sun.COM if (err) {
22510023SGordon.Ross@Sun.COM DPRINT("compute MAC, err %d", err);
22610023SGordon.Ross@Sun.COM /*
22710023SGordon.Ross@Sun.COM * If we can't compute a MAC, then there's
22810023SGordon.Ross@Sun.COM * no point trying other seqno values.
22910023SGordon.Ross@Sun.COM */
23010023SGordon.Ross@Sun.COM return (EBADRPC);
23110023SGordon.Ross@Sun.COM }
23210023SGordon.Ross@Sun.COM
23310023SGordon.Ross@Sun.COM /*
23410023SGordon.Ross@Sun.COM * Compare the computed signature with the
23510023SGordon.Ross@Sun.COM * one found in the message (at sigloc)
23610023SGordon.Ross@Sun.COM */
23710023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
23810023SGordon.Ross@Sun.COM return (0);
23910023SGordon.Ross@Sun.COM
24010023SGordon.Ross@Sun.COM DPRINT("BAD signature, MID=0x%x", rqp->rq_mid);
24110023SGordon.Ross@Sun.COM
24210023SGordon.Ross@Sun.COM #ifdef DEBUG
24310023SGordon.Ross@Sun.COM /*
24410023SGordon.Ross@Sun.COM * For diag purposes, we check whether the client/server idea
24510023SGordon.Ross@Sun.COM * of the sequence # has gotten a bit out of sync.
24610023SGordon.Ross@Sun.COM */
24710023SGordon.Ross@Sun.COM for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
248*11332SGordon.Ross@Sun.COM (void) smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf);
24910023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
25010023SGordon.Ross@Sun.COM break;
251*11332SGordon.Ross@Sun.COM (void) smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf);
25210023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
25310023SGordon.Ross@Sun.COM fudge = -fudge;
25410023SGordon.Ross@Sun.COM break;
25510023SGordon.Ross@Sun.COM }
25610023SGordon.Ross@Sun.COM }
25710023SGordon.Ross@Sun.COM if (fudge <= nsmb_signing_fudge) {
25810023SGordon.Ross@Sun.COM DPRINT("rseqno=%d, but %d would have worked",
25910023SGordon.Ross@Sun.COM rseqno, rseqno + fudge);
26010023SGordon.Ross@Sun.COM }
26110023SGordon.Ross@Sun.COM #endif
26210023SGordon.Ross@Sun.COM return (EBADRPC);
26310023SGordon.Ross@Sun.COM }
264