diff options
Diffstat (limited to 'sys/netsmb/smb_crypt.c')
-rw-r--r-- | sys/netsmb/smb_crypt.c | 193 |
1 files changed, 192 insertions, 1 deletions
diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c index 6695d57..9d01217 100644 --- a/sys/netsmb/smb_crypt.c +++ b/sys/netsmb/smb_crypt.c @@ -2,6 +2,9 @@ * Copyright (c) 2000-2001, Boris Popov * All rights reserved. * + * Copyright (c) 2003, 2004 Tim J. Robbins. + * All rights reserved. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -43,13 +46,17 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/sysctl.h> - +#include <sys/endian.h> +#include <sys/mbuf.h> +#include <sys/mchain.h> #include <sys/md4.h> +#include <sys/md5.h> #include <sys/iconv.h> #include <netsmb/smb.h> #include <netsmb/smb_conn.h> #include <netsmb/smb_subr.h> +#include <netsmb/smb_rq.h> #include <netsmb/smb_dev.h> #include "opt_netsmb.h" @@ -146,3 +153,187 @@ smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN) #endif } +/* + * Calculate message authentication code (MAC) key for virtual circuit. + */ +int +smb_calcmackey(struct smb_vc *vcp) +{ +#ifdef NETSMBCRYPTO + const char *pwd; + u_int16_t *unipwd; + int len; + MD4_CTX md4; + u_char S16[16], S21[21]; + + KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE, + ("signatures not enabled")); + + if (vcp->vc_mackey != NULL) { + free(vcp->vc_mackey, M_SMBTEMP); + vcp->vc_mackey = NULL; + vcp->vc_mackeylen = 0; + vcp->vc_seqno = 0; + } + + /* + * The partial MAC key is the concatenation of the 16 byte session + * key and the 24 byte challenge response. + */ + vcp->vc_mackeylen = 16 + 24; + vcp->vc_mackey = malloc(vcp->vc_mackeylen, M_SMBTEMP, M_WAITOK); + + /* + * Calculate session key: + * MD4(MD4(U(PN))) + */ + pwd = smb_vc_getpass(vcp); + len = strlen(pwd); + unipwd = malloc((len + 1) * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK); + smb_strtouni(unipwd, pwd); + MD4Init(&md4); + MD4Update(&md4, (u_char *)unipwd, len * sizeof(u_int16_t)); + MD4Final(S16, &md4); + MD4Init(&md4); + MD4Update(&md4, S16, 16); + MD4Final(vcp->vc_mackey, &md4); + free(unipwd, M_SMBTEMP); + + /* + * Calculate response to challenge: + * Ex(concat(MD4(U(pass)), zeros(5)), C8) + */ + bzero(S21, 21); + bcopy(S16, S21, 16); + smb_E(S21, vcp->vc_ch, vcp->vc_mackey + 16); + smb_E(S21 + 7, vcp->vc_ch, vcp->vc_mackey + 24); + smb_E(S21 + 14, vcp->vc_ch, vcp->vc_mackey + 32); + + return (0); +#else + panic("smb_calcmackey: encryption not available"); + return (0); +#endif /* NETSMBCRYPTO */ +} + +/* + * Sign request with MAC. + */ +int +smb_rq_sign(struct smb_rq *rqp) +{ +#ifdef NETSMBCRYPTO + struct smb_vc *vcp = rqp->sr_vc; + struct mbchain *mbp; + struct mbuf *mb; + MD5_CTX md5; + u_char digest[16]; + + KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE, + ("signatures not enabled")); + + if (vcp->vc_mackey == NULL) + /* XXX Should assert that cmd == SMB_COM_NEGOTIATE. */ + return (0); + + /* + * This is a bit of a kludge. If the request is non-TRANSACTION, + * or it is the first request of a transaction, give it the next + * sequence number, and expect the reply to have the sequence number + * following that one. Otherwise, it is a secondary request in + * a transaction, and it gets the same sequence numbers as the + * primary request. + */ + if (rqp->sr_t2 == NULL || + (rqp->sr_t2->t2_flags & SMBT2_SECONDARY) == 0) { + rqp->sr_seqno = vcp->vc_seqno++; + rqp->sr_rseqno = vcp->vc_seqno++; + } else { + /* + * Sequence numbers are already in the struct because + * smb_t2_request_int() uses the same one for all the + * requests in the transaction. + * (At least we hope so.) + */ + KASSERT(rqp->sr_t2 == NULL || + (rqp->sr_t2->t2_flags & SMBT2_SECONDARY) == 0 || + rqp->sr_t2->t2_rq == rqp, + ("sec t2 rq not using same smb_rq")); + } + + /* Initialize sec. signature field to sequence number + zeros. */ + *(u_int32_t *)rqp->sr_rqsig = htole32(rqp->sr_seqno); + *(u_int32_t *)(rqp->sr_rqsig + 4) = 0; + + /* + * Compute HMAC-MD5 of packet data, keyed by MAC key. + * Store the first 8 bytes in the sec. signature field. + */ + smb_rq_getrequest(rqp, &mbp); + MD5Init(&md5); + MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen); + for (mb = mbp->mb_top; mb != NULL; mb = mb->m_next) + MD5Update(&md5, mtod(mb, void *), mb->m_len); + MD5Final(digest, &md5); + bcopy(digest, rqp->sr_rqsig, 8); + + return (0); +#else + panic("smb_rq_sign: encryption not available"); + return (0); +#endif /* NETSMBCRYPTO */ +} + +/* + * Verify reply signature. + */ +int +smb_rq_verify(struct smb_rq *rqp) +{ +#ifdef NETSMBCRYPTO + struct smb_vc *vcp = rqp->sr_vc; + struct mdchain *mdp; + u_char sigbuf[8]; + MD5_CTX md5; + u_char digest[16]; + struct mbuf *mb; + + KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE, + ("signatures not enabled")); + + if (vcp->vc_mackey == NULL) + /* XXX Should check that this is a SMB_COM_NEGOTIATE reply. */ + return (0); + + /* + * Compute HMAC-MD5 of packet data, keyed by MAC key. + * We play games to pretend the security signature field + * contains their sequence number, to avoid modifying + * the packet itself. + */ + smb_rq_getreply(rqp, &mdp); + mb = mdp->md_top; + KASSERT(mb->m_len >= SMB_HDRLEN, ("forgot to m_pullup")); + MD5Init(&md5); + MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen); + MD5Update(&md5, mtod(mb, void *), 14); + *(u_int32_t *)sigbuf = htole32(rqp->sr_rseqno); + *(u_int32_t *)(sigbuf + 4) = 0; + MD5Update(&md5, sigbuf, 8); + MD5Update(&md5, mtod(mb, u_char *) + 22, mb->m_len - 22); + for (mb = mb->m_next; mb != NULL; mb = mb->m_next) + MD5Update(&md5, mtod(mb, void *), mb->m_len); + MD5Final(digest, &md5); + + /* + * Now verify the signature. + */ + if (bcmp(mtod(mdp->md_top, u_char *) + 14, digest, 8) != 0) + return (EAUTH); + + return (0); +#else + panic("smb_rq_verify: encryption not available"); + return (0); +#endif /* NETSMBCRYPTO */ +} |