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
smb_sign_init(smb_vc_t * vcp)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
smb_compute_MAC(struct smb_vc * vcp,mblk_t * mp,uint32_t seqno,uchar_t * signature)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
smb_rq_sign(struct smb_rq * rqp)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
smb_rq_verify(struct smb_rq * rqp)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