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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Signing support, using libmd
37 #include <sys/types.h>
40 #include <netsmb/mchain.h>
41 #include <netsmb/smb.h>
42 #include <netsmb/smb_lib.h>
46 #define SMBSIGOFF 14 /* SMB signature offset */
47 #define SMBSIGLEN 8 /* SMB signature length */
50 * Set this to a small number to debug sequence numbers
51 * that seem to get out of step.
54 int nsmb_signing_fudge
= 4;
58 * Compute MD5 digest of packet data, using the stored MAC key.
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
66 smb_compute_MAC(struct smb_ctx
*ctx
, mbuf_t
*m
,
67 uint32_t seqno
, uchar_t
*signature
)
70 uchar_t digest
[MD5_DIGEST_LENGTH
];
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.
81 uint8_t skip
[2]; /* not used - just alignment */
82 uint8_t raw
[SMB_HDRLEN
]; /* header length (32) */
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 */
92 if (m
->m_len
< SMB_HDRLEN
)
94 if (ctx
->ct_mackey
== NULL
)
98 * Make an aligned copy of the SMB header
99 * and fill in the sequence number.
101 bcopy(m
->m_data
, smbhdr
.r
.raw
, SMB_HDRLEN
);
102 smbhdr
.s
.sig
[0] = htolel(seqno
);
106 * Compute the MAC: MD5(concat(Key, message))
110 /* Digest the MAC Key */
111 MD5Update(&md5
, ctx
->ct_mackey
, ctx
->ct_mackeylen
);
113 /* Digest the (copied) SMB header */
114 MD5Update(&md5
, smbhdr
.r
.raw
, SMB_HDRLEN
);
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
);
123 /* Digest rest of the SMB message. */
125 MD5Update(&md5
, m
->m_data
, m
->m_len
);
130 MD5Final(digest
, &md5
);
133 * Finally, store the signature.
134 * (first 8 bytes of the digest)
137 bcopy(digest
, signature
, SMBSIGLEN
);
143 * Sign a request with HMAC-MD5.
146 smb_rq_sign(struct smb_rq
*rqp
)
148 struct smb_ctx
*ctx
= rqp
->rq_ctx
;
149 mbuf_t
*m
= rqp
->rq_rq
.mb_top
;
154 * Our mblk allocation ensures this,
155 * but just in case...
157 if (m
->m_len
< SMB_HDRLEN
)
159 sigloc
= (uchar_t
*)m
->m_data
+ SMBSIGOFF
;
161 if (ctx
->ct_mackey
== NULL
) {
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, ...
167 bcopy("BSRSPLY", sigloc
, 8);
172 * This will compute the MAC and store it
173 * directly into the message at sigloc.
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
);
179 DPRINT("compute MAC, err %d", err
);
180 bzero(sigloc
, SMBSIGLEN
);
185 * Verify reply signature.
188 smb_rq_verify(struct smb_rq
*rqp
)
190 struct smb_ctx
*ctx
= rqp
->rq_ctx
;
191 mbuf_t
*m
= rqp
->rq_rp
.mb_top
;
192 uint8_t sigbuf
[SMBSIGLEN
];
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.
202 if (ctx
->ct_mackey
== NULL
)
206 * Let caller deal with empty reply or short messages by
207 * returning zero. Caller will fail later, in parsing.
210 DPRINT("empty reply");
213 if (m
->m_len
< SMB_HDRLEN
) {
214 DPRINT("short reply");
218 sigloc
= (uchar_t
*)m
->m_data
+ SMBSIGOFF
;
219 rseqno
= rqp
->rq_seqno
+ 1;
221 DPRINT("rq_rseqno = 0x%x", rseqno
);
223 err
= smb_compute_MAC(ctx
, m
, rseqno
, sigbuf
);
225 DPRINT("compute MAC, err %d", err
);
227 * If we can't compute a MAC, then there's
228 * no point trying other seqno values.
234 * Compare the computed signature with the
235 * one found in the message (at sigloc)
237 if (bcmp(sigbuf
, sigloc
, SMBSIGLEN
) == 0)
240 DPRINT("BAD signature, MID=0x%x", rqp
->rq_mid
);
244 * For diag purposes, we check whether the client/server idea
245 * of the sequence # has gotten a bit out of sync.
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)
251 (void) smb_compute_MAC(ctx
, m
, rseqno
- fudge
, sigbuf
);
252 if (bcmp(sigbuf
, sigloc
, SMBSIGLEN
) == 0) {
257 if (fudge
<= nsmb_signing_fudge
) {
258 DPRINT("rseqno=%d, but %d would have worked",
259 rseqno
, rseqno
+ fudge
);