xref: /titanic_41/usr/src/lib/libsmbfs/smb/signing.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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
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 int
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 (EIO);
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 (0);
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 		return (ENOTSUP);
182 	}
183 	return (0);
184 }
185 
186 /*
187  * Verify reply signature.
188  */
189 int
190 smb_rq_verify(struct smb_rq *rqp)
191 {
192 	struct smb_ctx *ctx = rqp->rq_ctx;
193 	mbuf_t *m = rqp->rq_rp.mb_top;
194 	uint8_t sigbuf[SMBSIGLEN];
195 	uint8_t *sigloc;
196 	uint32_t rseqno;
197 	int err, fudge;
198 
199 	/*
200 	 * Note ct_mackey and ct_mackeylen gets initialized by
201 	 * smb_smb_ssnsetup.  It's normal to have a null MAC key
202 	 * during extended security session setup.
203 	 */
204 	if (ctx->ct_mackey == NULL)
205 		return (0);
206 
207 	/*
208 	 * Let caller deal with empty reply or short messages by
209 	 * returning zero.  Caller will fail later, in parsing.
210 	 */
211 	if (m == NULL) {
212 		DPRINT("empty reply");
213 		return (0);
214 	}
215 	if (m->m_len < SMB_HDRLEN) {
216 		DPRINT("short reply");
217 		return (0);
218 	}
219 
220 	sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
221 	rseqno = rqp->rq_seqno + 1;
222 
223 	DPRINT("rq_rseqno = 0x%x", rseqno);
224 
225 	err = smb_compute_MAC(ctx, m, rseqno, sigbuf);
226 	if (err) {
227 		DPRINT("compute MAC, err %d", err);
228 		/*
229 		 * If we can't compute a MAC, then there's
230 		 * no point trying other seqno values.
231 		 */
232 		return (EBADRPC);
233 	}
234 
235 	/*
236 	 * Compare the computed signature with the
237 	 * one found in the message (at sigloc)
238 	 */
239 	if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
240 		return (0);
241 
242 	DPRINT("BAD signature, MID=0x%x", rqp->rq_mid);
243 
244 #ifdef DEBUG
245 	/*
246 	 * For diag purposes, we check whether the client/server idea
247 	 * of the sequence # has gotten a bit out of sync.
248 	 */
249 	for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
250 		smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf);
251 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
252 			break;
253 		smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf);
254 		if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
255 			fudge = -fudge;
256 			break;
257 		}
258 	}
259 	if (fudge <= nsmb_signing_fudge) {
260 		DPRINT("rseqno=%d, but %d would have worked",
261 		    rseqno, rseqno + fudge);
262 	}
263 #endif
264 	return (EBADRPC);
265 }
266