Refactor signing code to remove most dependencies on 'struct cli'.
[jra/samba/.git] / source3 / 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->sign_info);
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 set_smb_signing_real_common(struct smb_sign_info *si)
98 {
99         if (si->mandatory_signing) {
100                 DEBUG(5, ("Mandatory SMB signing enabled!\n"));
101                 si->doing_signing = True;
102         }
103
104         DEBUG(5, ("SMB signing enabled!\n"));
105
106         return True;
107 }
108
109 static void mark_packet_signed(char *outbuf)
110 {
111         uint16 flags2;
112         flags2 = SVAL(outbuf,smb_flg2);
113         flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
114         SSVAL(outbuf,smb_flg2, flags2);
115 }
116
117 /***********************************************************
118  SMB signing - NULL implementation - calculate a MAC to send.
119 ************************************************************/
120
121 static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
122 {
123         /* we can't zero out the sig, as we might be trying to send a
124            session request - which is NBT-level, not SMB level and doesn't
125            have the field */
126         return;
127 }
128
129 /***********************************************************
130  SMB signing - NULL implementation - check a MAC sent by server.
131 ************************************************************/
132
133 static BOOL null_check_incoming_message(char *inbuf, struct smb_sign_info *si)
134 {
135         return True;
136 }
137
138 /***********************************************************
139  SMB signing - NULL implementation - free signing context
140 ************************************************************/
141
142 static void null_free_signing_context(struct smb_sign_info *si)
143 {
144         return;
145 }
146
147 /**
148  SMB signing - NULL implementation - setup the MAC key.
149
150  @note Used as an initialisation only - it will not correctly
151        shut down a real signing mechanism
152 */
153
154 static BOOL null_set_signing(struct smb_sign_info *si)
155 {
156         si->signing_context = NULL;
157         
158         si->sign_outgoing_message = null_sign_outgoing_message;
159         si->check_incoming_message = null_check_incoming_message;
160         si->free_signing_context = null_free_signing_context;
161
162         return True;
163 }
164
165 /**
166  * Free the signing context
167  */
168  
169 static void free_signing_context(struct smb_sign_info *si)
170 {
171         if (si->free_signing_context) {
172                 si->free_signing_context(si);
173                 si->signing_context = NULL;
174         }
175
176         null_set_signing(si);
177 }
178
179
180 static BOOL signing_good(char *inbuf, struct smb_sign_info *si, BOOL good) 
181 {
182         DEBUG(10, ("got SMB signature of\n"));
183         dump_data(10,&inbuf[smb_ss_field] , 8);
184
185         if (good && !si->doing_signing) {
186                 si->doing_signing = True;
187         }
188
189         if (!good) {
190                 if (si->doing_signing) {
191                         DEBUG(1, ("SMB signature check failed!\n"));
192                         return False;
193                 } else {
194                         DEBUG(3, ("Server did not sign reply correctly\n"));
195                         free_signing_context(si);
196                         return False;
197                 }
198         }
199         return True;
200 }       
201
202 /***********************************************************
203  SMB signing - Simple implementation - calculate a MAC on the packet
204 ************************************************************/
205
206 static void simple_packet_signature(struct smb_basic_signing_context *data, 
207                                     const uchar *buf, uint32 seq_number, 
208                                     unsigned char calc_md5_mac[16])
209 {
210         const size_t offset_end_of_sig = (smb_ss_field + 8);
211         unsigned char sequence_buf[8];
212         struct MD5Context md5_ctx;
213
214         /*
215          * Firstly put the sequence number into the first 4 bytes.
216          * and zero out the next 4 bytes.
217          *
218          * We do this here, to avoid modifying the packet.
219          */
220
221         SIVAL(sequence_buf, 0, seq_number);
222         SIVAL(sequence_buf, 4, 0);
223
224         /* Calculate the 16 byte MAC - but don't alter the data in the
225            incoming packet.
226            
227            This makes for a bit for fussing about, but it's not too bad.
228         */
229         MD5Init(&md5_ctx);
230
231         /* intialise with the key */
232         MD5Update(&md5_ctx, data->mac_key.data, 
233                   data->mac_key.length); 
234
235         /* copy in the first bit of the SMB header */
236         MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
237
238         /* copy in the sequence number, instead of the signature */
239         MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
240
241         /* copy in the rest of the packet in, skipping the signature */
242         MD5Update(&md5_ctx, buf + offset_end_of_sig, 
243                   smb_len(buf) - (offset_end_of_sig - 4));
244
245         /* calculate the MD5 sig */ 
246         MD5Final(calc_md5_mac, &md5_ctx);
247 }
248
249
250 /***********************************************************
251  SMB signing - Simple implementation - send the MAC.
252 ************************************************************/
253
254 static void cli_simple_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
255 {
256         unsigned char calc_md5_mac[16];
257         struct smb_basic_signing_context *data = si->signing_context;
258
259         /* mark the packet as signed - BEFORE we sign it...*/
260         mark_packet_signed(outbuf);
261
262         simple_packet_signature(data, outbuf, data->send_seq_num, calc_md5_mac);
263
264         DEBUG(10, ("sent SMB signature of\n"));
265         dump_data(10, calc_md5_mac, 8);
266
267         memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
268
269 /*      cli->outbuf[smb_ss_field+2]=0; 
270         Uncomment this to test if the remote server actually verifies signatures...*/
271
272         data->send_seq_num++;
273         store_sequence_for_reply(&data->outstanding_packet_list, 
274                                  SVAL(outbuf,smb_mid),
275                                  data->send_seq_num);
276         data->send_seq_num++;
277 }
278
279 /***********************************************************
280  SMB signing - Simple implementation - check a MAC sent by server.
281 ************************************************************/
282
283 static BOOL cli_simple_check_incoming_message(char *inbuf, struct smb_sign_info *si)
284 {
285         BOOL good;
286         uint32 reply_seq_number;
287         unsigned char calc_md5_mac[16];
288         unsigned char *server_sent_mac;
289
290         struct smb_basic_signing_context *data = si->signing_context;
291
292         if (!get_sequence_for_reply(&data->outstanding_packet_list, 
293                                     SVAL(inbuf, smb_mid), 
294                                     &reply_seq_number)) {
295                 return False;
296         }
297
298         simple_packet_signature(data, inbuf, reply_seq_number, calc_md5_mac);
299
300         server_sent_mac = &inbuf[smb_ss_field];
301         good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
302         
303         if (!good) {
304                 DEBUG(5, ("BAD SIG: wanted SMB signature of\n"));
305                 dump_data(5, calc_md5_mac, 8);
306                 
307                 DEBUG(5, ("BAD SIG: got SMB signature of\n"));
308                 dump_data(5, server_sent_mac, 8);
309         }
310         return signing_good(inbuf, si, good);
311 }
312
313 /***********************************************************
314  SMB signing - Simple implementation - free signing context
315 ************************************************************/
316
317 static void cli_simple_free_signing_context(struct smb_sign_info *si)
318 {
319         struct smb_basic_signing_context *data = si->signing_context;
320         struct outstanding_packet_lookup *list = data->outstanding_packet_list;
321         
322         while (list) {
323                 struct outstanding_packet_lookup *old_head = list;
324                 DLIST_REMOVE(list, list);
325                 SAFE_FREE(old_head);
326         }
327
328         data_blob_free(&data->mac_key);
329         SAFE_FREE(si->signing_context);
330
331         return;
332 }
333
334 /***********************************************************
335  SMB signing - Simple implementation - setup the MAC key.
336 ************************************************************/
337
338 BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
339 {
340         struct smb_basic_signing_context *data;
341
342         if (!user_session_key)
343                 return False;
344
345         if (!cli_set_smb_signing_common(cli)) {
346                 return False;
347         }
348
349         if (!set_smb_signing_real_common(&cli->sign_info)) {
350                 return False;
351         }
352
353         data = smb_xmalloc(sizeof(*data));
354
355         cli->sign_info.signing_context = data;
356         
357         data->mac_key = data_blob(NULL, response.length + 16);
358
359         memcpy(&data->mac_key.data[0], user_session_key, 16);
360         memcpy(&data->mac_key.data[16],response.data, response.length);
361
362         /* Initialise the sequence number */
363         data->send_seq_num = 0;
364
365         /* Initialise the list of outstanding packets */
366         data->outstanding_packet_list = NULL;
367
368         cli->sign_info.sign_outgoing_message = cli_simple_sign_outgoing_message;
369         cli->sign_info.check_incoming_message = cli_simple_check_incoming_message;
370         cli->sign_info.free_signing_context = cli_simple_free_signing_context;
371
372         return True;
373 }
374
375 /***********************************************************
376  SMB signing - TEMP implementation - calculate a MAC to send.
377 ************************************************************/
378
379 static void cli_temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
380 {
381         /* mark the packet as signed - BEFORE we sign it...*/
382         mark_packet_signed(outbuf);
383
384         /* I wonder what BSRSPYL stands for - but this is what MS 
385            actually sends! */
386         memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8);
387         return;
388 }
389
390 /***********************************************************
391  SMB signing - TEMP implementation - check a MAC sent by server.
392 ************************************************************/
393
394 static BOOL cli_temp_check_incoming_message(char *inbuf, struct smb_sign_info *si)
395 {
396         return True;
397 }
398
399 /***********************************************************
400  SMB signing - TEMP implementation - free signing context
401 ************************************************************/
402
403 static void cli_temp_free_signing_context(struct smb_sign_info *si)
404 {
405         return;
406 }
407
408 /***********************************************************
409  SMB signing - NULL implementation - setup the MAC key.
410 ************************************************************/
411
412 BOOL cli_null_set_signing(struct cli_state *cli)
413 {
414         return null_set_signing(&cli->sign_info);
415 }
416
417 /***********************************************************
418  SMB signing - temp implementation - setup the MAC key.
419 ************************************************************/
420
421 BOOL cli_temp_set_signing(struct cli_state *cli)
422 {
423         if (!cli_set_smb_signing_common(cli)) {
424                 return False;
425         }
426
427         cli->sign_info.signing_context = NULL;
428         
429         cli->sign_info.sign_outgoing_message = cli_temp_sign_outgoing_message;
430         cli->sign_info.check_incoming_message = cli_temp_check_incoming_message;
431         cli->sign_info.free_signing_context = cli_temp_free_signing_context;
432
433         return True;
434 }
435
436 void cli_free_signing_context(struct cli_state *cli)
437 {
438         free_signing_context(&cli->sign_info);
439 }
440
441 /**
442  * Sign a packet with the current mechanism
443  */
444  
445 void cli_calculate_sign_mac(struct cli_state *cli)
446 {
447         cli->sign_info.sign_outgoing_message(cli->outbuf, &cli->sign_info);
448 }
449
450 /**
451  * Check a packet with the current mechanism
452  * @return False if we had an established signing connection
453  *         which had a back checksum, True otherwise
454  */
455  
456 BOOL cli_check_sign_mac(struct cli_state *cli) 
457 {
458         BOOL good;
459
460         if (smb_len(cli->inbuf) < (smb_ss_field + 8 - 4)) {
461                 DEBUG(cli->sign_info.doing_signing ? 1 : 10, ("Can't check signature on short packet! smb_len = %u\n", smb_len(cli->inbuf)));
462                 good = False;
463         } else {
464                 good = cli->sign_info.check_incoming_message(cli->inbuf, &cli->sign_info);
465         }
466
467         if (!good) {
468                 if (cli->sign_info.doing_signing) {
469                         return False;
470                 } else {
471                         free_signing_context(&cli->sign_info);  
472                 }
473         }
474
475         return True;
476 }
477
478 /***********************************************************
479  SMB signing - server API's.
480 ************************************************************/
481
482 void srv_enable_signing(void)
483 {
484 }
485
486 void srv_disable_signing(void)
487 {
488 }
489
490 BOOL srv_check_sign_mac(char *buf)
491 {
492         return True;
493 }
494
495 void srv_calculate_sign_mac(char *buf)
496 {
497 }
498
499 BOOL allow_sendfile(void)
500 {
501         return True;
502 }