fs/smb/client/smb2transport.c

Source file repositories/reference/linux-study-clean/fs/smb/client/smb2transport.c

File Facts

System
Linux kernel
Corpus path
fs/smb/client/smb2transport.c
Extension
.c
Size
21510 bytes
Lines
817
Domain
Core OS
Bucket
VFS And Filesystem Core
Inferred role
Core OS: implementation source
Status
source implementation candidate

Why This File Exists

Core operating-system implementation surface: boot, tasks, memory, VFS, syscall-facing interfaces, synchronization, credentials, and isolation.

Dependency Surface

Detected Declarations

Annotated Snippet

struct derivation {
	struct kvec label;
	struct kvec context;
};

struct derivation_triplet {
	struct derivation signing;
	struct derivation encryption;
	struct derivation decryption;
};

static int
generate_smb3signingkey(struct cifs_ses *ses,
			struct TCP_Server_Info *server,
			const struct derivation_triplet *ptriplet)
{
	unsigned int full_key_size = SMB2_NTLMV2_SESSKEY_SIZE;
	bool is_binding = false;
	int chan_index = 0;

	spin_lock(&ses->ses_lock);
	spin_lock(&ses->chan_lock);
	is_binding = (cifs_chan_needs_reconnect(ses, server) &&
		      ses->ses_status == SES_GOOD);

	chan_index = cifs_ses_get_chan_index(ses, server);
	if (chan_index == CIFS_INVAL_CHAN_INDEX) {
		spin_unlock(&ses->chan_lock);
		spin_unlock(&ses->ses_lock);

		return -EINVAL;
	}

	spin_unlock(&ses->chan_lock);
	spin_unlock(&ses->ses_lock);

	/*
	 * All channels use the same encryption/decryption keys but
	 * they have their own signing key.
	 *
	 * When we generate the keys, check if it is for a new channel
	 * (binding) in which case we only need to generate a signing
	 * key and store it in the channel as to not overwrite the
	 * master connection signing key stored in the session
	 */

	if (is_binding) {
		generate_key(ses, ptriplet->signing.label,
			     ptriplet->signing.context,
			     ses->chans[chan_index].signkey, SMB3_SIGN_KEY_SIZE,
			     SMB2_NTLMV2_SESSKEY_SIZE);
	} else {
		generate_key(ses, ptriplet->signing.label,
			     ptriplet->signing.context, ses->smb3signingkey,
			     SMB3_SIGN_KEY_SIZE, SMB2_NTLMV2_SESSKEY_SIZE);

		/*
		 * Per MS-SMB2 3.2.5.3.1, signing key always uses Session.SessionKey
		 * (first 16 bytes). Encryption/decryption keys use
		 * Session.FullSessionKey when dialect is 3.1.1 and cipher is
		 * AES-256-CCM or AES-256-GCM, otherwise Session.SessionKey.
		 */

		if (server->dialect == SMB311_PROT_ID &&
		    (server->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
		     server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
			full_key_size = ses->auth_key.len;

		/* safe to access primary channel, since it will never go away */
		spin_lock(&ses->chan_lock);
		memcpy(ses->chans[chan_index].signkey, ses->smb3signingkey,
		       SMB3_SIGN_KEY_SIZE);
		spin_unlock(&ses->chan_lock);

		generate_key(ses, ptriplet->encryption.label,
			     ptriplet->encryption.context,
			     ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE,
			     full_key_size);

		generate_key(ses, ptriplet->decryption.label,
			     ptriplet->decryption.context,
			     ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE,
			     full_key_size);
	}

#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
	cifs_dbg(VFS, "%s: dumping generated AES session keys\n", __func__);
	/*
	 * The session id is opaque in terms of endianness, so we can't
	 * print it as a long long. we dump it as we got it on the wire

Annotation

Implementation Notes