trying to get HEAD building again. If you want the code
[Samba.git] / source / libsmb / smb_signing.c
blobd4f77bf07c89c8f3099e0ef17a2059b3017d847f
1 /*
2 Unix SMB/CIFS implementation.
3 SMB Signing Code
4 Copyright (C) Jeremy Allison 2002.
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "includes.h"
24 /* Lookup a packet's MID (multiplex id) and figure out it's sequence number */
25 struct outstanding_packet_lookup {
26 uint16 mid;
27 uint32 reply_seq_num;
28 struct outstanding_packet_lookup *prev, *next;
31 struct smb_basic_signing_context {
32 DATA_BLOB mac_key;
33 uint32 send_seq_num;
34 struct outstanding_packet_lookup *outstanding_packet_list;
37 static void store_sequence_for_reply(struct outstanding_packet_lookup **list,
38 uint16 mid, uint32 reply_seq_num)
40 struct outstanding_packet_lookup *t;
41 struct outstanding_packet_lookup *tmp;
43 t = smb_xmalloc(sizeof(*t));
44 ZERO_STRUCTP(t);
46 DLIST_ADD_END(*list, t, tmp);
47 t->mid = mid;
48 t->reply_seq_num = reply_seq_num;
51 static BOOL get_sequence_for_reply(struct outstanding_packet_lookup **list,
52 uint16 mid, uint32 *reply_seq_num)
54 struct outstanding_packet_lookup *t;
56 for (t = *list; t; t = t->next) {
57 if (t->mid == mid) {
58 *reply_seq_num = t->reply_seq_num;
59 DLIST_REMOVE(*list, t);
60 return True;
63 DEBUG(0, ("Unexpected incoming packet, it's MID (%u) does not match"
64 " a MID in our outstanding list!\n", mid));
65 return False;
68 /***********************************************************
69 SMB signing - Common code before we set a new signing implementation
70 ************************************************************/
72 static BOOL cli_set_smb_signing_common(struct cli_state *cli)
74 if (!cli->sign_info.negotiated_smb_signing
75 && !cli->sign_info.mandatory_signing) {
76 return False;
79 if (cli->sign_info.doing_signing) {
80 return False;
83 if (cli->sign_info.free_signing_context)
84 cli->sign_info.free_signing_context(cli);
86 /* These calls are INCOMPATIBLE with SMB signing */
87 cli->readbraw_supported = False;
88 cli->writebraw_supported = False;
90 return True;
93 /***********************************************************
94 SMB signing - Common code for 'real' implementations
95 ************************************************************/
97 static BOOL cli_set_smb_signing_real_common(struct cli_state *cli)
99 if (cli->sign_info.mandatory_signing) {
100 DEBUG(5, ("Mandatory SMB signing enabled!\n"));
101 cli->sign_info.doing_signing = True;
104 DEBUG(5, ("SMB signing enabled!\n"));
106 return True;
109 static void cli_mark_packet_signed(struct cli_state *cli)
111 uint16 flags2;
112 flags2 = SVAL(cli->outbuf,smb_flg2);
113 flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
114 SSVAL(cli->outbuf,smb_flg2, flags2);
117 static BOOL cli_signing_good(struct cli_state *cli, BOOL good)
119 DEBUG(10, ("got SMB signature of\n"));
120 dump_data(10,&cli->inbuf[smb_ss_field] , 8);
122 if (good && !cli->sign_info.doing_signing) {
123 cli->sign_info.doing_signing = True;
126 if (!good) {
127 if (cli->sign_info.doing_signing) {
128 DEBUG(1, ("SMB signature check failed!\n"));
129 return False;
130 } else {
131 DEBUG(3, ("Server did not sign reply correctly\n"));
132 cli_free_signing_context(cli);
133 return False;
136 return True;
139 /***********************************************************
140 SMB signing - Simple implementation - calculate a MAC on the packet
141 ************************************************************/
143 static void simple_packet_signature(struct smb_basic_signing_context *data,
144 const uchar *buf, uint32 seq_number,
145 unsigned char calc_md5_mac[16])
147 const size_t offset_end_of_sig = (smb_ss_field + 8);
148 unsigned char sequence_buf[8];
149 struct MD5Context md5_ctx;
152 * Firstly put the sequence number into the first 4 bytes.
153 * and zero out the next 4 bytes.
155 * We do this here, to avoid modifying the packet.
158 SIVAL(sequence_buf, 0, seq_number);
159 SIVAL(sequence_buf, 4, 0);
161 /* Calculate the 16 byte MAC - but don't alter the data in the
162 incoming packet.
164 This makes for a bit for fussing about, but it's not too bad.
166 MD5Init(&md5_ctx);
168 /* intialise with the key */
169 MD5Update(&md5_ctx, data->mac_key.data,
170 data->mac_key.length);
172 /* copy in the first bit of the SMB header */
173 MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
175 /* copy in the sequence number, instead of the signature */
176 MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
178 /* copy in the rest of the packet in, skipping the signature */
179 MD5Update(&md5_ctx, buf + offset_end_of_sig,
180 smb_len(buf) - (offset_end_of_sig - 4));
182 /* caclulate the MD5 sig */
183 MD5Final(calc_md5_mac, &md5_ctx);
187 /***********************************************************
188 SMB signing - Simple implementation - send the MAC.
189 ************************************************************/
191 static void cli_simple_sign_outgoing_message(struct cli_state *cli)
193 unsigned char calc_md5_mac[16];
194 struct smb_basic_signing_context *data = cli->sign_info.signing_context;
196 /* mark the packet as signed - BEFORE we sign it...*/
197 cli_mark_packet_signed(cli);
199 simple_packet_signature(data, cli->outbuf, data->send_seq_num,
200 calc_md5_mac);
202 DEBUG(10, ("sent SMB signature of\n"));
203 dump_data(10, calc_md5_mac, 8);
205 memcpy(&cli->outbuf[smb_ss_field], calc_md5_mac, 8);
207 /* cli->outbuf[smb_ss_field+2]=0;
208 Uncomment this to test if the remote server actually verifies signatures...*/
210 data->send_seq_num++;
211 store_sequence_for_reply(&data->outstanding_packet_list,
212 cli->mid,
213 data->send_seq_num);
214 data->send_seq_num++;
217 /***********************************************************
218 SMB signing - Simple implementation - check a MAC sent by server.
219 ************************************************************/
221 static BOOL cli_simple_check_incoming_message(struct cli_state *cli)
223 BOOL good;
224 uint32 reply_seq_number;
225 unsigned char calc_md5_mac[16];
226 unsigned char *server_sent_mac;
228 struct smb_basic_signing_context *data = cli->sign_info.signing_context;
230 if (!get_sequence_for_reply(&data->outstanding_packet_list,
231 SVAL(cli->inbuf, smb_mid),
232 &reply_seq_number)) {
233 return False;
236 simple_packet_signature(data, cli->inbuf, reply_seq_number, calc_md5_mac);
238 server_sent_mac = &cli->inbuf[smb_ss_field];
239 good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
241 if (!good) {
242 DEBUG(5, ("BAD SIG: wanted SMB signature of\n"));
243 dump_data(5, calc_md5_mac, 8);
245 DEBUG(5, ("BAD SIG: got SMB signature of\n"));
246 dump_data(5, server_sent_mac, 8);
248 return cli_signing_good(cli, good);
251 /***********************************************************
252 SMB signing - Simple implementation - free signing context
253 ************************************************************/
255 static void cli_simple_free_signing_context(struct cli_state *cli)
257 struct smb_basic_signing_context *data = cli->sign_info.signing_context;
258 struct outstanding_packet_lookup *list = data->outstanding_packet_list;
260 while (list) {
261 struct outstanding_packet_lookup *old_head = list;
262 DLIST_REMOVE(list, list);
263 SAFE_FREE(old_head);
266 data_blob_free(&data->mac_key);
267 SAFE_FREE(cli->sign_info.signing_context);
269 return;
272 /***********************************************************
273 SMB signing - Simple implementation - setup the MAC key.
274 ************************************************************/
276 BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
278 struct smb_basic_signing_context *data;
280 if (!user_session_key)
281 return False;
283 if (!cli_set_smb_signing_common(cli)) {
284 return False;
287 if (!cli_set_smb_signing_real_common(cli)) {
288 return False;
291 data = smb_xmalloc(sizeof(*data));
293 cli->sign_info.signing_context = data;
295 data->mac_key = data_blob(NULL, response.length + 16);
297 memcpy(&data->mac_key.data[0], user_session_key, 16);
298 memcpy(&data->mac_key.data[16],response.data, response.length);
300 /* Initialise the sequence number */
301 data->send_seq_num = 0;
303 /* Initialise the list of outstanding packets */
304 data->outstanding_packet_list = NULL;
306 cli->sign_info.sign_outgoing_message = cli_simple_sign_outgoing_message;
307 cli->sign_info.check_incoming_message = cli_simple_check_incoming_message;
308 cli->sign_info.free_signing_context = cli_simple_free_signing_context;
310 return True;
313 /***********************************************************
314 SMB signing - NULL implementation - calculate a MAC to send.
315 ************************************************************/
317 static void cli_null_sign_outgoing_message(struct cli_state *cli)
319 /* we can't zero out the sig, as we might be trying to send a
320 session request - which is NBT-level, not SMB level and doesn't
321 have the field */
322 return;
325 /***********************************************************
326 SMB signing - NULL implementation - check a MAC sent by server.
327 ************************************************************/
329 static BOOL cli_null_check_incoming_message(struct cli_state *cli)
331 return True;
334 /***********************************************************
335 SMB signing - NULL implementation - free signing context
336 ************************************************************/
338 static void cli_null_free_signing_context(struct cli_state *cli)
340 return;
344 SMB signing - NULL implementation - setup the MAC key.
346 @note Used as an initialisation only - it will not correctly
347 shut down a real signing mechanism
350 BOOL cli_null_set_signing(struct cli_state *cli)
352 cli->sign_info.signing_context = NULL;
354 cli->sign_info.sign_outgoing_message = cli_null_sign_outgoing_message;
355 cli->sign_info.check_incoming_message = cli_null_check_incoming_message;
356 cli->sign_info.free_signing_context = cli_null_free_signing_context;
358 return True;
361 /***********************************************************
362 SMB signing - TEMP implementation - calculate a MAC to send.
363 ************************************************************/
365 static void cli_temp_sign_outgoing_message(struct cli_state *cli)
367 /* mark the packet as signed - BEFORE we sign it...*/
368 cli_mark_packet_signed(cli);
370 /* I wonder what BSRSPYL stands for - but this is what MS
371 actually sends! */
372 memcpy(&cli->outbuf[smb_ss_field], "BSRSPYL ", 8);
373 return;
376 /***********************************************************
377 SMB signing - TEMP implementation - check a MAC sent by server.
378 ************************************************************/
380 static BOOL cli_temp_check_incoming_message(struct cli_state *cli)
382 return True;
385 /***********************************************************
386 SMB signing - TEMP implementation - free signing context
387 ************************************************************/
389 static void cli_temp_free_signing_context(struct cli_state *cli)
391 return;
394 /***********************************************************
395 SMB signing - NULL implementation - setup the MAC key.
396 ************************************************************/
398 BOOL cli_temp_set_signing(struct cli_state *cli)
400 if (!cli_set_smb_signing_common(cli)) {
401 return False;
404 cli->sign_info.signing_context = NULL;
406 cli->sign_info.sign_outgoing_message = cli_temp_sign_outgoing_message;
407 cli->sign_info.check_incoming_message = cli_temp_check_incoming_message;
408 cli->sign_info.free_signing_context = cli_temp_free_signing_context;
410 return True;
414 * Free the signing context
417 void cli_free_signing_context(struct cli_state *cli)
419 if (cli->sign_info.free_signing_context)
420 cli->sign_info.free_signing_context(cli);
422 cli_null_set_signing(cli);
426 * Sign a packet with the current mechanism
429 void cli_caclulate_sign_mac(struct cli_state *cli)
431 cli->sign_info.sign_outgoing_message(cli);
435 * Check a packet with the current mechanism
436 * @return False if we had an established signing connection
437 * which had a back checksum, True otherwise
440 BOOL cli_check_sign_mac(struct cli_state *cli)
442 BOOL good;
444 if (smb_len(cli->inbuf) < (smb_ss_field + 8 - 4)) {
445 DEBUG(cli->sign_info.doing_signing ? 1 : 10, ("Can't check signature on short packet! smb_len = %u\n", smb_len(cli->inbuf)));
446 good = False;
447 } else {
448 good = cli->sign_info.check_incoming_message(cli);
451 if (!good) {
452 if (cli->sign_info.doing_signing) {
453 return False;
454 } else {
455 cli_free_signing_context(cli);
459 return True;