static int cifs_calc_sealsignature(const struct kvec *iov, int n_vec, struct cifs_tcon *tcon, char *sealsign, enum sealdir dir) { int i, rc; u32 nvernum = NTLMSSP_SIGN_VERSION; unsigned char sealsigncph[CIFS_HMAC_MD5_HASH_SIZE]; struct scatterlist sg; if (!tcon->secmech.arc4) { cERROR(1, "%s: Error: NULL rc4 encryption function", __func__); return -1; } if (!tcon->ses->server->secmech.sdeschmacmd5) { cERROR(1, "%s: Can't generate signature\n", __func__); return -1; } /* generate hmac-md5 hash using client side sign key */ if (dir == send) rc = crypto_shash_setkey( tcon->ses->server->secmech.hmacmd5, tcon->clsignkey, CIFS_HMAC_MD5_HASH_SIZE); else rc = crypto_shash_setkey( tcon->ses->server->secmech.hmacmd5, tcon->srvsignkey, CIFS_HMAC_MD5_HASH_SIZE); if (rc) { cERROR(1, "%s: Could not set client signing key\n", __func__); return rc; } rc = crypto_shash_init(&tcon->ses->server->secmech.sdeschmacmd5->shash); if (rc) { cERROR(1, "%s: Could not init md5\n", __func__); return rc; } rc = crypto_shash_update( &tcon->ses->server->secmech.sdeschmacmd5->shash, (char *)&tcon->sequence_number, sizeof(__u32)); if (rc) { cERROR(1, "%s: Could not update with sequence #\n", __func__); return rc; } for (i = 0; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cERROR(1, "null iovec entry"); return -EIO; } /* The first entry includes a length field (which does not get signed that occupies the first 4 bytes before the header */ if (i == 0) { if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ rc = crypto_shash_update( &tcon->ses->server->secmech.sdeschmacmd5->shash, iov[i].iov_base + 8, iov[i].iov_len - 8); } else { rc = crypto_shash_update( &tcon->ses->server->secmech.sdeschmacmd5->shash, iov[i].iov_base, iov[i].iov_len); } if (rc) { cERROR(1, "%s: Could not update with payload\n", __func__); return rc; } } rc = crypto_shash_final( &tcon->ses->server->secmech.sdeschmacmd5->shash, sealsigncph); if (rc) { cERROR(1, "%s: Could not generate md5 hash\n", __func__); return rc; } /* encrypt/seal the hmac-md5 hash using client side seal key */ if (dir == send) rc = crypto_blkcipher_setkey(tcon->secmech.arc4, tcon->clsealkey, CIFS_HMAC_MD5_HASH_SIZE); else rc = crypto_blkcipher_setkey(tcon->secmech.arc4, tcon->srvsealkey, CIFS_HMAC_MD5_HASH_SIZE); if (rc) { cERROR(1, "%s: Could not set response as a key", __func__); return rc; } // memcpy(sealsigncph, "abcdefgh", 8); sg_init_one(&sg, sealsigncph, 8); cifs_dump_mem("XXX unenc seal sign", sealsigncph, 8); rc = crypto_blkcipher_encrypt(&tcon->secmech.sdescarc4, &sg, &sg, 8); if (rc) { cERROR(1, "%s: Error %d during seal sign ciphertext\n", __func__, rc); return rc; } cifs_dump_mem("XXX enc seal sign", sealsigncph, 8); /* SSPI signature consists of version number + encrypted signature + tcon seq number */ memcpy(sealsign, &nvernum, 4); memcpy(sealsign + 4, sealsigncph, 8); memcpy(sealsign + 12, &tcon->sequence_number, 4); return rc; } /* must be called with server->srv_mutex held */ int cifs_seal_smb(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number, struct cifs_tcon *tcon) { int i; int rc = 0; unsigned int pldlen = 0; unsigned buflen; struct scatterlist *sgin, *sgout; char sealsign[CIFS_HMAC_MD5_HASH_SIZE]; struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base; unsigned char *niov; buflen = be32_to_cpu(cifs_pdu->smb_buf_length); memcpy(iov[0].iov_base + 8, "jCIFS", 5); iov[0].iov_len = 8 + 5; buflen = 4 + 5; cifs_pdu->smb_buf_length = cpu_to_be32(buflen); buflen = be32_to_cpu(cifs_pdu->smb_buf_length); cERROR(1, "%s: XXX iov[0].iov_len: %d buflen: %d\n", __func__, iov[0].iov_len, buflen); cifs_dump_mem("XXX iov[0]", iov[0].iov_base, iov[0].iov_len); rc = cifs_calc_sealsignature(iov, n_vec, tcon, sealsign, send); if (rc) { cERROR(1, "%s: Error %d generating seal signature\n", __func__, rc); return rc; } /* generate the sealed message */ if (!server->secmech.arc4) { cERROR(1, "%s: Error: NULL rc4 encryption function", __func__); return -ENOKEY; } sgin = kmalloc(sizeof(struct scatterlist) * n_vec, GFP_KERNEL); if (!sgin) { cERROR(1, "%s: Can't allocate scatterlist memory", __func__); return -ENOMEM; } sgout = kmalloc(sizeof(struct scatterlist) * n_vec, GFP_KERNEL); if (!sgout) { cERROR(1, "%s: Can't allocate scatterlist memory", __func__); return -ENOMEM; } niov = kmalloc(iov[0].iov_len + 16, GFP_KERNEL); if (!niov) { cERROR(1, "%s: Can't allocate buffer memory", __func__); return -ENOMEM; } rc = crypto_blkcipher_setkey(tcon->secmech.arc4, tcon->clsealkey, CIFS_HMAC_MD5_HASH_SIZE); if (rc) { cERROR(1, "%s: Could not set response as a key", __func__); goto cifs_seal_msg_ret; } for (i = 0; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; if (iov[i].iov_base == NULL) { cERROR(1, "null iovec entry"); rc = -EIO; goto cifs_seal_msg_ret; } /* The first entry includes a length field (which does not get * signed that occupies the first 4 bytes before the header */ if (i == 0) { if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ sg_init_one(&sgin[i], iov[i].iov_base + 8, iov[i].iov_len - 8); sg_init_one(&sgout[i], niov, iov[i].iov_len - 8); } else { sg_init_one(&sgin[i], iov[i].iov_base, iov[i].iov_len); sg_init_one(&sgout[i], iov[i].iov_base, iov[i].iov_len); } } pldlen = iov[0].iov_len - 8; rc = crypto_blkcipher_encrypt_iv(&tcon->secmech.sdescarc4, sgout, sgin, pldlen); if (rc) { cERROR(1, "%s: Error %d encrypting smb signature\n", __func__, rc); goto cifs_seal_msg_ret; } *(char *)(iov[0].iov_base + 4) = 0xFF; *(char *)(iov[0].iov_base + 5) = 'E'; *(char *)(iov[0].iov_base + 6) = 0x0; *(char *)(iov[0].iov_base + 7) = 0x0; memcpy(iov[0].iov_base + 24, niov, iov[0].iov_len - 8); memcpy(iov[0].iov_base + 8, sealsign, CIFS_HMAC_MD5_HASH_SIZE); iov[0].iov_len += 16; cifs_pdu->smb_buf_length = cpu_to_be32(buflen + 16); *pexpected_response_sequence_number = tcon->sequence_number++; tcon->sequence_number++; cifs_seal_msg_ret: kfree(sgin); kfree(sgout); kfree(niov); return rc; } int calc_seckey(struct cifs_ses *ses) { int rc = 0; struct scatterlist sgin, sgout; unsigned char sec_key[CIFS_SESS_KEY_SIZE]; /* a nonce */ unsigned long long fixedkey1 = 0x0807060504030201; unsigned long long fixedkey2 = 0x000f0e0d0c0b0a09; mutex_lock(&ses->server->srv_mutex); if (!ses->server->secmech.arc4) { rc = -ENOKEY; cERROR(1, "%s: Error: NULL rc4 encryption function", __func__); goto calc_seckey_ret; } // get_random_bytes(sec_key, CIFS_SESS_KEY_SIZE); memcpy(sec_key, &fixedkey1, 8); memcpy(sec_key + 8, &fixedkey2, 8); rc = crypto_blkcipher_setkey(ses->server->secmech.arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE); if (rc) { cERROR(1, "%s: Could not set response as a key", __func__); goto calc_seckey_ret; } sg_init_one(&sgin, sec_key, CIFS_SESS_KEY_SIZE); sg_init_one(&sgout, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE); rc = crypto_blkcipher_encrypt(&ses->server->secmech.sdescarc4, &sgout, &sgin, CIFS_CPHTXT_SIZE); if (rc) { cERROR(1, "could not encrypt session key rc: %d\n", rc); goto calc_seckey_ret; } /* make secondary_key/nonce as session key */ memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE); /* and make len as that of session key only */ ses->auth_key.len = CIFS_SESS_KEY_SIZE; calc_seckey_ret: mutex_unlock(&ses->server->srv_mutex); return rc; }