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