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 * Copyright 2017 Nexenta Systems, Inc. All rights reserved. 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/cmn_err.h> 42 #include <sys/stream.h> 43 #include <sys/strsun.h> 44 #include <sys/sdt.h> 45 46 #include <netsmb/smb_osdep.h> 47 #include <netsmb/smb.h> 48 #include <netsmb/smb_conn.h> 49 #include <netsmb/smb_subr.h> 50 #include <netsmb/smb_dev.h> 51 #include <netsmb/smb_rq.h> 52 #include <netsmb/smb_signing.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 smb_sign_mech_t smb_mech_md5; 64 65 void 66 smb_crypto_mech_init(void) 67 { 68 if (smb_md5_getmech(&smb_mech_md5) != 0) 69 cmn_err(CE_NOTE, "nsmb can't get md5 mech"); 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 uchar_t digest[MD5_DIGEST_LENGTH]; 87 smb_sign_ctx_t ctx = 0; 88 mblk_t *m = mp; 89 int size; 90 int rc; 91 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 /* Later: check vcp->sign_mech == NULL */ 113 if (vcp->vc_mackey == NULL) 114 return (-1); 115 116 if ((rc = smb_md5_init(&ctx, &smb_mech_md5)) != 0) 117 return (rc); 118 119 /* Digest the MAC Key */ 120 rc = smb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen); 121 if (rc != 0) 122 return (rc); 123 124 ASSERT(m != NULL); 125 ASSERT(MBLKL(m) >= SMB_HDRLEN); 126 127 /* 128 * Make an aligned copy of the SMB header, 129 * fill in the sequence number, and digest. 130 */ 131 size = SMB_HDRLEN; 132 if (MBLKL(m) < size) 133 (void) pullupmsg(m, size); 134 bcopy(m->b_rptr, smbhdr.r.raw, size); 135 smbhdr.s.sig[0] = htolel(seqno); 136 smbhdr.s.sig[1] = 0; 137 138 rc = smb_md5_update(ctx, &smbhdr.r.raw, size); 139 if (rc != 0) 140 return (rc); 141 142 /* 143 * Digest the rest of the SMB header packet, starting at 144 * the data just after the SMB header. 145 */ 146 size = MBLKL(m) - SMB_HDRLEN; 147 rc = smb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size); 148 if (rc != 0) 149 return (rc); 150 m = m->b_cont; 151 152 /* Digest rest of the SMB message. */ 153 while (m != NULL) { 154 size = MBLKL(m); 155 if (size > 0) { 156 rc = smb_md5_update(ctx, m->b_rptr, size); 157 if (rc != 0) 158 return (rc); 159 } 160 m = m->b_cont; 161 } 162 rc = smb_md5_final(ctx, digest); 163 if (rc != 0) 164 return (rc); 165 166 /* 167 * Finally, store the signature. 168 * (first 8 bytes of the mac) 169 */ 170 if (signature) 171 bcopy(digest, signature, SMBSIGLEN); 172 173 return (0); 174 } 175 176 /* 177 * Sign a request with HMAC-MD5. 178 */ 179 void 180 smb_rq_sign(struct smb_rq *rqp) 181 { 182 struct smb_vc *vcp = rqp->sr_vc; 183 mblk_t *mp = rqp->sr_rq.mb_top; 184 uint8_t *sigloc; 185 int status; 186 187 /* 188 * Our mblk allocation ensures this, 189 * but just in case... 190 */ 191 if (MBLKL(mp) < SMB_HDRLEN) { 192 if (!pullupmsg(mp, SMB_HDRLEN)) 193 return; 194 } 195 sigloc = mp->b_rptr + SMBSIGOFF; 196 197 if (vcp->vc_mackey == NULL) { 198 /* 199 * Signing is required, but we have no key yet 200 * fill in with the magic fake signing value. 201 * This happens with SPNEGO, NTLMSSP, ... 202 */ 203 bcopy("BSRSPLY", sigloc, 8); 204 return; 205 } 206 207 /* 208 * This will compute the MAC and store it 209 * directly into the message at sigloc. 210 */ 211 status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc); 212 if (status != 0) { 213 SMBSDEBUG("Crypto error %d", status); 214 bzero(sigloc, SMBSIGLEN); 215 } 216 } 217 218 /* 219 * Verify reply signature. 220 */ 221 int 222 smb_rq_verify(struct smb_rq *rqp) 223 { 224 struct smb_vc *vcp = rqp->sr_vc; 225 mblk_t *mp = rqp->sr_rp.md_top; 226 uint8_t sigbuf[SMBSIGLEN]; 227 uint8_t *sigloc; 228 int fudge, rsn, status; 229 230 /* 231 * Note vc_mackey and vc_mackeylen gets filled in by 232 * smb_usr_iod_work as the connection comes in. 233 */ 234 if (vcp->vc_mackey == NULL) { 235 SMBSDEBUG("no mac key\n"); 236 return (0); 237 } 238 239 /* 240 * Let caller deal with empty reply or short messages by 241 * returning zero. Caller will fail later, in parsing. 242 */ 243 if (mp == NULL) { 244 SMBSDEBUG("empty reply\n"); 245 return (0); 246 } 247 if (MBLKL(mp) < SMB_HDRLEN) { 248 if (!pullupmsg(mp, SMB_HDRLEN)) 249 return (0); 250 } 251 sigloc = mp->b_rptr + SMBSIGOFF; 252 253 /* 254 * Compute the expected signature in sigbuf. 255 */ 256 rsn = rqp->sr_rseqno; 257 status = smb_compute_MAC(vcp, mp, rsn, sigbuf); 258 if (status != 0) { 259 SMBSDEBUG("Crypto error %d", status); 260 /* 261 * If we can't compute a MAC, then there's 262 * no point trying other seqno values. 263 */ 264 return (EBADRPC); 265 } 266 267 /* 268 * Compare the computed signature with the 269 * one found in the message (at sigloc) 270 */ 271 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 272 return (0); 273 274 SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n", 275 vcp->vc_srvname, rqp->sr_mid, rsn); 276 277 #ifdef DEBUG 278 /* 279 * For diag purposes, we check whether the client/server idea 280 * of the sequence # has gotten a bit out of sync. 281 */ 282 for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) { 283 (void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf); 284 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 285 break; 286 (void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf); 287 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { 288 fudge = -fudge; 289 break; 290 } 291 } 292 if (fudge <= nsmb_signing_fudge) { 293 SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n", 294 rqp->sr_mid, rsn, rsn + fudge); 295 } 296 #endif 297 return (EBADRPC); 298 } 299