xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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  */
26 
27 /*
28  * Support for SMB "signing" (message integrity)
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/conf.h>
34 #include <sys/proc.h>
35 #include <sys/fcntl.h>
36 #include <sys/socket.h>
37 #include <sys/md4.h>
38 #include <sys/md5.h>
39 #include <sys/des.h>
40 #include <sys/kmem.h>
41 #include <sys/cmn_err.h>
42 #include <sys/stream.h>
43 #include <sys/strsun.h>
44 #include <sys/sdt.h>
45 
46 #include <netsmb/smb_osdep.h>
47 #include <netsmb/smb.h>
48 #include <netsmb/smb_conn.h>
49 #include <netsmb/smb_subr.h>
50 #include <netsmb/smb_dev.h>
51 #include <netsmb/smb_rq.h>
52 #include <netsmb/smb_signing.h>
53 
54 #ifdef DEBUG
55 /*
56  * Set this to a small number to debug sequence numbers
57  * that seem to get out of step.
58  */
59 int nsmb_signing_fudge = 0;
60 #endif
61 
62 /*
63  * This is called just after session setup completes,
64  * at the top of smb_iod_vc_work().  Initialize signing.
65  */
66 int
67 smb_sign_init(smb_vc_t *vcp)
68 {
69 	int rc;
70 
71 	ASSERT(vcp->vc_ssnkey != NULL);
72 	ASSERT(vcp->vc_mackey == NULL);
73 
74 	rc = smb_md5_getmech(&vcp->vc_signmech);
75 	if (rc != 0) {
76 		cmn_err(CE_NOTE, "smb can't get signing mechanism");
77 		return (EAUTH);
78 	}
79 
80 	/*
81 	 * Convert the session key to the MAC key.
82 	 * SMB1 uses the whole session key.
83 	 */
84 	vcp->vc_mackeylen = vcp->vc_ssnkeylen;
85 	vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
86 	bcopy(vcp->vc_ssnkey, vcp->vc_mackey, vcp->vc_mackeylen);
87 
88 	/* The initial sequence number is two. */
89 	vcp->vc_next_seq = 2;
90 
91 	return (0);
92 }
93 
94 
95 #define	SMBSIGLEN	8	/* SMB signature length */
96 #define	SMBSIGOFF	14	/* SMB signature offset */
97 
98 /*
99  * Compute HMAC-MD5 of packet data, using the stored MAC key.
100  *
101  * See similar code for the server side:
102  * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc
103  */
104 static int
105 smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
106 	uint32_t seqno, uchar_t *signature)
107 {
108 	uchar_t digest[MD5_DIGEST_LENGTH];
109 	smb_sign_ctx_t ctx = 0;
110 	mblk_t *m = mp;
111 	int size;
112 	int rc;
113 
114 	/*
115 	 * This union is a little bit of trickery to:
116 	 * (1) get the sequence number int aligned, and
117 	 * (2) reduce the number of digest calls, at the
118 	 * cost of a copying 32 bytes instead of 8.
119 	 * Both sides of this union are 2+32 bytes.
120 	 */
121 	union {
122 		struct {
123 			uint8_t skip[2]; /* not used - just alignment */
124 			uint8_t raw[SMB_HDRLEN];  /* header length (32) */
125 		} r;
126 		struct {
127 			uint8_t skip[2]; /* not used - just alignment */
128 			uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
129 			uint32_t sig[2]; /* MAC signature, aligned! */
130 			uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
131 		} s;
132 	} smbhdr;
133 
134 	if (vcp->vc_mackey == NULL)
135 		return (-1);
136 
137 	if ((rc = smb_md5_init(&ctx, &vcp->vc_signmech)) != 0)
138 		return (rc);
139 
140 	/* Digest the MAC Key */
141 	rc = smb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen);
142 	if (rc != 0)
143 		return (rc);
144 
145 	/* Our caller should ensure mp has a contiguous header */
146 	ASSERT(m != NULL);
147 	ASSERT(MBLKL(m) >= SMB_HDRLEN);
148 
149 	/*
150 	 * Make an aligned copy of the SMB header,
151 	 * fill in the sequence number, and digest.
152 	 */
153 	size = SMB_HDRLEN;
154 	bcopy(m->b_rptr, smbhdr.r.raw, size);
155 	smbhdr.s.sig[0] = htolel(seqno);
156 	smbhdr.s.sig[1] = 0;
157 
158 	rc = smb_md5_update(ctx, &smbhdr.r.raw, size);
159 	if (rc != 0)
160 		return (rc);
161 
162 	/*
163 	 * Digest the rest of the SMB header packet, starting at
164 	 * the data just after the SMB header.
165 	 */
166 	size = MBLKL(m) - SMB_HDRLEN;
167 	rc = smb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size);
168 	if (rc != 0)
169 		return (rc);
170 	m = m->b_cont;
171 
172 	/* Digest rest of the SMB message. */
173 	while (m != NULL) {
174 		size = MBLKL(m);
175 		if (size > 0) {
176 			rc = smb_md5_update(ctx, m->b_rptr, size);
177 			if (rc != 0)
178 				return (rc);
179 		}
180 		m = m->b_cont;
181 	}
182 	rc = smb_md5_final(ctx, digest);
183 	if (rc != 0)
184 		return (rc);
185 
186 	/*
187 	 * Finally, store the signature.
188 	 * (first 8 bytes of the mac)
189 	 */
190 	if (signature != NULL)
191 		bcopy(digest, signature, SMBSIGLEN);
192 
193 	return (0);
194 }
195 
196 /*
197  * Sign a request with HMAC-MD5.
198  */
199 void
200 smb_rq_sign(struct smb_rq *rqp)
201 {
202 	struct smb_vc *vcp = rqp->sr_vc;
203 	mblk_t *mp = rqp->sr_rq.mb_top;
204 	uint8_t *sigloc;
205 	int status;
206 
207 	/*
208 	 * smb_rq_new() ensures this,
209 	 * but just in case..
210 	 */
211 	ASSERT(MBLKL(mp) >= SMB_HDRLEN);
212 	sigloc = mp->b_rptr + SMBSIGOFF;
213 
214 	if (vcp->vc_mackey == NULL) {
215 		/*
216 		 * Signing is required, but we have no key yet
217 		 * fill in with the magic fake signing value.
218 		 * This happens with SPNEGO, NTLMSSP, ...
219 		 */
220 		bcopy("BSRSPLY", sigloc, 8);
221 		return;
222 	}
223 
224 	/*
225 	 * This will compute the MAC and store it
226 	 * directly into the message at sigloc.
227 	 */
228 	status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
229 	if (status != 0) {
230 		SMBSDEBUG("Crypto error %d", status);
231 		bzero(sigloc, SMBSIGLEN);
232 	}
233 }
234 
235 /*
236  * Verify reply signature.
237  */
238 int
239 smb_rq_verify(struct smb_rq *rqp)
240 {
241 	struct smb_vc *vcp = rqp->sr_vc;
242 	mblk_t *mp = rqp->sr_rp.md_top;
243 	uint8_t sigbuf[SMBSIGLEN];
244 	uint8_t *sigloc;
245 	int fudge, rsn, status;
246 
247 	/*
248 	 * Note vc_mackey and vc_mackeylen gets filled in by
249 	 * smb_usr_iod_work as the connection comes in.
250 	 */
251 	if (vcp->vc_mackey == NULL) {
252 		SMBSDEBUG("no mac key\n");
253 		return (0);
254 	}
255 
256 	/*
257 	 * Let caller deal with empty reply or short messages by
258 	 * returning zero.  Caller will fail later, in parsing.
259 	 */
260 	if (mp == NULL) {
261 		SMBSDEBUG("empty reply\n");
262 		return (0);
263 	}
264 
265 	ASSERT(MBLKL(mp) >= SMB_HDRLEN);
266 	sigloc = mp->b_rptr + SMBSIGOFF;
267 
268 	/*
269 	 * Compute the expected signature in sigbuf.
270 	 */
271 	rsn = rqp->sr_rseqno;
272 	status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
273 	if (status != 0) {
274 		SMBSDEBUG("Crypto error %d", status);
275 		/*
276 		 * If we can't compute a MAC, then there's
277 		 * no point trying other seqno values.
278 		 */
279 		return (EBADRPC);
280 	}
281 
282 	/*
283 	 * Compare the computed signature with the
284 	 * one found in the message (at sigloc)
285 	 */
286 	if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
287 		return (0);
288 
289 	SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n",
290 	    vcp->vc_srvname, rqp->sr_mid, rsn);
291 
292 #ifdef DEBUG
293 	/*
294 	 * For diag purposes, we check whether the client/server idea
295 	 * of the sequence # has gotten a bit out of sync.
296 	 */
297 	for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
298 		(void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf);
299 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
300 			break;
301 		(void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf);
302 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
303 			fudge = -fudge;
304 			break;
305 		}
306 	}
307 	if (fudge <= nsmb_signing_fudge) {
308 		SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n",
309 		    rqp->sr_mid, rsn, rsn + fudge);
310 	}
311 #endif
312 	return (EBADRPC);
313 }
314