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