xref: /onnv-gate/usr/src/lib/libsmbfs/smb/signing.c (revision 11332:ed3411181494)
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