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