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 * Signing support, using libmd
29 */
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <strings.h>
36
37 #include <sys/types.h>
38 #include <sys/md5.h>
39
40 #include <netsmb/mchain.h>
41 #include <netsmb/smb.h>
42 #include <netsmb/smb_lib.h>
43
44 #include "private.h"
45
46 #define SMBSIGOFF 14 /* SMB signature offset */
47 #define SMBSIGLEN 8 /* SMB signature length */
48
49 /*
50 * Set this to a small number to debug sequence numbers
51 * that seem to get out of step.
52 */
53 #ifdef DEBUG
54 int nsmb_signing_fudge = 4;
55 #endif
56
57 /*
58 * Compute MD5 digest of packet data, using the stored MAC key.
59 *
60 * See similar code in the driver:
61 * uts/common/fs/smbclnt/netsmb/smb_signing.c
62 * and on the server side:
63 * uts/common/fs/smbsrv/smb_signing.c
64 */
65 static int
smb_compute_MAC(struct smb_ctx * ctx,mbuf_t * m,uint32_t seqno,uchar_t * signature)66 smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m,
67 uint32_t seqno, uchar_t *signature)
68 {
69 MD5_CTX md5;
70 uchar_t digest[MD5_DIGEST_LENGTH];
71
72 /*
73 * This union is a little bit of trickery to:
74 * (1) get the sequence number int aligned, and
75 * (2) reduce the number of digest calls, at the
76 * cost of a copying 32 bytes instead of 8.
77 * Both sides of this union are 2+32 bytes.
78 */
79 union {
80 struct {
81 uint8_t skip[2]; /* not used - just alignment */
82 uint8_t raw[SMB_HDRLEN]; /* header length (32) */
83 } r;
84 struct {
85 uint8_t skip[2]; /* not used - just alignment */
86 uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
87 uint32_t sig[2]; /* MAC signature, aligned! */
88 uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
89 } s;
90 } smbhdr;
91
92 if (m->m_len < SMB_HDRLEN)
93 return (EIO);
94 if (ctx->ct_mackey == NULL)
95 return (EINVAL);
96
97 /*
98 * Make an aligned copy of the SMB header
99 * and fill in the sequence number.
100 */
101 bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN);
102 smbhdr.s.sig[0] = htolel(seqno);
103 smbhdr.s.sig[1] = 0;
104
105 /*
106 * Compute the MAC: MD5(concat(Key, message))
107 */
108 MD5Init(&md5);
109
110 /* Digest the MAC Key */
111 MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen);
112
113 /* Digest the (copied) SMB header */
114 MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN);
115
116 /* Digest the rest of the first mbuf */
117 if (m->m_len > SMB_HDRLEN) {
118 MD5Update(&md5, m->m_data + SMB_HDRLEN,
119 m->m_len - SMB_HDRLEN);
120 }
121 m = m->m_next;
122
123 /* Digest rest of the SMB message. */
124 while (m) {
125 MD5Update(&md5, m->m_data, m->m_len);
126 m = m->m_next;
127 }
128
129 /* Final */
130 MD5Final(digest, &md5);
131
132 /*
133 * Finally, store the signature.
134 * (first 8 bytes of the digest)
135 */
136 if (signature)
137 bcopy(digest, signature, SMBSIGLEN);
138
139 return (0);
140 }
141
142 /*
143 * Sign a request with HMAC-MD5.
144 */
145 void
smb_rq_sign(struct smb_rq * rqp)146 smb_rq_sign(struct smb_rq *rqp)
147 {
148 struct smb_ctx *ctx = rqp->rq_ctx;
149 mbuf_t *m = rqp->rq_rq.mb_top;
150 uint8_t *sigloc;
151 int err;
152
153 /*
154 * Our mblk allocation ensures this,
155 * but just in case...
156 */
157 if (m->m_len < SMB_HDRLEN)
158 return;
159 sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
160
161 if (ctx->ct_mackey == NULL) {
162 /*
163 * Signing is required, but we have no key yet
164 * fill in with the magic fake signing value.
165 * This happens with SPNEGO, NTLMSSP, ...
166 */
167 bcopy("BSRSPLY", sigloc, 8);
168 return;
169 }
170
171 /*
172 * This will compute the MAC and store it
173 * directly into the message at sigloc.
174 */
175 rqp->rq_seqno = ctx->ct_mac_seqno;
176 ctx->ct_mac_seqno += 2;
177 err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc);
178 if (err) {
179 DPRINT("compute MAC, err %d", err);
180 bzero(sigloc, SMBSIGLEN);
181 }
182 }
183
184 /*
185 * Verify reply signature.
186 */
187 int
smb_rq_verify(struct smb_rq * rqp)188 smb_rq_verify(struct smb_rq *rqp)
189 {
190 struct smb_ctx *ctx = rqp->rq_ctx;
191 mbuf_t *m = rqp->rq_rp.mb_top;
192 uint8_t sigbuf[SMBSIGLEN];
193 uint8_t *sigloc;
194 uint32_t rseqno;
195 int err, fudge;
196
197 /*
198 * Note ct_mackey and ct_mackeylen gets initialized by
199 * smb_smb_ssnsetup. It's normal to have a null MAC key
200 * during extended security session setup.
201 */
202 if (ctx->ct_mackey == NULL)
203 return (0);
204
205 /*
206 * Let caller deal with empty reply or short messages by
207 * returning zero. Caller will fail later, in parsing.
208 */
209 if (m == NULL) {
210 DPRINT("empty reply");
211 return (0);
212 }
213 if (m->m_len < SMB_HDRLEN) {
214 DPRINT("short reply");
215 return (0);
216 }
217
218 sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
219 rseqno = rqp->rq_seqno + 1;
220
221 DPRINT("rq_rseqno = 0x%x", rseqno);
222
223 err = smb_compute_MAC(ctx, m, rseqno, sigbuf);
224 if (err) {
225 DPRINT("compute MAC, err %d", err);
226 /*
227 * If we can't compute a MAC, then there's
228 * no point trying other seqno values.
229 */
230 return (EBADRPC);
231 }
232
233 /*
234 * Compare the computed signature with the
235 * one found in the message (at sigloc)
236 */
237 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
238 return (0);
239
240 DPRINT("BAD signature, MID=0x%x", rqp->rq_mid);
241
242 #ifdef DEBUG
243 /*
244 * For diag purposes, we check whether the client/server idea
245 * of the sequence # has gotten a bit out of sync.
246 */
247 for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
248 (void) smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf);
249 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
250 break;
251 (void) smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf);
252 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
253 fudge = -fudge;
254 break;
255 }
256 }
257 if (fudge <= nsmb_signing_fudge) {
258 DPRINT("rseqno=%d, but %d would have worked",
259 rseqno, rseqno + fudge);
260 }
261 #endif
262 return (EBADRPC);
263 }
264