xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb2_sign.c (revision 48e11a6ea0245c522078ddb86a73f16c8c28b949)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25  * Copyright 2024 RackTop Systems, Inc.
26  */
27 
28 /*
29  * Support for SMB2 "signing" (message integrity)
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/conf.h>
35 #include <sys/proc.h>
36 #include <sys/fcntl.h>
37 #include <sys/socket.h>
38 #include <sys/md4.h>
39 #include <sys/md5.h>
40 #include <sys/des.h>
41 #include <sys/kmem.h>
42 #include <sys/cmn_err.h>
43 #include <sys/stream.h>
44 #include <sys/strsun.h>
45 #include <sys/sdt.h>
46 
47 #include <netsmb/nsmb_kcrypt.h>
48 
49 #include <netsmb/smb_osdep.h>
50 #include <netsmb/smb2.h>
51 #include <netsmb/smb_conn.h>
52 #include <netsmb/smb_subr.h>
53 #include <netsmb/smb_dev.h>
54 #include <netsmb/smb_rq.h>
55 
56 #define	SMB2_SIG_OFF	48
57 #define	SMB2_SIG_LEN	16
58 
59 typedef struct smb_mac_ops {
60 	int (*mac_init)(smb_sign_ctx_t *, smb_crypto_mech_t *,
61 			uint8_t *, size_t);
62 	int (*mac_update)(smb_sign_ctx_t, uint8_t *, size_t);
63 	int (*mac_final)(smb_sign_ctx_t, uint8_t *);
64 } smb_mac_ops_t;
65 
66 static smb_mac_ops_t
67 smb2_sign_ops = {
68 	nsmb_hmac_init,
69 	nsmb_hmac_update,
70 	nsmb_hmac_final
71 };
72 
73 static struct smb_mac_ops
74 smb3_sign_ops = {
75 	nsmb_cmac_init,
76 	nsmb_cmac_update,
77 	nsmb_cmac_final
78 };
79 
80 /*
81  * smb2_sign_init
82  *
83  * Get the mechanism info and initilize SMB2 or SMB3 signing.
84  */
85 int
smb2_sign_init(smb_vc_t * vcp)86 smb2_sign_init(smb_vc_t *vcp)
87 {
88 	uint_t copysize;
89 	int rc;
90 
91 	ASSERT(vcp->vc_ssnkey != NULL);
92 	ASSERT(vcp->vc_mackey == NULL);
93 
94 	if (SMB_DIALECT(vcp) < SMB2_DIALECT_0300)
95 		rc = nsmb_hmac_getmech(&vcp->vc_signmech);
96 	else
97 		rc = nsmb_cmac_getmech(&vcp->vc_signmech);
98 	if (rc != 0)
99 		return (EAUTH);
100 
101 	/*
102 	 * Convert the session key to the MAC key.
103 	 *
104 	 * For SMB2, the signing key is just the first 16 bytes
105 	 * of the session key (truncated or padded with zeros).
106 	 * For SMB3, the signing key is a "KDF" hash of the
107 	 * session key.   [MS-SMB2] 3.2.5.3.1
108 	 */
109 	vcp->vc_mackeylen = SMB2_SIG_LEN;
110 	vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
111 	if (SMB_DIALECT(vcp) < SMB2_DIALECT_0300) {
112 		copysize = vcp->vc_ssnkeylen;
113 		if (copysize > vcp->vc_mackeylen)
114 			copysize = vcp->vc_mackeylen;
115 		bcopy(vcp->vc_ssnkey, vcp->vc_mackey, copysize);
116 
117 		vcp->vc_sign_ops = &smb2_sign_ops;
118 	} else {
119 		rc = nsmb_kdf(vcp->vc_mackey, SMB3_KEYLEN,
120 		    vcp->vc_ssnkey, vcp->vc_ssnkeylen,
121 		    (uint8_t *)"SMB2AESCMAC", 12,
122 		    (uint8_t *)"SmbSign", 8);
123 		if (rc != 0)
124 			return (EAUTH);
125 		vcp->vc_sign_ops = &smb3_sign_ops;
126 	}
127 
128 	return (0);
129 }
130 
131 /*
132  * Compute MAC signature of packet data, using the stored MAC key.
133  *
134  * The signature is in the last 16 bytes of the SMB2 header.
135  * The signature algorighm is to compute HMAC SHA256 over the
136  * entire command, with the signature field set to zeros.
137  *
138  * See similar code for the server side:
139  * uts/common/fs/smbsrv/smb2_signing.c : smb2_sign_calc
140  */
141 static int
smb2_compute_MAC(struct smb_vc * vcp,mblk_t * mp,uchar_t * signature)142 smb2_compute_MAC(struct smb_vc *vcp, mblk_t *mp, uchar_t *signature)
143 {
144 	uint8_t tmp_hdr[SMB2_HDR_SIZE];
145 	smb_sign_ctx_t ctx = 0;
146 	smb_mac_ops_t *ops;
147 	mblk_t *m = mp;
148 	int size;
149 	int rc;
150 
151 	if (vcp->vc_mackey == NULL)
152 		return (-1);
153 	if ((ops = vcp->vc_sign_ops) == NULL)
154 		return (-1);
155 
156 	rc = ops->mac_init(&ctx, &vcp->vc_signmech,
157 	    vcp->vc_mackey, vcp->vc_mackeylen);
158 	if (rc != 0)
159 		return (rc);
160 
161 	/* Our caller should ensure mp has a contiguous header */
162 	ASSERT(m != NULL);
163 	ASSERT(MBLKL(m) >= SMB2_HDRLEN);
164 
165 	/*
166 	 * Copy of the SMB2 header, zero out the signature, and digest.
167 	 */
168 	size = SMB2_HDRLEN;
169 	bcopy(m->b_rptr, tmp_hdr, size);
170 	bzero(tmp_hdr + SMB2_SIG_OFF, SMB2_SIG_LEN);
171 	rc = ops->mac_update(ctx, tmp_hdr, size);
172 	if (rc != 0)
173 		return (rc);
174 
175 	/*
176 	 * Digest the rest of the SMB2 header packet, starting at
177 	 * the data just after the SMB2 header.
178 	 */
179 	size = MBLKL(m) - SMB2_HDRLEN;
180 	rc = ops->mac_update(ctx, m->b_rptr + SMB2_HDRLEN, size);
181 	if (rc != 0)
182 		return (rc);
183 	m = m->b_cont;
184 
185 	/* Digest rest of the SMB2 message. */
186 	while (m != NULL) {
187 		size = MBLKL(m);
188 		if (size > 0) {
189 			rc = ops->mac_update(ctx, m->b_rptr, size);
190 			if (rc != 0)
191 				return (rc);
192 		}
193 		m = m->b_cont;
194 	}
195 	rc = ops->mac_final(ctx, signature);
196 
197 	return (rc);
198 }
199 
200 /*
201  * Sign a request with HMAC-MD5.
202  */
203 void
smb2_rq_sign(struct smb_rq * rqp)204 smb2_rq_sign(struct smb_rq *rqp)
205 {
206 	struct smb_vc *vcp = rqp->sr_vc;
207 	mblk_t *mp = rqp->sr_rq.mb_top;
208 	uint8_t *sigloc;
209 	int rc;
210 
211 	/*
212 	 * smb_rq_new() ensures this,
213 	 * but just in case..
214 	 */
215 	ASSERT(MBLKL(mp) >= SMB2_HDRLEN);
216 	sigloc = mp->b_rptr + SMB2_SIG_OFF;
217 
218 	if (vcp->vc_mackey == NULL)
219 		return;
220 
221 	/*
222 	 * This will compute the MAC and store it
223 	 * directly into the message at sigloc.
224 	 */
225 	rc = smb2_compute_MAC(vcp, mp, sigloc);
226 	if (rc != 0) {
227 		SMBSDEBUG("Crypto error %d", rc);
228 		bzero(sigloc, SMB2_SIG_LEN);
229 	}
230 }
231 
232 /*
233  * Verify reply signature.
234  */
235 int
smb2_rq_verify(struct smb_rq * rqp)236 smb2_rq_verify(struct smb_rq *rqp)
237 {
238 	struct smb_vc *vcp = rqp->sr_vc;
239 	mblk_t *mp = rqp->sr_rp.md_top;
240 	uint8_t sigbuf[SMB2_SIG_LEN];
241 	uint8_t *sigloc;
242 	int rc;
243 
244 	/*
245 	 * Note vc_mackey and vc_mackeylen gets filled in by
246 	 * smb_usr_iod_work as the connection comes in.
247 	 */
248 	if (vcp->vc_mackey == NULL) {
249 		SMBSDEBUG("no mac key\n");
250 		return (0);
251 	}
252 
253 	/*
254 	 * Let caller deal with empty reply or short messages by
255 	 * returning zero.  Caller will fail later, in parsing.
256 	 */
257 	if (mp == NULL) {
258 		SMBSDEBUG("empty reply\n");
259 		return (0);
260 	}
261 
262 	/* smb2_iod_process ensures this */
263 	ASSERT(MBLKL(mp) >= SMB2_HDRLEN);
264 	sigloc = mp->b_rptr + SMB2_SIG_OFF;
265 
266 	/*
267 	 * Compute the expected signature in sigbuf.
268 	 */
269 	rc = smb2_compute_MAC(vcp, mp, sigbuf);
270 	if (rc != 0) {
271 		SMBSDEBUG("Crypto error %d", rc);
272 		/*
273 		 * If we can't compute a MAC, then there's
274 		 * no point trying other seqno values.
275 		 */
276 		return (EBADRPC);
277 	}
278 
279 	/*
280 	 * Compare the computed signature with the
281 	 * one found in the message (at sigloc)
282 	 */
283 	if (bcmp(sigbuf, sigloc, SMB2_SIG_LEN) == 0)
284 		return (0);
285 
286 	SMBERROR("BAD signature, Server=%s MID=0x%llx\n",
287 	    vcp->vc_srvname, (long long)rqp->sr2_messageid);
288 
289 	return (EBADRPC);
290 }
291