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 * Support for SMB "signing" (message integrity) 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/conf.h> 34 #include <sys/proc.h> 35 #include <sys/fcntl.h> 36 #include <sys/socket.h> 37 #include <sys/md4.h> 38 #include <sys/md5.h> 39 #include <sys/des.h> 40 #include <sys/kmem.h> 41 #include <sys/crypto/api.h> 42 #include <sys/crypto/common.h> 43 #include <sys/cmn_err.h> 44 #include <sys/stream.h> 45 #include <sys/strsun.h> 46 #include <sys/sdt.h> 47 48 #include <netsmb/smb_osdep.h> 49 #include <netsmb/smb.h> 50 #include <netsmb/smb_conn.h> 51 #include <netsmb/smb_subr.h> 52 #include <netsmb/smb_dev.h> 53 #include <netsmb/smb_rq.h> 54 55 #ifdef DEBUG 56 /* 57 * Set this to a small number to debug sequence numbers 58 * that seem to get out of step. 59 */ 60 int nsmb_signing_fudge = 0; 61 #endif 62 63 /* Mechanism definitions */ 64 static crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID }; 65 66 void 67 smb_crypto_mech_init(void) 68 { 69 crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5); 70 } 71 72 73 74 #define SMBSIGLEN 8 /* SMB signature length */ 75 #define SMBSIGOFF 14 /* SMB signature offset */ 76 77 /* 78 * Compute HMAC-MD5 of packet data, using the stored MAC key. 79 * 80 * See similar code for the server side: 81 * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc 82 */ 83 static int 84 smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, 85 uint32_t seqno, uchar_t *signature) 86 { 87 crypto_context_t crypto_ctx; 88 crypto_data_t key; 89 crypto_data_t data; 90 crypto_data_t digest; 91 uchar_t mac[16]; 92 int status; 93 /* 94 * This union is a little bit of trickery to: 95 * (1) get the sequence number int aligned, and 96 * (2) reduce the number of digest calls, at the 97 * cost of a copying 32 bytes instead of 8. 98 * Both sides of this union are 2+32 bytes. 99 */ 100 union { 101 struct { 102 uint8_t skip[2]; /* not used - just alignment */ 103 uint8_t raw[SMB_HDRLEN]; /* header length (32) */ 104 } r; 105 struct { 106 uint8_t skip[2]; /* not used - just alignment */ 107 uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */ 108 uint32_t sig[2]; /* MAC signature, aligned! */ 109 uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */ 110 } s; 111 } smbhdr; 112 113 ASSERT(mp != NULL); 114 ASSERT(MBLKL(mp) >= SMB_HDRLEN); 115 ASSERT(vcp->vc_mackey != NULL); 116 117 /* 118 * Make an aligned copy of the SMB header 119 * and fill in the sequence number. 120 */ 121 bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN); 122 smbhdr.s.sig[0] = htolel(seqno); 123 smbhdr.s.sig[1] = 0; 124 125 /* 126 * Compute the MAC: MD5(concat(Key, message)) 127 */ 128 if (crypto_mech_md5.cm_type == CRYPTO_MECH_INVALID) { 129 SMBSDEBUG("crypto_mech_md5 invalid\n"); 130 return (CRYPTO_MECHANISM_INVALID); 131 } 132 status = crypto_digest_init(&crypto_mech_md5, &crypto_ctx, 0); 133 if (status != CRYPTO_SUCCESS) 134 return (status); 135 136 /* Digest the MAC Key */ 137 key.cd_format = CRYPTO_DATA_RAW; 138 key.cd_offset = 0; 139 key.cd_length = vcp->vc_mackeylen; 140 key.cd_miscdata = 0; 141 key.cd_raw.iov_base = (char *)vcp->vc_mackey; 142 key.cd_raw.iov_len = vcp->vc_mackeylen; 143 status = crypto_digest_update(crypto_ctx, &key, 0); 144 if (status != CRYPTO_SUCCESS) 145 return (status); 146 147 /* Digest the (copied) SMB header */ 148 data.cd_format = CRYPTO_DATA_RAW; 149 data.cd_offset = 0; 150 data.cd_length = SMB_HDRLEN; 151 data.cd_miscdata = 0; 152 data.cd_raw.iov_base = (char *)smbhdr.r.raw; 153 data.cd_raw.iov_len = SMB_HDRLEN; 154 status = crypto_digest_update(crypto_ctx, &data, 0); 155 if (status != CRYPTO_SUCCESS) 156 return (status); 157 158 /* Digest rest of the SMB message. */ 159 data.cd_format = CRYPTO_DATA_MBLK; 160 data.cd_offset = SMB_HDRLEN; 161 data.cd_length = msgdsize(mp) - SMB_HDRLEN; 162 data.cd_miscdata = 0; 163 data.cd_mp = mp; 164 status = crypto_digest_update(crypto_ctx, &data, 0); 165 if (status != CRYPTO_SUCCESS) 166 return (status); 167 168 /* Final */ 169 digest.cd_format = CRYPTO_DATA_RAW; 170 digest.cd_offset = 0; 171 digest.cd_length = sizeof (mac); 172 digest.cd_miscdata = 0; 173 digest.cd_raw.iov_base = (char *)mac; 174 digest.cd_raw.iov_len = sizeof (mac); 175 status = crypto_digest_final(crypto_ctx, &digest, 0); 176 if (status != CRYPTO_SUCCESS) 177 return (status); 178 179 /* 180 * Finally, store the signature. 181 * (first 8 bytes of the mac) 182 */ 183 if (signature) 184 bcopy(mac, signature, SMBSIGLEN); 185 186 return (0); 187 } 188 189 /* 190 * Sign a request with HMAC-MD5. 191 */ 192 int 193 smb_rq_sign(struct smb_rq *rqp) 194 { 195 struct smb_vc *vcp = rqp->sr_vc; 196 mblk_t *mp = rqp->sr_rq.mb_top; 197 uint8_t *sigloc; 198 int status; 199 200 /* 201 * Our mblk allocation ensures this, 202 * but just in case... 203 */ 204 if (MBLKL(mp) < SMB_HDRLEN) { 205 if (!pullupmsg(mp, SMB_HDRLEN)) 206 return (0); 207 } 208 sigloc = mp->b_rptr + SMBSIGOFF; 209 210 if (vcp->vc_mackey == NULL) { 211 /* 212 * Signing is required, but we have no key yet 213 * fill in with the magic fake signing value. 214 * This happens with SPNEGO, NTLMSSP, ... 215 */ 216 bcopy("BSRSPLY", sigloc, 8); 217 return (0); 218 } 219 220 /* 221 * This will compute the MAC and store it 222 * directly into the message at sigloc. 223 */ 224 status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc); 225 if (status != CRYPTO_SUCCESS) { 226 SMBSDEBUG("Crypto error %d", status); 227 bzero(sigloc, SMBSIGLEN); 228 return (ENOTSUP); 229 } 230 return (0); 231 } 232 233 /* 234 * Verify reply signature. 235 */ 236 int 237 smb_rq_verify(struct smb_rq *rqp) 238 { 239 struct smb_vc *vcp = rqp->sr_vc; 240 mblk_t *mp = rqp->sr_rp.md_top; 241 uint8_t sigbuf[SMBSIGLEN]; 242 uint8_t *sigloc; 243 int status; 244 int fudge; 245 246 /* 247 * Note vc_mackey and vc_mackeylen gets filled in by 248 * smb_usr_iod_work as the connection comes in. 249 */ 250 if (vcp->vc_mackey == NULL) { 251 SMBSDEBUG("no mac key\n"); 252 return (0); 253 } 254 255 /* 256 * Let caller deal with empty reply or short messages by 257 * returning zero. Caller will fail later, in parsing. 258 */ 259 if (mp == NULL) { 260 SMBSDEBUG("empty reply\n"); 261 return (0); 262 } 263 if (MBLKL(mp) < SMB_HDRLEN) { 264 if (!pullupmsg(mp, SMB_HDRLEN)) 265 return (0); 266 } 267 sigloc = mp->b_rptr + SMBSIGOFF; 268 269 SMBSDEBUG("sr_rseqno = 0x%x\n", rqp->sr_rseqno); 270 271 status = smb_compute_MAC(vcp, mp, rqp->sr_rseqno, sigbuf); 272 if (status != CRYPTO_SUCCESS) { 273 SMBSDEBUG("Crypto error %d", status); 274 /* 275 * If we can't compute a MAC, then there's 276 * no point trying other seqno values. 277 */ 278 return (EBADRPC); 279 } 280 281 /* 282 * Compare the computed signature with the 283 * one found in the message (at sigloc) 284 */ 285 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 286 return (0); 287 288 SMBSDEBUG("BAD signature, MID=0x%x\n", rqp->sr_mid); 289 290 #ifdef DEBUG 291 /* 292 * For diag purposes, we check whether the client/server idea 293 * of the sequence # has gotten a bit out of sync. 294 */ 295 for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) { 296 smb_compute_MAC(vcp, mp, rqp->sr_rseqno + fudge, sigbuf); 297 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 298 break; 299 smb_compute_MAC(vcp, mp, rqp->sr_rseqno - fudge, sigbuf); 300 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { 301 fudge = -fudge; 302 break; 303 } 304 } 305 if (fudge <= nsmb_signing_fudge) { 306 SMBSDEBUG("sr_rseqno=%d, but %d would have worked\n", 307 rqp->sr_rseqno, rqp->sr_rseqno + fudge); 308 } 309 #endif 310 return (EBADRPC); 311 } 312