registry: Fix Coverity ID 1034918 Wrong sizeof argument
[Samba.git] / libcli / auth / schannel_sign.c
blob9502cba6bb17fab126158ad98a4adcd2767c0d47
1 /*
2 Unix SMB/CIFS implementation.
4 schannel library code
6 Copyright (C) Andrew Tridgell 2004
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "../libcli/auth/schannel.h"
25 #include "../lib/crypto/crypto.h"
27 struct schannel_state {
28 uint64_t seq_num;
29 bool initiator;
30 struct netlogon_creds_CredentialState *creds;
33 #define SETUP_SEQNUM(state, buf, initiator) do { \
34 uint8_t *_buf = buf; \
35 uint32_t _seq_num_low = (state)->seq_num & UINT32_MAX; \
36 uint32_t _seq_num_high = (state)->seq_num >> 32; \
37 if (initiator) { \
38 _seq_num_high |= 0x80000000; \
39 } \
40 RSIVAL(_buf, 0, _seq_num_low); \
41 RSIVAL(_buf, 4, _seq_num_high); \
42 } while(0)
44 struct schannel_state *netsec_create_state(TALLOC_CTX *mem_ctx,
45 struct netlogon_creds_CredentialState *creds,
46 bool initiator)
48 struct schannel_state *state;
50 state = talloc(mem_ctx, struct schannel_state);
51 if (state == NULL) {
52 return NULL;
55 state->initiator = initiator;
56 state->seq_num = 0;
57 state->creds = netlogon_creds_copy(state, creds);
58 if (state->creds == NULL) {
59 talloc_free(state);
60 return NULL;
63 return state;
66 static void netsec_offset_and_sizes(struct schannel_state *state,
67 bool do_seal,
68 uint32_t *_min_sig_size,
69 uint32_t *_used_sig_size,
70 uint32_t *_checksum_length,
71 uint32_t *_confounder_ofs)
73 uint32_t min_sig_size;
74 uint32_t used_sig_size;
75 uint32_t checksum_length;
76 uint32_t confounder_ofs;
78 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
79 min_sig_size = 48;
80 used_sig_size = 56;
82 * Note: windows has a bug here and uses the old values...
84 * checksum_length = 32;
85 * confounder_ofs = 48;
87 checksum_length = 8;
88 confounder_ofs = 24;
89 } else {
90 min_sig_size = 24;
91 used_sig_size = 32;
92 checksum_length = 8;
93 confounder_ofs = 24;
96 if (do_seal) {
97 min_sig_size += 8;
100 if (_min_sig_size) {
101 *_min_sig_size = min_sig_size;
104 if (_used_sig_size) {
105 *_used_sig_size = used_sig_size;
108 if (_checksum_length) {
109 *_checksum_length = checksum_length;
112 if (_confounder_ofs) {
113 *_confounder_ofs = confounder_ofs;
117 /*******************************************************************
118 Encode or Decode the sequence number (which is symmetric)
119 ********************************************************************/
120 static void netsec_do_seq_num(struct schannel_state *state,
121 const uint8_t *checksum,
122 uint32_t checksum_length,
123 uint8_t seq_num[8])
125 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
126 AES_KEY key;
127 uint8_t iv[AES_BLOCK_SIZE];
129 AES_set_encrypt_key(state->creds->session_key, 128, &key);
130 ZERO_STRUCT(iv);
131 memcpy(iv+0, checksum, 8);
132 memcpy(iv+8, checksum, 8);
134 aes_cfb8_encrypt(seq_num, seq_num, 8, &key, iv, AES_ENCRYPT);
135 } else {
136 static const uint8_t zeros[4];
137 uint8_t sequence_key[16];
138 uint8_t digest1[16];
140 hmac_md5(state->creds->session_key, zeros, sizeof(zeros), digest1);
141 hmac_md5(digest1, checksum, checksum_length, sequence_key);
142 arcfour_crypt(seq_num, sequence_key, 8);
145 state->seq_num++;
148 static void netsec_do_seal(struct schannel_state *state,
149 const uint8_t seq_num[8],
150 uint8_t confounder[8],
151 uint8_t *data, uint32_t length,
152 bool forward)
154 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
155 AES_KEY key;
156 uint8_t iv[AES_BLOCK_SIZE];
157 uint8_t sess_kf0[16];
158 int i;
160 for (i = 0; i < 16; i++) {
161 sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
164 AES_set_encrypt_key(sess_kf0, 128, &key);
165 ZERO_STRUCT(iv);
166 memcpy(iv+0, seq_num, 8);
167 memcpy(iv+8, seq_num, 8);
169 if (forward) {
170 aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_ENCRYPT);
171 aes_cfb8_encrypt(data, data, length, &key, iv, AES_ENCRYPT);
172 } else {
173 aes_cfb8_encrypt(confounder, confounder, 8, &key, iv, AES_DECRYPT);
174 aes_cfb8_encrypt(data, data, length, &key, iv, AES_DECRYPT);
176 } else {
177 uint8_t sealing_key[16];
178 static const uint8_t zeros[4];
179 uint8_t digest2[16];
180 uint8_t sess_kf0[16];
181 int i;
183 for (i = 0; i < 16; i++) {
184 sess_kf0[i] = state->creds->session_key[i] ^ 0xf0;
187 hmac_md5(sess_kf0, zeros, 4, digest2);
188 hmac_md5(digest2, seq_num, 8, sealing_key);
190 arcfour_crypt(confounder, sealing_key, 8);
191 arcfour_crypt(data, sealing_key, length);
195 /*******************************************************************
196 Create a digest over the entire packet (including the data), and
197 MD5 it with the session key.
198 ********************************************************************/
199 static void netsec_do_sign(struct schannel_state *state,
200 const uint8_t *confounder,
201 const uint8_t *data, size_t length,
202 uint8_t header[8],
203 uint8_t *checksum)
205 if (state->creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
206 struct HMACSHA256Context ctx;
208 hmac_sha256_init(state->creds->session_key,
209 sizeof(state->creds->session_key),
210 &ctx);
212 if (confounder) {
213 SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
214 SSVAL(header, 2, NL_SEAL_AES128);
215 SSVAL(header, 4, 0xFFFF);
216 SSVAL(header, 6, 0x0000);
218 hmac_sha256_update(header, 8, &ctx);
219 hmac_sha256_update(confounder, 8, &ctx);
220 } else {
221 SSVAL(header, 0, NL_SIGN_HMAC_SHA256);
222 SSVAL(header, 2, NL_SEAL_NONE);
223 SSVAL(header, 4, 0xFFFF);
224 SSVAL(header, 6, 0x0000);
226 hmac_sha256_update(header, 8, &ctx);
229 hmac_sha256_update(data, length, &ctx);
231 hmac_sha256_final(checksum, &ctx);
232 } else {
233 uint8_t packet_digest[16];
234 static const uint8_t zeros[4];
235 MD5_CTX ctx;
237 MD5Init(&ctx);
238 MD5Update(&ctx, zeros, 4);
239 if (confounder) {
240 SSVAL(header, 0, NL_SIGN_HMAC_MD5);
241 SSVAL(header, 2, NL_SEAL_RC4);
242 SSVAL(header, 4, 0xFFFF);
243 SSVAL(header, 6, 0x0000);
245 MD5Update(&ctx, header, 8);
246 MD5Update(&ctx, confounder, 8);
247 } else {
248 SSVAL(header, 0, NL_SIGN_HMAC_MD5);
249 SSVAL(header, 2, NL_SEAL_NONE);
250 SSVAL(header, 4, 0xFFFF);
251 SSVAL(header, 6, 0x0000);
253 MD5Update(&ctx, header, 8);
255 MD5Update(&ctx, data, length);
256 MD5Final(packet_digest, &ctx);
258 hmac_md5(state->creds->session_key,
259 packet_digest, sizeof(packet_digest),
260 checksum);
264 NTSTATUS netsec_incoming_packet(struct schannel_state *state,
265 bool do_unseal,
266 uint8_t *data, size_t length,
267 const DATA_BLOB *sig)
269 uint32_t min_sig_size = 0;
270 uint8_t header[8];
271 uint8_t checksum[32];
272 uint32_t checksum_length = sizeof(checksum_length);
273 uint8_t _confounder[8];
274 uint8_t *confounder = NULL;
275 uint32_t confounder_ofs = 0;
276 uint8_t seq_num[8];
277 int ret;
279 netsec_offset_and_sizes(state,
280 do_unseal,
281 &min_sig_size,
282 NULL,
283 &checksum_length,
284 &confounder_ofs);
286 if (sig->length < min_sig_size) {
287 return NT_STATUS_ACCESS_DENIED;
290 if (do_unseal) {
291 confounder = _confounder;
292 memcpy(confounder, sig->data+confounder_ofs, 8);
293 } else {
294 confounder = NULL;
297 SETUP_SEQNUM(state, seq_num, !state->initiator);
299 if (do_unseal) {
300 netsec_do_seal(state, seq_num,
301 confounder,
302 data, length,
303 false);
306 netsec_do_sign(state, confounder,
307 data, length,
308 header, checksum);
310 ret = memcmp(checksum, sig->data+16, checksum_length);
311 if (ret != 0) {
312 dump_data_pw("calc digest:", checksum, checksum_length);
313 dump_data_pw("wire digest:", sig->data+16, checksum_length);
314 return NT_STATUS_ACCESS_DENIED;
317 netsec_do_seq_num(state, checksum, checksum_length, seq_num);
319 ret = memcmp(seq_num, sig->data+8, 8);
320 if (ret != 0) {
321 dump_data_pw("calc seq num:", seq_num, 8);
322 dump_data_pw("wire seq num:", sig->data+8, 8);
323 return NT_STATUS_ACCESS_DENIED;
326 return NT_STATUS_OK;
329 uint32_t netsec_outgoing_sig_size(struct schannel_state *state)
331 uint32_t sig_size = 0;
333 netsec_offset_and_sizes(state,
334 true,
335 NULL,
336 &sig_size,
337 NULL,
338 NULL);
340 return sig_size;
343 NTSTATUS netsec_outgoing_packet(struct schannel_state *state,
344 TALLOC_CTX *mem_ctx,
345 bool do_seal,
346 uint8_t *data, size_t length,
347 DATA_BLOB *sig)
349 uint32_t min_sig_size = 0;
350 uint32_t used_sig_size = 0;
351 uint8_t header[8];
352 uint8_t checksum[32];
353 uint32_t checksum_length = sizeof(checksum_length);
354 uint8_t _confounder[8];
355 uint8_t *confounder = NULL;
356 uint32_t confounder_ofs = 0;
357 uint8_t seq_num[8];
359 netsec_offset_and_sizes(state,
360 do_seal,
361 &min_sig_size,
362 &used_sig_size,
363 &checksum_length,
364 &confounder_ofs);
366 SETUP_SEQNUM(state, seq_num, state->initiator);
368 if (do_seal) {
369 confounder = _confounder;
370 generate_random_buffer(confounder, 8);
371 } else {
372 confounder = NULL;
375 netsec_do_sign(state, confounder,
376 data, length,
377 header, checksum);
379 if (do_seal) {
380 netsec_do_seal(state, seq_num,
381 confounder,
382 data, length,
383 true);
386 netsec_do_seq_num(state, checksum, checksum_length, seq_num);
388 (*sig) = data_blob_talloc_zero(mem_ctx, used_sig_size);
390 memcpy(sig->data, header, 8);
391 memcpy(sig->data+8, seq_num, 8);
392 memcpy(sig->data+16, checksum, checksum_length);
394 if (confounder) {
395 memcpy(sig->data+confounder_ofs, confounder, 8);
398 dump_data_pw("signature:", sig->data+ 0, 8);
399 dump_data_pw("seq_num :", sig->data+ 8, 8);
400 dump_data_pw("digest :", sig->data+16, checksum_length);
401 dump_data_pw("confound :", sig->data+confounder_ofs, 8);
403 return NT_STATUS_OK;