1*10023SGordon.Ross@Sun.COM /* 2*10023SGordon.Ross@Sun.COM * CDDL HEADER START 3*10023SGordon.Ross@Sun.COM * 4*10023SGordon.Ross@Sun.COM * The contents of this file are subject to the terms of the 5*10023SGordon.Ross@Sun.COM * Common Development and Distribution License (the "License"). 6*10023SGordon.Ross@Sun.COM * You may not use this file except in compliance with the License. 7*10023SGordon.Ross@Sun.COM * 8*10023SGordon.Ross@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*10023SGordon.Ross@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*10023SGordon.Ross@Sun.COM * See the License for the specific language governing permissions 11*10023SGordon.Ross@Sun.COM * and limitations under the License. 12*10023SGordon.Ross@Sun.COM * 13*10023SGordon.Ross@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*10023SGordon.Ross@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*10023SGordon.Ross@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*10023SGordon.Ross@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*10023SGordon.Ross@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*10023SGordon.Ross@Sun.COM * 19*10023SGordon.Ross@Sun.COM * CDDL HEADER END 20*10023SGordon.Ross@Sun.COM */ 21*10023SGordon.Ross@Sun.COM 22*10023SGordon.Ross@Sun.COM /* 23*10023SGordon.Ross@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*10023SGordon.Ross@Sun.COM * Use is subject to license terms. 25*10023SGordon.Ross@Sun.COM */ 26*10023SGordon.Ross@Sun.COM 27*10023SGordon.Ross@Sun.COM /* 28*10023SGordon.Ross@Sun.COM * Signing support, using libmd 29*10023SGordon.Ross@Sun.COM */ 30*10023SGordon.Ross@Sun.COM 31*10023SGordon.Ross@Sun.COM #include <errno.h> 32*10023SGordon.Ross@Sun.COM #include <stdio.h> 33*10023SGordon.Ross@Sun.COM #include <stdlib.h> 34*10023SGordon.Ross@Sun.COM #include <unistd.h> 35*10023SGordon.Ross@Sun.COM #include <strings.h> 36*10023SGordon.Ross@Sun.COM 37*10023SGordon.Ross@Sun.COM #include <sys/types.h> 38*10023SGordon.Ross@Sun.COM #include <sys/md5.h> 39*10023SGordon.Ross@Sun.COM 40*10023SGordon.Ross@Sun.COM #include <netsmb/mchain.h> 41*10023SGordon.Ross@Sun.COM #include <netsmb/smb.h> 42*10023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h> 43*10023SGordon.Ross@Sun.COM 44*10023SGordon.Ross@Sun.COM #include "private.h" 45*10023SGordon.Ross@Sun.COM 46*10023SGordon.Ross@Sun.COM #define SMBSIGOFF 14 /* SMB signature offset */ 47*10023SGordon.Ross@Sun.COM #define SMBSIGLEN 8 /* SMB signature length */ 48*10023SGordon.Ross@Sun.COM 49*10023SGordon.Ross@Sun.COM /* 50*10023SGordon.Ross@Sun.COM * Set this to a small number to debug sequence numbers 51*10023SGordon.Ross@Sun.COM * that seem to get out of step. 52*10023SGordon.Ross@Sun.COM */ 53*10023SGordon.Ross@Sun.COM #ifdef DEBUG 54*10023SGordon.Ross@Sun.COM int nsmb_signing_fudge = 4; 55*10023SGordon.Ross@Sun.COM #endif 56*10023SGordon.Ross@Sun.COM 57*10023SGordon.Ross@Sun.COM /* 58*10023SGordon.Ross@Sun.COM * Compute MD5 digest of packet data, using the stored MAC key. 59*10023SGordon.Ross@Sun.COM * 60*10023SGordon.Ross@Sun.COM * See similar code in the driver: 61*10023SGordon.Ross@Sun.COM * uts/common/fs/smbclnt/netsmb/smb_signing.c 62*10023SGordon.Ross@Sun.COM * and on the server side: 63*10023SGordon.Ross@Sun.COM * uts/common/fs/smbsrv/smb_signing.c 64*10023SGordon.Ross@Sun.COM */ 65*10023SGordon.Ross@Sun.COM static int 66*10023SGordon.Ross@Sun.COM smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m, 67*10023SGordon.Ross@Sun.COM uint32_t seqno, uchar_t *signature) 68*10023SGordon.Ross@Sun.COM { 69*10023SGordon.Ross@Sun.COM MD5_CTX md5; 70*10023SGordon.Ross@Sun.COM uchar_t digest[MD5_DIGEST_LENGTH]; 71*10023SGordon.Ross@Sun.COM 72*10023SGordon.Ross@Sun.COM /* 73*10023SGordon.Ross@Sun.COM * This union is a little bit of trickery to: 74*10023SGordon.Ross@Sun.COM * (1) get the sequence number int aligned, and 75*10023SGordon.Ross@Sun.COM * (2) reduce the number of digest calls, at the 76*10023SGordon.Ross@Sun.COM * cost of a copying 32 bytes instead of 8. 77*10023SGordon.Ross@Sun.COM * Both sides of this union are 2+32 bytes. 78*10023SGordon.Ross@Sun.COM */ 79*10023SGordon.Ross@Sun.COM union { 80*10023SGordon.Ross@Sun.COM struct { 81*10023SGordon.Ross@Sun.COM uint8_t skip[2]; /* not used - just alignment */ 82*10023SGordon.Ross@Sun.COM uint8_t raw[SMB_HDRLEN]; /* header length (32) */ 83*10023SGordon.Ross@Sun.COM } r; 84*10023SGordon.Ross@Sun.COM struct { 85*10023SGordon.Ross@Sun.COM uint8_t skip[2]; /* not used - just alignment */ 86*10023SGordon.Ross@Sun.COM uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */ 87*10023SGordon.Ross@Sun.COM uint32_t sig[2]; /* MAC signature, aligned! */ 88*10023SGordon.Ross@Sun.COM uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */ 89*10023SGordon.Ross@Sun.COM } s; 90*10023SGordon.Ross@Sun.COM } smbhdr; 91*10023SGordon.Ross@Sun.COM 92*10023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN) 93*10023SGordon.Ross@Sun.COM return (EIO); 94*10023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) 95*10023SGordon.Ross@Sun.COM return (EINVAL); 96*10023SGordon.Ross@Sun.COM 97*10023SGordon.Ross@Sun.COM /* 98*10023SGordon.Ross@Sun.COM * Make an aligned copy of the SMB header 99*10023SGordon.Ross@Sun.COM * and fill in the sequence number. 100*10023SGordon.Ross@Sun.COM */ 101*10023SGordon.Ross@Sun.COM bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN); 102*10023SGordon.Ross@Sun.COM smbhdr.s.sig[0] = htolel(seqno); 103*10023SGordon.Ross@Sun.COM smbhdr.s.sig[1] = 0; 104*10023SGordon.Ross@Sun.COM 105*10023SGordon.Ross@Sun.COM /* 106*10023SGordon.Ross@Sun.COM * Compute the MAC: MD5(concat(Key, message)) 107*10023SGordon.Ross@Sun.COM */ 108*10023SGordon.Ross@Sun.COM MD5Init(&md5); 109*10023SGordon.Ross@Sun.COM 110*10023SGordon.Ross@Sun.COM /* Digest the MAC Key */ 111*10023SGordon.Ross@Sun.COM MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen); 112*10023SGordon.Ross@Sun.COM 113*10023SGordon.Ross@Sun.COM /* Digest the (copied) SMB header */ 114*10023SGordon.Ross@Sun.COM MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN); 115*10023SGordon.Ross@Sun.COM 116*10023SGordon.Ross@Sun.COM /* Digest the rest of the first mbuf */ 117*10023SGordon.Ross@Sun.COM if (m->m_len > SMB_HDRLEN) { 118*10023SGordon.Ross@Sun.COM MD5Update(&md5, m->m_data + SMB_HDRLEN, 119*10023SGordon.Ross@Sun.COM m->m_len - SMB_HDRLEN); 120*10023SGordon.Ross@Sun.COM } 121*10023SGordon.Ross@Sun.COM m = m->m_next; 122*10023SGordon.Ross@Sun.COM 123*10023SGordon.Ross@Sun.COM /* Digest rest of the SMB message. */ 124*10023SGordon.Ross@Sun.COM while (m) { 125*10023SGordon.Ross@Sun.COM MD5Update(&md5, m->m_data, m->m_len); 126*10023SGordon.Ross@Sun.COM m = m->m_next; 127*10023SGordon.Ross@Sun.COM } 128*10023SGordon.Ross@Sun.COM 129*10023SGordon.Ross@Sun.COM /* Final */ 130*10023SGordon.Ross@Sun.COM MD5Final(digest, &md5); 131*10023SGordon.Ross@Sun.COM 132*10023SGordon.Ross@Sun.COM /* 133*10023SGordon.Ross@Sun.COM * Finally, store the signature. 134*10023SGordon.Ross@Sun.COM * (first 8 bytes of the digest) 135*10023SGordon.Ross@Sun.COM */ 136*10023SGordon.Ross@Sun.COM if (signature) 137*10023SGordon.Ross@Sun.COM bcopy(digest, signature, SMBSIGLEN); 138*10023SGordon.Ross@Sun.COM 139*10023SGordon.Ross@Sun.COM return (0); 140*10023SGordon.Ross@Sun.COM } 141*10023SGordon.Ross@Sun.COM 142*10023SGordon.Ross@Sun.COM /* 143*10023SGordon.Ross@Sun.COM * Sign a request with HMAC-MD5. 144*10023SGordon.Ross@Sun.COM */ 145*10023SGordon.Ross@Sun.COM int 146*10023SGordon.Ross@Sun.COM smb_rq_sign(struct smb_rq *rqp) 147*10023SGordon.Ross@Sun.COM { 148*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = rqp->rq_ctx; 149*10023SGordon.Ross@Sun.COM mbuf_t *m = rqp->rq_rq.mb_top; 150*10023SGordon.Ross@Sun.COM uint8_t *sigloc; 151*10023SGordon.Ross@Sun.COM int err; 152*10023SGordon.Ross@Sun.COM 153*10023SGordon.Ross@Sun.COM /* 154*10023SGordon.Ross@Sun.COM * Our mblk allocation ensures this, 155*10023SGordon.Ross@Sun.COM * but just in case... 156*10023SGordon.Ross@Sun.COM */ 157*10023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN) 158*10023SGordon.Ross@Sun.COM return (EIO); 159*10023SGordon.Ross@Sun.COM sigloc = (uchar_t *)m->m_data + SMBSIGOFF; 160*10023SGordon.Ross@Sun.COM 161*10023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) { 162*10023SGordon.Ross@Sun.COM /* 163*10023SGordon.Ross@Sun.COM * Signing is required, but we have no key yet 164*10023SGordon.Ross@Sun.COM * fill in with the magic fake signing value. 165*10023SGordon.Ross@Sun.COM * This happens with SPNEGO, NTLMSSP, ... 166*10023SGordon.Ross@Sun.COM */ 167*10023SGordon.Ross@Sun.COM bcopy("BSRSPLY", sigloc, 8); 168*10023SGordon.Ross@Sun.COM return (0); 169*10023SGordon.Ross@Sun.COM } 170*10023SGordon.Ross@Sun.COM 171*10023SGordon.Ross@Sun.COM /* 172*10023SGordon.Ross@Sun.COM * This will compute the MAC and store it 173*10023SGordon.Ross@Sun.COM * directly into the message at sigloc. 174*10023SGordon.Ross@Sun.COM */ 175*10023SGordon.Ross@Sun.COM rqp->rq_seqno = ctx->ct_mac_seqno; 176*10023SGordon.Ross@Sun.COM ctx->ct_mac_seqno += 2; 177*10023SGordon.Ross@Sun.COM err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc); 178*10023SGordon.Ross@Sun.COM if (err) { 179*10023SGordon.Ross@Sun.COM DPRINT("compute MAC, err %d", err); 180*10023SGordon.Ross@Sun.COM bzero(sigloc, SMBSIGLEN); 181*10023SGordon.Ross@Sun.COM return (ENOTSUP); 182*10023SGordon.Ross@Sun.COM } 183*10023SGordon.Ross@Sun.COM return (0); 184*10023SGordon.Ross@Sun.COM } 185*10023SGordon.Ross@Sun.COM 186*10023SGordon.Ross@Sun.COM /* 187*10023SGordon.Ross@Sun.COM * Verify reply signature. 188*10023SGordon.Ross@Sun.COM */ 189*10023SGordon.Ross@Sun.COM int 190*10023SGordon.Ross@Sun.COM smb_rq_verify(struct smb_rq *rqp) 191*10023SGordon.Ross@Sun.COM { 192*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = rqp->rq_ctx; 193*10023SGordon.Ross@Sun.COM mbuf_t *m = rqp->rq_rp.mb_top; 194*10023SGordon.Ross@Sun.COM uint8_t sigbuf[SMBSIGLEN]; 195*10023SGordon.Ross@Sun.COM uint8_t *sigloc; 196*10023SGordon.Ross@Sun.COM uint32_t rseqno; 197*10023SGordon.Ross@Sun.COM int err, fudge; 198*10023SGordon.Ross@Sun.COM 199*10023SGordon.Ross@Sun.COM /* 200*10023SGordon.Ross@Sun.COM * Note ct_mackey and ct_mackeylen gets initialized by 201*10023SGordon.Ross@Sun.COM * smb_smb_ssnsetup. It's normal to have a null MAC key 202*10023SGordon.Ross@Sun.COM * during extended security session setup. 203*10023SGordon.Ross@Sun.COM */ 204*10023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) 205*10023SGordon.Ross@Sun.COM return (0); 206*10023SGordon.Ross@Sun.COM 207*10023SGordon.Ross@Sun.COM /* 208*10023SGordon.Ross@Sun.COM * Let caller deal with empty reply or short messages by 209*10023SGordon.Ross@Sun.COM * returning zero. Caller will fail later, in parsing. 210*10023SGordon.Ross@Sun.COM */ 211*10023SGordon.Ross@Sun.COM if (m == NULL) { 212*10023SGordon.Ross@Sun.COM DPRINT("empty reply"); 213*10023SGordon.Ross@Sun.COM return (0); 214*10023SGordon.Ross@Sun.COM } 215*10023SGordon.Ross@Sun.COM if (m->m_len < SMB_HDRLEN) { 216*10023SGordon.Ross@Sun.COM DPRINT("short reply"); 217*10023SGordon.Ross@Sun.COM return (0); 218*10023SGordon.Ross@Sun.COM } 219*10023SGordon.Ross@Sun.COM 220*10023SGordon.Ross@Sun.COM sigloc = (uchar_t *)m->m_data + SMBSIGOFF; 221*10023SGordon.Ross@Sun.COM rseqno = rqp->rq_seqno + 1; 222*10023SGordon.Ross@Sun.COM 223*10023SGordon.Ross@Sun.COM DPRINT("rq_rseqno = 0x%x", rseqno); 224*10023SGordon.Ross@Sun.COM 225*10023SGordon.Ross@Sun.COM err = smb_compute_MAC(ctx, m, rseqno, sigbuf); 226*10023SGordon.Ross@Sun.COM if (err) { 227*10023SGordon.Ross@Sun.COM DPRINT("compute MAC, err %d", err); 228*10023SGordon.Ross@Sun.COM /* 229*10023SGordon.Ross@Sun.COM * If we can't compute a MAC, then there's 230*10023SGordon.Ross@Sun.COM * no point trying other seqno values. 231*10023SGordon.Ross@Sun.COM */ 232*10023SGordon.Ross@Sun.COM return (EBADRPC); 233*10023SGordon.Ross@Sun.COM } 234*10023SGordon.Ross@Sun.COM 235*10023SGordon.Ross@Sun.COM /* 236*10023SGordon.Ross@Sun.COM * Compare the computed signature with the 237*10023SGordon.Ross@Sun.COM * one found in the message (at sigloc) 238*10023SGordon.Ross@Sun.COM */ 239*10023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 240*10023SGordon.Ross@Sun.COM return (0); 241*10023SGordon.Ross@Sun.COM 242*10023SGordon.Ross@Sun.COM DPRINT("BAD signature, MID=0x%x", rqp->rq_mid); 243*10023SGordon.Ross@Sun.COM 244*10023SGordon.Ross@Sun.COM #ifdef DEBUG 245*10023SGordon.Ross@Sun.COM /* 246*10023SGordon.Ross@Sun.COM * For diag purposes, we check whether the client/server idea 247*10023SGordon.Ross@Sun.COM * of the sequence # has gotten a bit out of sync. 248*10023SGordon.Ross@Sun.COM */ 249*10023SGordon.Ross@Sun.COM for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) { 250*10023SGordon.Ross@Sun.COM smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf); 251*10023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) 252*10023SGordon.Ross@Sun.COM break; 253*10023SGordon.Ross@Sun.COM smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf); 254*10023SGordon.Ross@Sun.COM if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { 255*10023SGordon.Ross@Sun.COM fudge = -fudge; 256*10023SGordon.Ross@Sun.COM break; 257*10023SGordon.Ross@Sun.COM } 258*10023SGordon.Ross@Sun.COM } 259*10023SGordon.Ross@Sun.COM if (fudge <= nsmb_signing_fudge) { 260*10023SGordon.Ross@Sun.COM DPRINT("rseqno=%d, but %d would have worked", 261*10023SGordon.Ross@Sun.COM rseqno, rseqno + fudge); 262*10023SGordon.Ross@Sun.COM } 263*10023SGordon.Ross@Sun.COM #endif 264*10023SGordon.Ross@Sun.COM return (EBADRPC); 265*10023SGordon.Ross@Sun.COM } 266