xref: /titanic_52/usr/src/lib/libsmbfs/smb/signing.c (revision 02d09e03eb27f3a2dc299de704e45dae5173f43f)
1613a2f6bSGordon Ross /*
2613a2f6bSGordon Ross  * CDDL HEADER START
3613a2f6bSGordon Ross  *
4613a2f6bSGordon Ross  * The contents of this file are subject to the terms of the
5613a2f6bSGordon Ross  * Common Development and Distribution License (the "License").
6613a2f6bSGordon Ross  * You may not use this file except in compliance with the License.
7613a2f6bSGordon Ross  *
8613a2f6bSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9613a2f6bSGordon Ross  * or http://www.opensolaris.org/os/licensing.
10613a2f6bSGordon Ross  * See the License for the specific language governing permissions
11613a2f6bSGordon Ross  * and limitations under the License.
12613a2f6bSGordon Ross  *
13613a2f6bSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14613a2f6bSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15613a2f6bSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16613a2f6bSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17613a2f6bSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18613a2f6bSGordon Ross  *
19613a2f6bSGordon Ross  * CDDL HEADER END
20613a2f6bSGordon Ross  */
21613a2f6bSGordon Ross 
22613a2f6bSGordon Ross /*
23613a2f6bSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24613a2f6bSGordon Ross  * Use is subject to license terms.
25613a2f6bSGordon Ross  */
26613a2f6bSGordon Ross 
27613a2f6bSGordon Ross /*
28613a2f6bSGordon Ross  * Signing support, using libmd
29613a2f6bSGordon Ross  */
30613a2f6bSGordon Ross 
31613a2f6bSGordon Ross #include <errno.h>
32613a2f6bSGordon Ross #include <stdio.h>
33613a2f6bSGordon Ross #include <stdlib.h>
34613a2f6bSGordon Ross #include <unistd.h>
35613a2f6bSGordon Ross #include <strings.h>
36613a2f6bSGordon Ross 
37613a2f6bSGordon Ross #include <sys/types.h>
38613a2f6bSGordon Ross #include <sys/md5.h>
39613a2f6bSGordon Ross 
40613a2f6bSGordon Ross #include <netsmb/mchain.h>
41613a2f6bSGordon Ross #include <netsmb/smb.h>
42613a2f6bSGordon Ross #include <netsmb/smb_lib.h>
43613a2f6bSGordon Ross 
44613a2f6bSGordon Ross #include "private.h"
45613a2f6bSGordon Ross 
46613a2f6bSGordon Ross #define	SMBSIGOFF	14	/* SMB signature offset */
47613a2f6bSGordon Ross #define	SMBSIGLEN	8	/* SMB signature length */
48613a2f6bSGordon Ross 
49613a2f6bSGordon Ross /*
50613a2f6bSGordon Ross  * Set this to a small number to debug sequence numbers
51613a2f6bSGordon Ross  * that seem to get out of step.
52613a2f6bSGordon Ross  */
53613a2f6bSGordon Ross #ifdef DEBUG
54613a2f6bSGordon Ross int nsmb_signing_fudge = 4;
55613a2f6bSGordon Ross #endif
56613a2f6bSGordon Ross 
57613a2f6bSGordon Ross /*
58613a2f6bSGordon Ross  * Compute MD5 digest of packet data, using the stored MAC key.
59613a2f6bSGordon Ross  *
60613a2f6bSGordon Ross  * See similar code in the driver:
61613a2f6bSGordon Ross  *	uts/common/fs/smbclnt/netsmb/smb_signing.c
62613a2f6bSGordon Ross  * and on the server side:
63613a2f6bSGordon Ross  *	uts/common/fs/smbsrv/smb_signing.c
64613a2f6bSGordon Ross  */
65613a2f6bSGordon Ross static int
66613a2f6bSGordon Ross smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m,
67613a2f6bSGordon Ross 	uint32_t seqno, uchar_t *signature)
68613a2f6bSGordon Ross {
69613a2f6bSGordon Ross 	MD5_CTX md5;
70613a2f6bSGordon Ross 	uchar_t digest[MD5_DIGEST_LENGTH];
71613a2f6bSGordon Ross 
72613a2f6bSGordon Ross 	/*
73613a2f6bSGordon Ross 	 * This union is a little bit of trickery to:
74613a2f6bSGordon Ross 	 * (1) get the sequence number int aligned, and
75613a2f6bSGordon Ross 	 * (2) reduce the number of digest calls, at the
76613a2f6bSGordon Ross 	 * cost of a copying 32 bytes instead of 8.
77613a2f6bSGordon Ross 	 * Both sides of this union are 2+32 bytes.
78613a2f6bSGordon Ross 	 */
79613a2f6bSGordon Ross 	union {
80613a2f6bSGordon Ross 		struct {
81613a2f6bSGordon Ross 			uint8_t skip[2]; /* not used - just alignment */
82613a2f6bSGordon Ross 			uint8_t raw[SMB_HDRLEN];  /* header length (32) */
83613a2f6bSGordon Ross 		} r;
84613a2f6bSGordon Ross 		struct {
85613a2f6bSGordon Ross 			uint8_t skip[2]; /* not used - just alignment */
86613a2f6bSGordon Ross 			uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
87613a2f6bSGordon Ross 			uint32_t sig[2]; /* MAC signature, aligned! */
88613a2f6bSGordon Ross 			uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
89613a2f6bSGordon Ross 		} s;
90613a2f6bSGordon Ross 	} smbhdr;
91613a2f6bSGordon Ross 
92613a2f6bSGordon Ross 	if (m->m_len < SMB_HDRLEN)
93613a2f6bSGordon Ross 		return (EIO);
94613a2f6bSGordon Ross 	if (ctx->ct_mackey == NULL)
95613a2f6bSGordon Ross 		return (EINVAL);
96613a2f6bSGordon Ross 
97613a2f6bSGordon Ross 	/*
98613a2f6bSGordon Ross 	 * Make an aligned copy of the SMB header
99613a2f6bSGordon Ross 	 * and fill in the sequence number.
100613a2f6bSGordon Ross 	 */
101613a2f6bSGordon Ross 	bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN);
102613a2f6bSGordon Ross 	smbhdr.s.sig[0] = htolel(seqno);
103613a2f6bSGordon Ross 	smbhdr.s.sig[1] = 0;
104613a2f6bSGordon Ross 
105613a2f6bSGordon Ross 	/*
106613a2f6bSGordon Ross 	 * Compute the MAC: MD5(concat(Key, message))
107613a2f6bSGordon Ross 	 */
108613a2f6bSGordon Ross 	MD5Init(&md5);
109613a2f6bSGordon Ross 
110613a2f6bSGordon Ross 	/* Digest the MAC Key */
111613a2f6bSGordon Ross 	MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen);
112613a2f6bSGordon Ross 
113613a2f6bSGordon Ross 	/* Digest the (copied) SMB header */
114613a2f6bSGordon Ross 	MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN);
115613a2f6bSGordon Ross 
116613a2f6bSGordon Ross 	/* Digest the rest of the first mbuf */
117613a2f6bSGordon Ross 	if (m->m_len > SMB_HDRLEN) {
118613a2f6bSGordon Ross 		MD5Update(&md5, m->m_data + SMB_HDRLEN,
119613a2f6bSGordon Ross 		    m->m_len - SMB_HDRLEN);
120613a2f6bSGordon Ross 	}
121613a2f6bSGordon Ross 	m = m->m_next;
122613a2f6bSGordon Ross 
123613a2f6bSGordon Ross 	/* Digest rest of the SMB message. */
124613a2f6bSGordon Ross 	while (m) {
125613a2f6bSGordon Ross 		MD5Update(&md5, m->m_data, m->m_len);
126613a2f6bSGordon Ross 		m = m->m_next;
127613a2f6bSGordon Ross 	}
128613a2f6bSGordon Ross 
129613a2f6bSGordon Ross 	/* Final */
130613a2f6bSGordon Ross 	MD5Final(digest, &md5);
131613a2f6bSGordon Ross 
132613a2f6bSGordon Ross 	/*
133613a2f6bSGordon Ross 	 * Finally, store the signature.
134613a2f6bSGordon Ross 	 * (first 8 bytes of the digest)
135613a2f6bSGordon Ross 	 */
136613a2f6bSGordon Ross 	if (signature)
137613a2f6bSGordon Ross 		bcopy(digest, signature, SMBSIGLEN);
138613a2f6bSGordon Ross 
139613a2f6bSGordon Ross 	return (0);
140613a2f6bSGordon Ross }
141613a2f6bSGordon Ross 
142613a2f6bSGordon Ross /*
143613a2f6bSGordon Ross  * Sign a request with HMAC-MD5.
144613a2f6bSGordon Ross  */
145*02d09e03SGordon Ross void
146613a2f6bSGordon Ross smb_rq_sign(struct smb_rq *rqp)
147613a2f6bSGordon Ross {
148613a2f6bSGordon Ross 	struct smb_ctx *ctx = rqp->rq_ctx;
149613a2f6bSGordon Ross 	mbuf_t *m = rqp->rq_rq.mb_top;
150613a2f6bSGordon Ross 	uint8_t *sigloc;
151613a2f6bSGordon Ross 	int err;
152613a2f6bSGordon Ross 
153613a2f6bSGordon Ross 	/*
154613a2f6bSGordon Ross 	 * Our mblk allocation ensures this,
155613a2f6bSGordon Ross 	 * but just in case...
156613a2f6bSGordon Ross 	 */
157613a2f6bSGordon Ross 	if (m->m_len < SMB_HDRLEN)
158*02d09e03SGordon Ross 		return;
159613a2f6bSGordon Ross 	sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
160613a2f6bSGordon Ross 
161613a2f6bSGordon Ross 	if (ctx->ct_mackey == NULL) {
162613a2f6bSGordon Ross 		/*
163613a2f6bSGordon Ross 		 * Signing is required, but we have no key yet
164613a2f6bSGordon Ross 		 * fill in with the magic fake signing value.
165613a2f6bSGordon Ross 		 * This happens with SPNEGO, NTLMSSP, ...
166613a2f6bSGordon Ross 		 */
167613a2f6bSGordon Ross 		bcopy("BSRSPLY", sigloc, 8);
168*02d09e03SGordon Ross 		return;
169613a2f6bSGordon Ross 	}
170613a2f6bSGordon Ross 
171613a2f6bSGordon Ross 	/*
172613a2f6bSGordon Ross 	 * This will compute the MAC and store it
173613a2f6bSGordon Ross 	 * directly into the message at sigloc.
174613a2f6bSGordon Ross 	 */
175613a2f6bSGordon Ross 	rqp->rq_seqno = ctx->ct_mac_seqno;
176613a2f6bSGordon Ross 	ctx->ct_mac_seqno += 2;
177613a2f6bSGordon Ross 	err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc);
178613a2f6bSGordon Ross 	if (err) {
179613a2f6bSGordon Ross 		DPRINT("compute MAC, err %d", err);
180613a2f6bSGordon Ross 		bzero(sigloc, SMBSIGLEN);
181613a2f6bSGordon Ross 	}
182613a2f6bSGordon Ross }
183613a2f6bSGordon Ross 
184613a2f6bSGordon Ross /*
185613a2f6bSGordon Ross  * Verify reply signature.
186613a2f6bSGordon Ross  */
187613a2f6bSGordon Ross int
188613a2f6bSGordon Ross smb_rq_verify(struct smb_rq *rqp)
189613a2f6bSGordon Ross {
190613a2f6bSGordon Ross 	struct smb_ctx *ctx = rqp->rq_ctx;
191613a2f6bSGordon Ross 	mbuf_t *m = rqp->rq_rp.mb_top;
192613a2f6bSGordon Ross 	uint8_t sigbuf[SMBSIGLEN];
193613a2f6bSGordon Ross 	uint8_t *sigloc;
194613a2f6bSGordon Ross 	uint32_t rseqno;
195613a2f6bSGordon Ross 	int err, fudge;
196613a2f6bSGordon Ross 
197613a2f6bSGordon Ross 	/*
198613a2f6bSGordon Ross 	 * Note ct_mackey and ct_mackeylen gets initialized by
199613a2f6bSGordon Ross 	 * smb_smb_ssnsetup.  It's normal to have a null MAC key
200613a2f6bSGordon Ross 	 * during extended security session setup.
201613a2f6bSGordon Ross 	 */
202613a2f6bSGordon Ross 	if (ctx->ct_mackey == NULL)
203613a2f6bSGordon Ross 		return (0);
204613a2f6bSGordon Ross 
205613a2f6bSGordon Ross 	/*
206613a2f6bSGordon Ross 	 * Let caller deal with empty reply or short messages by
207613a2f6bSGordon Ross 	 * returning zero.  Caller will fail later, in parsing.
208613a2f6bSGordon Ross 	 */
209613a2f6bSGordon Ross 	if (m == NULL) {
210613a2f6bSGordon Ross 		DPRINT("empty reply");
211613a2f6bSGordon Ross 		return (0);
212613a2f6bSGordon Ross 	}
213613a2f6bSGordon Ross 	if (m->m_len < SMB_HDRLEN) {
214613a2f6bSGordon Ross 		DPRINT("short reply");
215613a2f6bSGordon Ross 		return (0);
216613a2f6bSGordon Ross 	}
217613a2f6bSGordon Ross 
218613a2f6bSGordon Ross 	sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
219613a2f6bSGordon Ross 	rseqno = rqp->rq_seqno + 1;
220613a2f6bSGordon Ross 
221613a2f6bSGordon Ross 	DPRINT("rq_rseqno = 0x%x", rseqno);
222613a2f6bSGordon Ross 
223613a2f6bSGordon Ross 	err = smb_compute_MAC(ctx, m, rseqno, sigbuf);
224613a2f6bSGordon Ross 	if (err) {
225613a2f6bSGordon Ross 		DPRINT("compute MAC, err %d", err);
226613a2f6bSGordon Ross 		/*
227613a2f6bSGordon Ross 		 * If we can't compute a MAC, then there's
228613a2f6bSGordon Ross 		 * no point trying other seqno values.
229613a2f6bSGordon Ross 		 */
230613a2f6bSGordon Ross 		return (EBADRPC);
231613a2f6bSGordon Ross 	}
232613a2f6bSGordon Ross 
233613a2f6bSGordon Ross 	/*
234613a2f6bSGordon Ross 	 * Compare the computed signature with the
235613a2f6bSGordon Ross 	 * one found in the message (at sigloc)
236613a2f6bSGordon Ross 	 */
237613a2f6bSGordon Ross 	if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
238613a2f6bSGordon Ross 		return (0);
239613a2f6bSGordon Ross 
240613a2f6bSGordon Ross 	DPRINT("BAD signature, MID=0x%x", rqp->rq_mid);
241613a2f6bSGordon Ross 
242613a2f6bSGordon Ross #ifdef DEBUG
243613a2f6bSGordon Ross 	/*
244613a2f6bSGordon Ross 	 * For diag purposes, we check whether the client/server idea
245613a2f6bSGordon Ross 	 * of the sequence # has gotten a bit out of sync.
246613a2f6bSGordon Ross 	 */
247613a2f6bSGordon Ross 	for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
248*02d09e03SGordon Ross 		(void) smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf);
249613a2f6bSGordon Ross 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
250613a2f6bSGordon Ross 			break;
251*02d09e03SGordon Ross 		(void) smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf);
252613a2f6bSGordon Ross 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
253613a2f6bSGordon Ross 			fudge = -fudge;
254613a2f6bSGordon Ross 			break;
255613a2f6bSGordon Ross 		}
256613a2f6bSGordon Ross 	}
257613a2f6bSGordon Ross 	if (fudge <= nsmb_signing_fudge) {
258613a2f6bSGordon Ross 		DPRINT("rseqno=%d, but %d would have worked",
259613a2f6bSGordon Ross 		    rseqno, rseqno + fudge);
260613a2f6bSGordon Ross 	}
261613a2f6bSGordon Ross #endif
262613a2f6bSGordon Ross 	return (EBADRPC);
263613a2f6bSGordon Ross }
264