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