xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c (revision 48e11a6ea0245c522078ddb86a73f16c8c28b949)
1613a2f6bSGordon Ross /*
2613a2f6bSGordon Ross  * CDDL HEADER START
3613a2f6bSGordon Ross  *
4613a2f6bSGordon Ross  * The contents of this file are subject to the terms of the
5613a2f6bSGordon Ross  * Common Development and Distribution License (the "License").
6613a2f6bSGordon Ross  * You may not use this file except in compliance with the License.
7613a2f6bSGordon Ross  *
8613a2f6bSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9613a2f6bSGordon Ross  * or http://www.opensolaris.org/os/licensing.
10613a2f6bSGordon Ross  * See the License for the specific language governing permissions
11613a2f6bSGordon Ross  * and limitations under the License.
12613a2f6bSGordon Ross  *
13613a2f6bSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14613a2f6bSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15613a2f6bSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16613a2f6bSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17613a2f6bSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18613a2f6bSGordon Ross  *
19613a2f6bSGordon Ross  * CDDL HEADER END
20613a2f6bSGordon Ross  */
21613a2f6bSGordon Ross 
22613a2f6bSGordon Ross /*
23a547be5dSGordon Ross  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2440c0e231SGordon Ross  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25*48e11a6eSGordon Ross  * Copyright 2024 RackTop Systems, Inc.
26613a2f6bSGordon Ross  */
27613a2f6bSGordon Ross 
28613a2f6bSGordon Ross /*
29613a2f6bSGordon Ross  * Support for SMB "signing" (message integrity)
30613a2f6bSGordon Ross  */
31613a2f6bSGordon Ross 
32613a2f6bSGordon Ross #include <sys/param.h>
33613a2f6bSGordon Ross #include <sys/systm.h>
34613a2f6bSGordon Ross #include <sys/conf.h>
35613a2f6bSGordon Ross #include <sys/proc.h>
36613a2f6bSGordon Ross #include <sys/fcntl.h>
37613a2f6bSGordon Ross #include <sys/socket.h>
38613a2f6bSGordon Ross #include <sys/md4.h>
39613a2f6bSGordon Ross #include <sys/md5.h>
40613a2f6bSGordon Ross #include <sys/des.h>
41613a2f6bSGordon Ross #include <sys/kmem.h>
42613a2f6bSGordon Ross #include <sys/cmn_err.h>
43613a2f6bSGordon Ross #include <sys/stream.h>
44613a2f6bSGordon Ross #include <sys/strsun.h>
45613a2f6bSGordon Ross #include <sys/sdt.h>
46613a2f6bSGordon Ross 
47*48e11a6eSGordon Ross #include <netsmb/nsmb_kcrypt.h>
48*48e11a6eSGordon Ross 
49613a2f6bSGordon Ross #include <netsmb/smb_osdep.h>
50613a2f6bSGordon Ross #include <netsmb/smb.h>
51613a2f6bSGordon Ross #include <netsmb/smb_conn.h>
52613a2f6bSGordon Ross #include <netsmb/smb_subr.h>
53613a2f6bSGordon Ross #include <netsmb/smb_dev.h>
54613a2f6bSGordon Ross #include <netsmb/smb_rq.h>
55613a2f6bSGordon Ross 
56613a2f6bSGordon Ross #ifdef DEBUG
57613a2f6bSGordon Ross /*
58613a2f6bSGordon Ross  * Set this to a small number to debug sequence numbers
59613a2f6bSGordon Ross  * that seem to get out of step.
60613a2f6bSGordon Ross  */
61613a2f6bSGordon Ross int nsmb_signing_fudge = 0;
62613a2f6bSGordon Ross #endif
63613a2f6bSGordon Ross 
6440c0e231SGordon Ross /*
6540c0e231SGordon Ross  * This is called just after session setup completes,
6640c0e231SGordon Ross  * at the top of smb_iod_vc_work().  Initialize signing.
6740c0e231SGordon Ross  */
6840c0e231SGordon Ross int
smb_sign_init(smb_vc_t * vcp)6940c0e231SGordon Ross smb_sign_init(smb_vc_t *vcp)
7040c0e231SGordon Ross {
71adee6784SGordon Ross 	int rc;
7240c0e231SGordon Ross 
7340c0e231SGordon Ross 	ASSERT(vcp->vc_ssnkey != NULL);
7440c0e231SGordon Ross 	ASSERT(vcp->vc_mackey == NULL);
7540c0e231SGordon Ross 
76*48e11a6eSGordon Ross 	rc = nsmb_md5_getmech(&vcp->vc_signmech);
77adee6784SGordon Ross 	if (rc != 0) {
78adee6784SGordon Ross 		cmn_err(CE_NOTE, "smb can't get signing mechanism");
79adee6784SGordon Ross 		return (EAUTH);
80adee6784SGordon Ross 	}
81adee6784SGordon Ross 
8240c0e231SGordon Ross 	/*
8340c0e231SGordon Ross 	 * Convert the session key to the MAC key.
8440c0e231SGordon Ross 	 * SMB1 uses the whole session key.
8540c0e231SGordon Ross 	 */
8640c0e231SGordon Ross 	vcp->vc_mackeylen = vcp->vc_ssnkeylen;
8740c0e231SGordon Ross 	vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
8840c0e231SGordon Ross 	bcopy(vcp->vc_ssnkey, vcp->vc_mackey, vcp->vc_mackeylen);
8940c0e231SGordon Ross 
9040c0e231SGordon Ross 	/* The initial sequence number is two. */
9140c0e231SGordon Ross 	vcp->vc_next_seq = 2;
9240c0e231SGordon Ross 
9340c0e231SGordon Ross 	return (0);
9440c0e231SGordon Ross }
9540c0e231SGordon Ross 
96613a2f6bSGordon Ross 
97613a2f6bSGordon Ross #define	SMBSIGLEN	8	/* SMB signature length */
98613a2f6bSGordon Ross #define	SMBSIGOFF	14	/* SMB signature offset */
99613a2f6bSGordon Ross 
100613a2f6bSGordon Ross /*
101613a2f6bSGordon Ross  * Compute HMAC-MD5 of packet data, using the stored MAC key.
102613a2f6bSGordon Ross  *
103613a2f6bSGordon Ross  * See similar code for the server side:
104613a2f6bSGordon Ross  * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc
105613a2f6bSGordon Ross  */
106613a2f6bSGordon Ross static int
smb_compute_MAC(struct smb_vc * vcp,mblk_t * mp,uint32_t seqno,uchar_t * signature)107613a2f6bSGordon Ross smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
108613a2f6bSGordon Ross     uint32_t seqno, uchar_t *signature)
109613a2f6bSGordon Ross {
1108329232eSGordon Ross 	uchar_t digest[MD5_DIGEST_LENGTH];
1118329232eSGordon Ross 	smb_sign_ctx_t ctx = 0;
1128329232eSGordon Ross 	mblk_t *m = mp;
1138329232eSGordon Ross 	int size;
1148329232eSGordon Ross 	int rc;
1158329232eSGordon Ross 
116613a2f6bSGordon Ross 	/*
117613a2f6bSGordon Ross 	 * This union is a little bit of trickery to:
118613a2f6bSGordon Ross 	 * (1) get the sequence number int aligned, and
119613a2f6bSGordon Ross 	 * (2) reduce the number of digest calls, at the
120613a2f6bSGordon Ross 	 * cost of a copying 32 bytes instead of 8.
121613a2f6bSGordon Ross 	 * Both sides of this union are 2+32 bytes.
122613a2f6bSGordon Ross 	 */
123613a2f6bSGordon Ross 	union {
124613a2f6bSGordon Ross 		struct {
125613a2f6bSGordon Ross 			uint8_t skip[2]; /* not used - just alignment */
126613a2f6bSGordon Ross 			uint8_t raw[SMB_HDRLEN];  /* header length (32) */
127613a2f6bSGordon Ross 		} r;
128613a2f6bSGordon Ross 		struct {
129613a2f6bSGordon Ross 			uint8_t skip[2]; /* not used - just alignment */
130613a2f6bSGordon Ross 			uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
131613a2f6bSGordon Ross 			uint32_t sig[2]; /* MAC signature, aligned! */
132613a2f6bSGordon Ross 			uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
133613a2f6bSGordon Ross 		} s;
134613a2f6bSGordon Ross 	} smbhdr;
135613a2f6bSGordon Ross 
1368329232eSGordon Ross 	if (vcp->vc_mackey == NULL)
1378329232eSGordon Ross 		return (-1);
1388329232eSGordon Ross 
139*48e11a6eSGordon Ross 	if ((rc = nsmb_md5_init(&ctx, &vcp->vc_signmech)) != 0)
1408329232eSGordon Ross 		return (rc);
1418329232eSGordon Ross 
1428329232eSGordon Ross 	/* Digest the MAC Key */
143*48e11a6eSGordon Ross 	rc = nsmb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen);
1448329232eSGordon Ross 	if (rc != 0)
1458329232eSGordon Ross 		return (rc);
1468329232eSGordon Ross 
147adee6784SGordon Ross 	/* Our caller should ensure mp has a contiguous header */
1488329232eSGordon Ross 	ASSERT(m != NULL);
1498329232eSGordon Ross 	ASSERT(MBLKL(m) >= SMB_HDRLEN);
150613a2f6bSGordon Ross 
151613a2f6bSGordon Ross 	/*
1528329232eSGordon Ross 	 * Make an aligned copy of the SMB header,
1538329232eSGordon Ross 	 * fill in the sequence number, and digest.
154613a2f6bSGordon Ross 	 */
1558329232eSGordon Ross 	size = SMB_HDRLEN;
1568329232eSGordon Ross 	bcopy(m->b_rptr, smbhdr.r.raw, size);
157613a2f6bSGordon Ross 	smbhdr.s.sig[0] = htolel(seqno);
158613a2f6bSGordon Ross 	smbhdr.s.sig[1] = 0;
159613a2f6bSGordon Ross 
160*48e11a6eSGordon Ross 	rc = nsmb_md5_update(ctx, &smbhdr.r.raw, size);
1618329232eSGordon Ross 	if (rc != 0)
1628329232eSGordon Ross 		return (rc);
1638329232eSGordon Ross 
164613a2f6bSGordon Ross 	/*
1658329232eSGordon Ross 	 * Digest the rest of the SMB header packet, starting at
1668329232eSGordon Ross 	 * the data just after the SMB header.
167613a2f6bSGordon Ross 	 */
1688329232eSGordon Ross 	size = MBLKL(m) - SMB_HDRLEN;
169*48e11a6eSGordon Ross 	rc = nsmb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size);
1708329232eSGordon Ross 	if (rc != 0)
1718329232eSGordon Ross 		return (rc);
1728329232eSGordon Ross 	m = m->b_cont;
173613a2f6bSGordon Ross 
174613a2f6bSGordon Ross 	/* Digest rest of the SMB message. */
1758329232eSGordon Ross 	while (m != NULL) {
1768329232eSGordon Ross 		size = MBLKL(m);
1778329232eSGordon Ross 		if (size > 0) {
178*48e11a6eSGordon Ross 			rc = nsmb_md5_update(ctx, m->b_rptr, size);
1798329232eSGordon Ross 			if (rc != 0)
1808329232eSGordon Ross 				return (rc);
1818329232eSGordon Ross 		}
1828329232eSGordon Ross 		m = m->b_cont;
1838329232eSGordon Ross 	}
184*48e11a6eSGordon Ross 	rc = nsmb_md5_final(ctx, digest);
1858329232eSGordon Ross 	if (rc != 0)
1868329232eSGordon Ross 		return (rc);
187613a2f6bSGordon Ross 
188613a2f6bSGordon Ross 	/*
189613a2f6bSGordon Ross 	 * Finally, store the signature.
190613a2f6bSGordon Ross 	 * (first 8 bytes of the mac)
191613a2f6bSGordon Ross 	 */
192adee6784SGordon Ross 	if (signature != NULL)
1938329232eSGordon Ross 		bcopy(digest, signature, SMBSIGLEN);
194613a2f6bSGordon Ross 
195613a2f6bSGordon Ross 	return (0);
196613a2f6bSGordon Ross }
197613a2f6bSGordon Ross 
198613a2f6bSGordon Ross /*
199613a2f6bSGordon Ross  * Sign a request with HMAC-MD5.
200613a2f6bSGordon Ross  */
20102d09e03SGordon Ross void
smb_rq_sign(struct smb_rq * rqp)202613a2f6bSGordon Ross smb_rq_sign(struct smb_rq *rqp)
203613a2f6bSGordon Ross {
204613a2f6bSGordon Ross 	struct smb_vc *vcp = rqp->sr_vc;
205613a2f6bSGordon Ross 	mblk_t *mp = rqp->sr_rq.mb_top;
206613a2f6bSGordon Ross 	uint8_t *sigloc;
207613a2f6bSGordon Ross 	int status;
208613a2f6bSGordon Ross 
209613a2f6bSGordon Ross 	/*
210adee6784SGordon Ross 	 * smb_rq_new() ensures this,
211adee6784SGordon Ross 	 * but just in case..
212613a2f6bSGordon Ross 	 */
213adee6784SGordon Ross 	ASSERT(MBLKL(mp) >= SMB_HDRLEN);
214613a2f6bSGordon Ross 	sigloc = mp->b_rptr + SMBSIGOFF;
215613a2f6bSGordon Ross 
216613a2f6bSGordon Ross 	if (vcp->vc_mackey == NULL) {
217613a2f6bSGordon Ross 		/*
218613a2f6bSGordon Ross 		 * Signing is required, but we have no key yet
219613a2f6bSGordon Ross 		 * fill in with the magic fake signing value.
220613a2f6bSGordon Ross 		 * This happens with SPNEGO, NTLMSSP, ...
221613a2f6bSGordon Ross 		 */
222613a2f6bSGordon Ross 		bcopy("BSRSPLY", sigloc, 8);
22302d09e03SGordon Ross 		return;
224613a2f6bSGordon Ross 	}
225613a2f6bSGordon Ross 
226613a2f6bSGordon Ross 	/*
227613a2f6bSGordon Ross 	 * This will compute the MAC and store it
228613a2f6bSGordon Ross 	 * directly into the message at sigloc.
229613a2f6bSGordon Ross 	 */
230613a2f6bSGordon Ross 	status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
2318329232eSGordon Ross 	if (status != 0) {
232613a2f6bSGordon Ross 		SMBSDEBUG("Crypto error %d", status);
233613a2f6bSGordon Ross 		bzero(sigloc, SMBSIGLEN);
234613a2f6bSGordon Ross 	}
235613a2f6bSGordon Ross }
236613a2f6bSGordon Ross 
237613a2f6bSGordon Ross /*
238613a2f6bSGordon Ross  * Verify reply signature.
239613a2f6bSGordon Ross  */
240613a2f6bSGordon Ross int
smb_rq_verify(struct smb_rq * rqp)241613a2f6bSGordon Ross smb_rq_verify(struct smb_rq *rqp)
242613a2f6bSGordon Ross {
243613a2f6bSGordon Ross 	struct smb_vc *vcp = rqp->sr_vc;
244613a2f6bSGordon Ross 	mblk_t *mp = rqp->sr_rp.md_top;
245613a2f6bSGordon Ross 	uint8_t sigbuf[SMBSIGLEN];
246613a2f6bSGordon Ross 	uint8_t *sigloc;
24702d09e03SGordon Ross 	int fudge, rsn, status;
248613a2f6bSGordon Ross 
249613a2f6bSGordon Ross 	/*
250613a2f6bSGordon Ross 	 * Note vc_mackey and vc_mackeylen gets filled in by
251613a2f6bSGordon Ross 	 * smb_usr_iod_work as the connection comes in.
252613a2f6bSGordon Ross 	 */
253613a2f6bSGordon Ross 	if (vcp->vc_mackey == NULL) {
254613a2f6bSGordon Ross 		SMBSDEBUG("no mac key\n");
255613a2f6bSGordon Ross 		return (0);
256613a2f6bSGordon Ross 	}
257613a2f6bSGordon Ross 
258613a2f6bSGordon Ross 	/*
259613a2f6bSGordon Ross 	 * Let caller deal with empty reply or short messages by
260613a2f6bSGordon Ross 	 * returning zero.  Caller will fail later, in parsing.
261613a2f6bSGordon Ross 	 */
262613a2f6bSGordon Ross 	if (mp == NULL) {
263613a2f6bSGordon Ross 		SMBSDEBUG("empty reply\n");
264613a2f6bSGordon Ross 		return (0);
265613a2f6bSGordon Ross 	}
266adee6784SGordon Ross 
267adee6784SGordon Ross 	ASSERT(MBLKL(mp) >= SMB_HDRLEN);
268613a2f6bSGordon Ross 	sigloc = mp->b_rptr + SMBSIGOFF;
269613a2f6bSGordon Ross 
27002d09e03SGordon Ross 	/*
27102d09e03SGordon Ross 	 * Compute the expected signature in sigbuf.
27202d09e03SGordon Ross 	 */
27302d09e03SGordon Ross 	rsn = rqp->sr_rseqno;
27402d09e03SGordon Ross 	status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
2758329232eSGordon Ross 	if (status != 0) {
276613a2f6bSGordon Ross 		SMBSDEBUG("Crypto error %d", status);
277613a2f6bSGordon Ross 		/*
278613a2f6bSGordon Ross 		 * If we can't compute a MAC, then there's
279613a2f6bSGordon Ross 		 * no point trying other seqno values.
280613a2f6bSGordon Ross 		 */
281613a2f6bSGordon Ross 		return (EBADRPC);
282613a2f6bSGordon Ross 	}
283613a2f6bSGordon Ross 
284613a2f6bSGordon Ross 	/*
285613a2f6bSGordon Ross 	 * Compare the computed signature with the
286613a2f6bSGordon Ross 	 * one found in the message (at sigloc)
287613a2f6bSGordon Ross 	 */
288613a2f6bSGordon Ross 	if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
289613a2f6bSGordon Ross 		return (0);
290613a2f6bSGordon Ross 
291a547be5dSGordon Ross 	SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n",
292a547be5dSGordon Ross 	    vcp->vc_srvname, rqp->sr_mid, rsn);
293613a2f6bSGordon Ross 
294613a2f6bSGordon Ross #ifdef DEBUG
295613a2f6bSGordon Ross 	/*
296613a2f6bSGordon Ross 	 * For diag purposes, we check whether the client/server idea
297613a2f6bSGordon Ross 	 * of the sequence # has gotten a bit out of sync.
298613a2f6bSGordon Ross 	 */
299613a2f6bSGordon Ross 	for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
30002d09e03SGordon Ross 		(void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf);
301613a2f6bSGordon Ross 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
302613a2f6bSGordon Ross 			break;
30302d09e03SGordon Ross 		(void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf);
304613a2f6bSGordon Ross 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
305613a2f6bSGordon Ross 			fudge = -fudge;
306613a2f6bSGordon Ross 			break;
307613a2f6bSGordon Ross 		}
308613a2f6bSGordon Ross 	}
309613a2f6bSGordon Ross 	if (fudge <= nsmb_signing_fudge) {
310a547be5dSGordon Ross 		SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n",
311a547be5dSGordon Ross 		    rqp->sr_mid, rsn, rsn + fudge);
312613a2f6bSGordon Ross 	}
313613a2f6bSGordon Ross #endif
314613a2f6bSGordon Ross 	return (EBADRPC);
315613a2f6bSGordon Ross }
316