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