1613a2f6bSGordon Ross /* 2613a2f6bSGordon Ross * CDDL HEADER START 3613a2f6bSGordon Ross * 4613a2f6bSGordon Ross * The contents of this file are subject to the terms of the 5613a2f6bSGordon Ross * Common Development and Distribution License (the "License"). 6613a2f6bSGordon Ross * You may not use this file except in compliance with the License. 7613a2f6bSGordon Ross * 8613a2f6bSGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9613a2f6bSGordon Ross * or http://www.opensolaris.org/os/licensing. 10613a2f6bSGordon Ross * See the License for the specific language governing permissions 11613a2f6bSGordon Ross * and limitations under the License. 12613a2f6bSGordon Ross * 13613a2f6bSGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14613a2f6bSGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15613a2f6bSGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16613a2f6bSGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17613a2f6bSGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18613a2f6bSGordon Ross * 19613a2f6bSGordon Ross * CDDL HEADER END 20613a2f6bSGordon Ross */ 21613a2f6bSGordon Ross 22613a2f6bSGordon Ross /* 23613a2f6bSGordon Ross * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24613a2f6bSGordon Ross * Use is subject to license terms. 25613a2f6bSGordon Ross */ 26613a2f6bSGordon Ross 27613a2f6bSGordon Ross /* 28613a2f6bSGordon Ross * Signing support, using libmd 29613a2f6bSGordon Ross */ 30613a2f6bSGordon Ross 31613a2f6bSGordon Ross #include <errno.h> 32613a2f6bSGordon Ross #include <stdio.h> 33613a2f6bSGordon Ross #include <stdlib.h> 34613a2f6bSGordon Ross #include <unistd.h> 35613a2f6bSGordon Ross #include <strings.h> 36613a2f6bSGordon Ross 37613a2f6bSGordon Ross #include <sys/types.h> 38613a2f6bSGordon Ross #include <sys/md5.h> 39613a2f6bSGordon Ross 40613a2f6bSGordon Ross #include <netsmb/mchain.h> 41613a2f6bSGordon Ross #include <netsmb/smb.h> 42613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 43613a2f6bSGordon Ross 44613a2f6bSGordon Ross #include "private.h" 45613a2f6bSGordon Ross 46613a2f6bSGordon Ross #define SMBSIGOFF 14 /* SMB signature offset */ 47613a2f6bSGordon Ross #define SMBSIGLEN 8 /* SMB signature length */ 48613a2f6bSGordon Ross 49613a2f6bSGordon Ross /* 50613a2f6bSGordon Ross * Set this to a small number to debug sequence numbers 51613a2f6bSGordon Ross * that seem to get out of step. 52613a2f6bSGordon Ross */ 53613a2f6bSGordon Ross #ifdef DEBUG 54613a2f6bSGordon Ross int nsmb_signing_fudge = 4; 55613a2f6bSGordon Ross #endif 56613a2f6bSGordon Ross 57613a2f6bSGordon Ross /* 58613a2f6bSGordon Ross * Compute MD5 digest of packet data, using the stored MAC key. 59613a2f6bSGordon Ross * 60613a2f6bSGordon Ross * See similar code in the driver: 61613a2f6bSGordon Ross * uts/common/fs/smbclnt/netsmb/smb_signing.c 62613a2f6bSGordon Ross * and on the server side: 63613a2f6bSGordon Ross * uts/common/fs/smbsrv/smb_signing.c 64613a2f6bSGordon Ross */ 65613a2f6bSGordon Ross static int 66613a2f6bSGordon Ross smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m, 67613a2f6bSGordon Ross uint32_t seqno, uchar_t *signature) 68613a2f6bSGordon Ross { 69613a2f6bSGordon Ross MD5_CTX md5; 70613a2f6bSGordon Ross uchar_t digest[MD5_DIGEST_LENGTH]; 71613a2f6bSGordon Ross 72613a2f6bSGordon Ross /* 73613a2f6bSGordon Ross * This union is a little bit of trickery to: 74613a2f6bSGordon Ross * (1) get the sequence number int aligned, and 75613a2f6bSGordon Ross * (2) reduce the number of digest calls, at the 76613a2f6bSGordon Ross * cost of a copying 32 bytes instead of 8. 77613a2f6bSGordon Ross * Both sides of this union are 2+32 bytes. 78613a2f6bSGordon Ross */ 79613a2f6bSGordon Ross union { 80613a2f6bSGordon Ross struct { 81613a2f6bSGordon Ross uint8_t skip[2]; /* not used - just alignment */ 82613a2f6bSGordon Ross uint8_t raw[SMB_HDRLEN]; /* header length (32) */ 83613a2f6bSGordon Ross } r; 84613a2f6bSGordon Ross struct { 85613a2f6bSGordon Ross uint8_t skip[2]; /* not used - just alignment */ 86613a2f6bSGordon Ross uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */ 87613a2f6bSGordon Ross uint32_t sig[2]; /* MAC signature, aligned! */ 88613a2f6bSGordon Ross uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */ 89613a2f6bSGordon Ross } s; 90613a2f6bSGordon Ross } smbhdr; 91613a2f6bSGordon Ross 92613a2f6bSGordon Ross if (m->m_len < SMB_HDRLEN) 93613a2f6bSGordon Ross return (EIO); 94613a2f6bSGordon Ross if (ctx->ct_mackey == NULL) 95613a2f6bSGordon Ross return (EINVAL); 96613a2f6bSGordon Ross 97613a2f6bSGordon Ross /* 98613a2f6bSGordon Ross * Make an aligned copy of the SMB header 99613a2f6bSGordon Ross * and fill in the sequence number. 100613a2f6bSGordon Ross */ 101613a2f6bSGordon Ross bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN); 102613a2f6bSGordon Ross smbhdr.s.sig[0] = htolel(seqno); 103613a2f6bSGordon Ross smbhdr.s.sig[1] = 0; 104613a2f6bSGordon Ross 105613a2f6bSGordon Ross /* 106613a2f6bSGordon Ross * Compute the MAC: MD5(concat(Key, message)) 107613a2f6bSGordon Ross */ 108613a2f6bSGordon Ross MD5Init(&md5); 109613a2f6bSGordon Ross 110613a2f6bSGordon Ross /* Digest the MAC Key */ 111613a2f6bSGordon Ross MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen); 112613a2f6bSGordon Ross 113613a2f6bSGordon Ross /* Digest the (copied) SMB header */ 114613a2f6bSGordon Ross MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN); 115613a2f6bSGordon Ross 116613a2f6bSGordon Ross /* Digest the rest of the first mbuf */ 117613a2f6bSGordon Ross if (m->m_len > SMB_HDRLEN) { 118613a2f6bSGordon Ross MD5Update(&md5, m->m_data + SMB_HDRLEN, 119613a2f6bSGordon Ross m->m_len - SMB_HDRLEN); 120613a2f6bSGordon Ross } 121613a2f6bSGordon Ross m = m->m_next; 122613a2f6bSGordon Ross 123613a2f6bSGordon Ross /* Digest rest of the SMB message. */ 124613a2f6bSGordon Ross while (m) { 125613a2f6bSGordon Ross MD5Update(&md5, m->m_data, m->m_len); 126613a2f6bSGordon Ross m = m->m_next; 127613a2f6bSGordon Ross } 128613a2f6bSGordon Ross 129613a2f6bSGordon Ross /* Final */ 130613a2f6bSGordon Ross MD5Final(digest, &md5); 131613a2f6bSGordon Ross 132613a2f6bSGordon Ross /* 133613a2f6bSGordon Ross * Finally, store the signature. 134613a2f6bSGordon Ross * (first 8 bytes of the digest) 135613a2f6bSGordon Ross */ 136613a2f6bSGordon Ross if (signature) 137613a2f6bSGordon Ross bcopy(digest, signature, SMBSIGLEN); 138613a2f6bSGordon Ross 139613a2f6bSGordon Ross return (0); 140613a2f6bSGordon Ross } 141613a2f6bSGordon Ross 142613a2f6bSGordon Ross /* 143613a2f6bSGordon Ross * Sign a request with HMAC-MD5. 144613a2f6bSGordon Ross */ 145*02d09e03SGordon Ross void 146613a2f6bSGordon Ross smb_rq_sign(struct smb_rq *rqp) 147613a2f6bSGordon Ross { 148613a2f6bSGordon Ross struct smb_ctx *ctx = rqp->rq_ctx; 149613a2f6bSGordon Ross mbuf_t *m = rqp->rq_rq.mb_top; 150613a2f6bSGordon Ross uint8_t *sigloc; 151613a2f6bSGordon Ross int err; 152613a2f6bSGordon Ross 153613a2f6bSGordon Ross /* 154613a2f6bSGordon Ross * Our mblk allocation ensures this, 155613a2f6bSGordon Ross * but just in case... 156613a2f6bSGordon Ross */ 157613a2f6bSGordon Ross if (m->m_len < SMB_HDRLEN) 158*02d09e03SGordon Ross return; 159613a2f6bSGordon Ross sigloc = (uchar_t *)m->m_data + SMBSIGOFF; 160613a2f6bSGordon Ross 161613a2f6bSGordon Ross if (ctx->ct_mackey == NULL) { 162613a2f6bSGordon Ross /* 163613a2f6bSGordon Ross * Signing is required, but we have no key yet 164613a2f6bSGordon Ross * fill in with the magic fake signing value. 165613a2f6bSGordon Ross * This happens with SPNEGO, NTLMSSP, ... 166613a2f6bSGordon Ross */ 167613a2f6bSGordon Ross bcopy("BSRSPLY", sigloc, 8); 168*02d09e03SGordon Ross return; 169613a2f6bSGordon Ross } 170613a2f6bSGordon Ross 171613a2f6bSGordon Ross /* 172613a2f6bSGordon Ross * This will compute the MAC and store it 173613a2f6bSGordon Ross * directly into the message at sigloc. 174613a2f6bSGordon Ross */ 175613a2f6bSGordon Ross rqp->rq_seqno = ctx->ct_mac_seqno; 176613a2f6bSGordon Ross ctx->ct_mac_seqno += 2; 177613a2f6bSGordon Ross err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc); 178613a2f6bSGordon Ross if (err) { 179613a2f6bSGordon Ross DPRINT("compute MAC, err %d", err); 180613a2f6bSGordon Ross bzero(sigloc, SMBSIGLEN); 181613a2f6bSGordon Ross } 182613a2f6bSGordon Ross } 183613a2f6bSGordon Ross 184613a2f6bSGordon Ross /* 185613a2f6bSGordon Ross * Verify reply signature. 186613a2f6bSGordon Ross */ 187613a2f6bSGordon Ross int 188613a2f6bSGordon Ross smb_rq_verify(struct smb_rq *rqp) 189613a2f6bSGordon Ross { 190613a2f6bSGordon Ross struct smb_ctx *ctx = rqp->rq_ctx; 191613a2f6bSGordon Ross mbuf_t *m = rqp->rq_rp.mb_top; 192613a2f6bSGordon Ross uint8_t sigbuf[SMBSIGLEN]; 193613a2f6bSGordon Ross uint8_t *sigloc; 194613a2f6bSGordon Ross uint32_t rseqno; 195613a2f6bSGordon Ross int err, fudge; 196613a2f6bSGordon Ross 197613a2f6bSGordon Ross /* 198613a2f6bSGordon Ross * Note ct_mackey and ct_mackeylen gets initialized by 199613a2f6bSGordon Ross * smb_smb_ssnsetup. It's normal to have a null MAC key 200613a2f6bSGordon Ross * during extended security session setup. 201613a2f6bSGordon Ross */ 202613a2f6bSGordon Ross if (ctx->ct_mackey == NULL) 203613a2f6bSGordon Ross return (0); 204613a2f6bSGordon Ross 205613a2f6bSGordon Ross /* 206613a2f6bSGordon Ross * Let caller deal with empty reply or short messages by 207613a2f6bSGordon Ross * returning zero. Caller will fail later, in parsing. 208613a2f6bSGordon Ross */ 209613a2f6bSGordon Ross if (m == NULL) { 210613a2f6bSGordon Ross DPRINT("empty reply"); 211613a2f6bSGordon Ross return (0); 212613a2f6bSGordon Ross } 213613a2f6bSGordon Ross if (m->m_len < SMB_HDRLEN) { 214613a2f6bSGordon Ross DPRINT("short reply"); 215613a2f6bSGordon Ross return (0); 216613a2f6bSGordon Ross } 217613a2f6bSGordon Ross 218613a2f6bSGordon Ross sigloc = (uchar_t *)m->m_data + SMBSIGOFF; 219613a2f6bSGordon Ross rseqno = rqp->rq_seqno + 1; 220613a2f6bSGordon Ross 221613a2f6bSGordon Ross DPRINT("rq_rseqno = 0x%x", rseqno); 222613a2f6bSGordon Ross 223613a2f6bSGordon Ross err = smb_compute_MAC(ctx, m, rseqno, sigbuf); 224613a2f6bSGordon Ross if (err) { 225613a2f6bSGordon Ross DPRINT("compute MAC, err %d", err); 226613a2f6bSGordon Ross /* 227613a2f6bSGordon Ross * If we can't compute a MAC, then there's 228613a2f6bSGordon Ross * no point trying other seqno values. 229613a2f6bSGordon Ross */ 230613a2f6bSGordon Ross return (EBADRPC); 231613a2f6bSGordon Ross } 232613a2f6bSGordon Ross 233613a2f6bSGordon Ross /* 234613a2f6bSGordon Ross * Compare the computed signature with the 235613a2f6bSGordon Ross * one found in the message (at sigloc) 236613a2f6bSGordon Ross */ 237613a2f6bSGordon Ross if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 238613a2f6bSGordon Ross return (0); 239613a2f6bSGordon Ross 240613a2f6bSGordon Ross DPRINT("BAD signature, MID=0x%x", rqp->rq_mid); 241613a2f6bSGordon Ross 242613a2f6bSGordon Ross #ifdef DEBUG 243613a2f6bSGordon Ross /* 244613a2f6bSGordon Ross * For diag purposes, we check whether the client/server idea 245613a2f6bSGordon Ross * of the sequence # has gotten a bit out of sync. 246613a2f6bSGordon Ross */ 247613a2f6bSGordon Ross for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) { 248*02d09e03SGordon Ross (void) smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf); 249613a2f6bSGordon Ross if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 250613a2f6bSGordon Ross break; 251*02d09e03SGordon Ross (void) smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf); 252613a2f6bSGordon Ross if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { 253613a2f6bSGordon Ross fudge = -fudge; 254613a2f6bSGordon Ross break; 255613a2f6bSGordon Ross } 256613a2f6bSGordon Ross } 257613a2f6bSGordon Ross if (fudge <= nsmb_signing_fudge) { 258613a2f6bSGordon Ross DPRINT("rseqno=%d, but %d would have worked", 259613a2f6bSGordon Ross rseqno, rseqno + fudge); 260613a2f6bSGordon Ross } 261613a2f6bSGordon Ross #endif 262613a2f6bSGordon Ross return (EBADRPC); 263613a2f6bSGordon Ross } 264