trying to get HEAD building again. If you want the code
[kai/samba-autobuild/.git] / source / libsmb / smb_signing.c
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
6    
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.
11    
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.
16    
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.
20 */
21
22 #include "includes.h"
23
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;
29 };
30
31 struct smb_basic_signing_context {
32         DATA_BLOB mac_key;
33         uint32 send_seq_num;
34         struct outstanding_packet_lookup *outstanding_packet_list;
35 };
36
37 static void store_sequence_for_reply(struct outstanding_packet_lookup **list, 
38                                      uint16 mid, uint32 reply_seq_num) 
39 {
40         struct outstanding_packet_lookup *t;
41         struct outstanding_packet_lookup *tmp;
42         
43         t = smb_xmalloc(sizeof(*t));
44         ZERO_STRUCTP(t);
45
46         DLIST_ADD_END(*list, t, tmp);
47         t->mid = mid;
48         t->reply_seq_num = reply_seq_num;
49 }
50
51 static BOOL get_sequence_for_reply(struct outstanding_packet_lookup **list,
52                                    uint16 mid, uint32 *reply_seq_num) 
53 {
54         struct outstanding_packet_lookup *t;
55
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;
61                 }
62         }
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;
66 }
67
68 /***********************************************************
69  SMB signing - Common code before we set a new signing implementation
70 ************************************************************/
71
72 static BOOL cli_set_smb_signing_common(struct cli_state *cli) 
73 {
74         if (!cli->sign_info.negotiated_smb_signing 
75             && !cli->sign_info.mandatory_signing) {
76                 return False;
77         }
78
79         if (cli->sign_info.doing_signing) {
80                 return False;
81         }
82         
83         if (cli->sign_info.free_signing_context)
84                 cli->sign_info.free_signing_context(cli);
85
86         /* These calls are INCOMPATIBLE with SMB signing */
87         cli->readbraw_supported = False;
88         cli->writebraw_supported = False;
89         
90         return True;
91 }
92
93 /***********************************************************
94  SMB signing - Common code for 'real' implementations
95 ************************************************************/
96
97 static BOOL cli_set_smb_signing_real_common(struct cli_state *cli) 
98 {
99         if (cli->sign_info.mandatory_signing) {
100                 DEBUG(5, ("Mandatory SMB signing enabled!\n"));
101                 cli->sign_info.doing_signing = True;
102         }
103
104         DEBUG(5, ("SMB signing enabled!\n"));
105
106         return True;
107 }
108
109 static void cli_mark_packet_signed(struct cli_state *cli) 
110 {
111         uint16 flags2;
112         flags2 = SVAL(cli->outbuf,smb_flg2);
113         flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
114         SSVAL(cli->outbuf,smb_flg2, flags2);
115 }
116
117 static BOOL cli_signing_good(struct cli_state *cli, BOOL good) 
118 {
119         DEBUG(10, ("got SMB signature of\n"));
120         dump_data(10,&cli->inbuf[smb_ss_field] , 8);
121
122         if (good && !cli->sign_info.doing_signing) {
123                 cli->sign_info.doing_signing = True;
124         }
125
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;
134                 }
135         }
136         return True;
137 }       
138
139 /***********************************************************
140  SMB signing - Simple implementation - calculate a MAC on the packet
141 ************************************************************/
142
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])
146 {
147         const size_t offset_end_of_sig = (smb_ss_field + 8);
148         unsigned char sequence_buf[8];
149         struct MD5Context md5_ctx;
150
151         /*
152          * Firstly put the sequence number into the first 4 bytes.
153          * and zero out the next 4 bytes.
154          *
155          * We do this here, to avoid modifying the packet.
156          */
157
158         SIVAL(sequence_buf, 0, seq_number);
159         SIVAL(sequence_buf, 4, 0);
160
161         /* Calculate the 16 byte MAC - but don't alter the data in the
162            incoming packet.
163            
164            This makes for a bit for fussing about, but it's not too bad.
165         */
166         MD5Init(&md5_ctx);
167
168         /* intialise with the key */
169         MD5Update(&md5_ctx, data->mac_key.data, 
170                   data->mac_key.length); 
171
172         /* copy in the first bit of the SMB header */
173         MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
174
175         /* copy in the sequence number, instead of the signature */
176         MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
177
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));
181
182         /* caclulate the MD5 sig */ 
183         MD5Final(calc_md5_mac, &md5_ctx);
184 }
185
186
187 /***********************************************************
188  SMB signing - Simple implementation - send the MAC.
189 ************************************************************/
190
191 static void cli_simple_sign_outgoing_message(struct cli_state *cli)
192 {
193         unsigned char calc_md5_mac[16];
194         struct smb_basic_signing_context *data = cli->sign_info.signing_context;
195
196         /* mark the packet as signed - BEFORE we sign it...*/
197         cli_mark_packet_signed(cli);
198
199         simple_packet_signature(data, cli->outbuf, data->send_seq_num, 
200                                 calc_md5_mac);
201
202         DEBUG(10, ("sent SMB signature of\n"));
203         dump_data(10, calc_md5_mac, 8);
204
205         memcpy(&cli->outbuf[smb_ss_field], calc_md5_mac, 8);
206
207 /*      cli->outbuf[smb_ss_field+2]=0; 
208         Uncomment this to test if the remote server actually verifies signatures...*/
209
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++;
215 }
216
217 /***********************************************************
218  SMB signing - Simple implementation - check a MAC sent by server.
219 ************************************************************/
220
221 static BOOL cli_simple_check_incoming_message(struct cli_state *cli)
222 {
223         BOOL good;
224         uint32 reply_seq_number;
225         unsigned char calc_md5_mac[16];
226         unsigned char *server_sent_mac;
227
228         struct smb_basic_signing_context *data = cli->sign_info.signing_context;
229
230         if (!get_sequence_for_reply(&data->outstanding_packet_list, 
231                                     SVAL(cli->inbuf, smb_mid), 
232                                     &reply_seq_number)) {
233                 return False;
234         }
235
236         simple_packet_signature(data, cli->inbuf, reply_seq_number, calc_md5_mac);
237
238         server_sent_mac = &cli->inbuf[smb_ss_field];
239         good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
240         
241         if (!good) {
242                 DEBUG(5, ("BAD SIG: wanted SMB signature of\n"));
243                 dump_data(5, calc_md5_mac, 8);
244                 
245                 DEBUG(5, ("BAD SIG: got SMB signature of\n"));
246                 dump_data(5, server_sent_mac, 8);
247         }
248         return cli_signing_good(cli, good);
249 }
250
251 /***********************************************************
252  SMB signing - Simple implementation - free signing context
253 ************************************************************/
254
255 static void cli_simple_free_signing_context(struct cli_state *cli)
256 {
257         struct smb_basic_signing_context *data = cli->sign_info.signing_context;
258         struct outstanding_packet_lookup *list = data->outstanding_packet_list;
259         
260         while (list) {
261                 struct outstanding_packet_lookup *old_head = list;
262                 DLIST_REMOVE(list, list);
263                 SAFE_FREE(old_head);
264         }
265
266         data_blob_free(&data->mac_key);
267         SAFE_FREE(cli->sign_info.signing_context);
268
269         return;
270 }
271
272 /***********************************************************
273  SMB signing - Simple implementation - setup the MAC key.
274 ************************************************************/
275
276 BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
277 {
278         struct smb_basic_signing_context *data;
279
280         if (!user_session_key)
281                 return False;
282
283         if (!cli_set_smb_signing_common(cli)) {
284                 return False;
285         }
286
287         if (!cli_set_smb_signing_real_common(cli)) {
288                 return False;
289         }
290
291         data = smb_xmalloc(sizeof(*data));
292
293         cli->sign_info.signing_context = data;
294         
295         data->mac_key = data_blob(NULL, response.length + 16);
296
297         memcpy(&data->mac_key.data[0], user_session_key, 16);
298         memcpy(&data->mac_key.data[16],response.data, response.length);
299
300         /* Initialise the sequence number */
301         data->send_seq_num = 0;
302
303         /* Initialise the list of outstanding packets */
304         data->outstanding_packet_list = NULL;
305
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;
309
310         return True;
311 }
312
313 /***********************************************************
314  SMB signing - NULL implementation - calculate a MAC to send.
315 ************************************************************/
316
317 static void cli_null_sign_outgoing_message(struct cli_state *cli)
318 {
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;
323 }
324
325 /***********************************************************
326  SMB signing - NULL implementation - check a MAC sent by server.
327 ************************************************************/
328
329 static BOOL cli_null_check_incoming_message(struct cli_state *cli)
330 {
331         return True;
332 }
333
334 /***********************************************************
335  SMB signing - NULL implementation - free signing context
336 ************************************************************/
337
338 static void cli_null_free_signing_context(struct cli_state *cli)
339 {
340         return;
341 }
342
343 /**
344  SMB signing - NULL implementation - setup the MAC key.
345
346  @note Used as an initialisation only - it will not correctly
347        shut down a real signing mechanism
348 */
349
350 BOOL cli_null_set_signing(struct cli_state *cli)
351 {
352         cli->sign_info.signing_context = NULL;
353         
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;
357
358         return True;
359 }
360
361 /***********************************************************
362  SMB signing - TEMP implementation - calculate a MAC to send.
363 ************************************************************/
364
365 static void cli_temp_sign_outgoing_message(struct cli_state *cli)
366 {
367         /* mark the packet as signed - BEFORE we sign it...*/
368         cli_mark_packet_signed(cli);
369
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;
374 }
375
376 /***********************************************************
377  SMB signing - TEMP implementation - check a MAC sent by server.
378 ************************************************************/
379
380 static BOOL cli_temp_check_incoming_message(struct cli_state *cli)
381 {
382         return True;
383 }
384
385 /***********************************************************
386  SMB signing - TEMP implementation - free signing context
387 ************************************************************/
388
389 static void cli_temp_free_signing_context(struct cli_state *cli)
390 {
391         return;
392 }
393
394 /***********************************************************
395  SMB signing - NULL implementation - setup the MAC key.
396 ************************************************************/
397
398 BOOL cli_temp_set_signing(struct cli_state *cli)
399 {
400         if (!cli_set_smb_signing_common(cli)) {
401                 return False;
402         }
403
404         cli->sign_info.signing_context = NULL;
405         
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;
409
410         return True;
411 }
412
413 /**
414  * Free the signing context
415  */
416  
417 void cli_free_signing_context(struct cli_state *cli) 
418 {
419         if (cli->sign_info.free_signing_context) 
420                 cli->sign_info.free_signing_context(cli);
421
422         cli_null_set_signing(cli);
423 }
424
425 /**
426  * Sign a packet with the current mechanism
427  */
428  
429 void cli_caclulate_sign_mac(struct cli_state *cli)
430 {
431         cli->sign_info.sign_outgoing_message(cli);
432 }
433
434 /**
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
438  */
439  
440 BOOL cli_check_sign_mac(struct cli_state *cli) 
441 {
442         BOOL good;
443
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);
449         }
450
451         if (!good) {
452                 if (cli->sign_info.doing_signing) {
453                         return False;
454                 } else {
455                         cli_free_signing_context(cli);  
456                 }
457         }
458
459         return True;
460 }