diff options
Diffstat (limited to 'fs/cifs/smb2transport.c')
-rw-r--r-- | fs/cifs/smb2transport.c | 165 |
1 files changed, 156 insertions, 9 deletions
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 31f5d420b3ea..66479f252ae5 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c | |||
@@ -36,6 +36,149 @@ | |||
36 | #include "smb2proto.h" | 36 | #include "smb2proto.h" |
37 | #include "cifs_debug.h" | 37 | #include "cifs_debug.h" |
38 | #include "smb2status.h" | 38 | #include "smb2status.h" |
39 | #include "smb2glob.h" | ||
40 | |||
41 | static int | ||
42 | smb2_calc_signature2(const struct kvec *iov, int n_vec, | ||
43 | struct TCP_Server_Info *server) | ||
44 | { | ||
45 | int i, rc; | ||
46 | unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; | ||
47 | unsigned char *sigptr = smb2_signature; | ||
48 | struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; | ||
49 | |||
50 | memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); | ||
51 | memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); | ||
52 | |||
53 | rc = crypto_shash_setkey(server->secmech.hmacsha256, | ||
54 | server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); | ||
55 | if (rc) { | ||
56 | cERROR(1, "%s: Could not update with response\n", __func__); | ||
57 | return rc; | ||
58 | } | ||
59 | |||
60 | rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); | ||
61 | if (rc) { | ||
62 | cERROR(1, "%s: Could not init md5\n", __func__); | ||
63 | return rc; | ||
64 | } | ||
65 | |||
66 | for (i = 0; i < n_vec; i++) { | ||
67 | if (iov[i].iov_len == 0) | ||
68 | continue; | ||
69 | if (iov[i].iov_base == NULL) { | ||
70 | cERROR(1, "null iovec entry"); | ||
71 | return -EIO; | ||
72 | } | ||
73 | /* | ||
74 | * The first entry includes a length field (which does not get | ||
75 | * signed that occupies the first 4 bytes before the header). | ||
76 | */ | ||
77 | if (i == 0) { | ||
78 | if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ | ||
79 | break; /* nothing to sign or corrupt header */ | ||
80 | rc = | ||
81 | crypto_shash_update( | ||
82 | &server->secmech.sdeschmacsha256->shash, | ||
83 | iov[i].iov_base + 4, iov[i].iov_len - 4); | ||
84 | } else { | ||
85 | rc = | ||
86 | crypto_shash_update( | ||
87 | &server->secmech.sdeschmacsha256->shash, | ||
88 | iov[i].iov_base, iov[i].iov_len); | ||
89 | } | ||
90 | if (rc) { | ||
91 | cERROR(1, "%s: Could not update with payload\n", | ||
92 | __func__); | ||
93 | return rc; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, | ||
98 | sigptr); | ||
99 | if (rc) | ||
100 | cERROR(1, "%s: Could not generate sha256 hash\n", __func__); | ||
101 | |||
102 | memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); | ||
103 | |||
104 | return rc; | ||
105 | } | ||
106 | |||
107 | /* must be called with server->srv_mutex held */ | ||
108 | static int | ||
109 | smb2_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server) | ||
110 | { | ||
111 | int rc = 0; | ||
112 | struct smb2_hdr *smb2_pdu = iov[0].iov_base; | ||
113 | |||
114 | if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) || | ||
115 | server->tcpStatus == CifsNeedNegotiate) | ||
116 | return rc; | ||
117 | |||
118 | if (!server->session_estab) { | ||
119 | strncpy(smb2_pdu->Signature, "BSRSPYL", 8); | ||
120 | return rc; | ||
121 | } | ||
122 | |||
123 | rc = smb2_calc_signature2(iov, n_vec, server); | ||
124 | |||
125 | return rc; | ||
126 | } | ||
127 | |||
128 | int | ||
129 | smb2_verify_signature2(struct kvec *iov, unsigned int n_vec, | ||
130 | struct TCP_Server_Info *server) | ||
131 | { | ||
132 | unsigned int rc; | ||
133 | char server_response_sig[16]; | ||
134 | struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; | ||
135 | |||
136 | if ((smb2_pdu->Command == SMB2_NEGOTIATE) || | ||
137 | (smb2_pdu->Command == SMB2_OPLOCK_BREAK) || | ||
138 | (!server->session_estab)) | ||
139 | return 0; | ||
140 | |||
141 | /* | ||
142 | * BB what if signatures are supposed to be on for session but | ||
143 | * server does not send one? BB | ||
144 | */ | ||
145 | |||
146 | /* Do not need to verify session setups with signature "BSRSPYL " */ | ||
147 | if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0) | ||
148 | cFYI(1, "dummy signature received for smb command 0x%x", | ||
149 | smb2_pdu->Command); | ||
150 | |||
151 | /* | ||
152 | * Save off the origiginal signature so we can modify the smb and check | ||
153 | * our calculated signature against what the server sent. | ||
154 | */ | ||
155 | memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE); | ||
156 | |||
157 | memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE); | ||
158 | |||
159 | mutex_lock(&server->srv_mutex); | ||
160 | rc = smb2_calc_signature2(iov, n_vec, server); | ||
161 | mutex_unlock(&server->srv_mutex); | ||
162 | |||
163 | if (rc) | ||
164 | return rc; | ||
165 | |||
166 | if (memcmp(server_response_sig, smb2_pdu->Signature, | ||
167 | SMB2_SIGNATURE_SIZE)) | ||
168 | return -EACCES; | ||
169 | else | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | smb2_verify_signature(struct smb2_hdr *smb2_pdu, struct TCP_Server_Info *server) | ||
175 | { | ||
176 | struct kvec iov; | ||
177 | |||
178 | iov.iov_base = (char *)smb2_pdu; | ||
179 | iov.iov_len = get_rfc1002_length(smb2_pdu) + 4; | ||
180 | return smb2_verify_signature2(&iov, 1, server); | ||
181 | } | ||
39 | 182 | ||
40 | /* | 183 | /* |
41 | * Set message id for the request. Should be called after wait_for_free_request | 184 | * Set message id for the request. Should be called after wait_for_free_request |
@@ -118,12 +261,15 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, | |||
118 | 261 | ||
119 | dump_smb(mid->resp_buf, min_t(u32, 80, len)); | 262 | dump_smb(mid->resp_buf, min_t(u32, 80, len)); |
120 | /* convert the length into a more usable form */ | 263 | /* convert the length into a more usable form */ |
121 | /* BB - uncomment with SMB2 signing implementation */ | 264 | if ((len > 24) && |
122 | /* if ((len > 24) && | ||
123 | (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) { | 265 | (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) { |
124 | if (smb2_verify_signature(mid->resp_buf, server)) | 266 | int rc; |
125 | cERROR(1, "Unexpected SMB signature"); | 267 | |
126 | } */ | 268 | rc = smb2_verify_signature(mid->resp_buf, server); |
269 | if (rc) | ||
270 | cERROR(1, "SMB signature verification returned error = " | ||
271 | "%d", rc); | ||
272 | } | ||
127 | 273 | ||
128 | return map_smb2_to_linux_error(mid->resp_buf, log_error); | 274 | return map_smb2_to_linux_error(mid->resp_buf, log_error); |
129 | } | 275 | } |
@@ -141,9 +287,9 @@ smb2_setup_request(struct cifs_ses *ses, struct kvec *iov, | |||
141 | rc = smb2_get_mid_entry(ses, hdr, &mid); | 287 | rc = smb2_get_mid_entry(ses, hdr, &mid); |
142 | if (rc) | 288 | if (rc) |
143 | return rc; | 289 | return rc; |
144 | /* rc = smb2_sign_smb2(iov, nvec, ses->server); | 290 | rc = smb2_sign_smb2(iov, nvec, ses->server); |
145 | if (rc) | 291 | if (rc) |
146 | delete_mid(mid); */ | 292 | cifs_delete_mid(mid); |
147 | *ret_mid = mid; | 293 | *ret_mid = mid; |
148 | return rc; | 294 | return rc; |
149 | } | 295 | } |
@@ -162,11 +308,12 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov, | |||
162 | if (mid == NULL) | 308 | if (mid == NULL) |
163 | return -ENOMEM; | 309 | return -ENOMEM; |
164 | 310 | ||
165 | /* rc = smb2_sign_smb2(iov, nvec, server); | 311 | rc = smb2_sign_smb2(iov, nvec, server); |
166 | if (rc) { | 312 | if (rc) { |
167 | DeleteMidQEntry(mid); | 313 | DeleteMidQEntry(mid); |
168 | return rc; | 314 | return rc; |
169 | }*/ | 315 | } |
316 | |||
170 | *ret_mid = mid; | 317 | *ret_mid = mid; |
171 | return rc; | 318 | return rc; |
172 | } | 319 | } |