Put the value(s) of a parameter into the top-level item for that
[obnox/wireshark/wip.git] / packet-tacacs.c
1 /* packet-tacacs.c
2  * Routines for cisco tacacs/xtacacs/tacacs+ packet dissection
3  * Copyright 2001, Paul Ionescu <paul@acorp.ro>
4  * 
5  * Full Tacacs+ parsing with decryption by
6  *   Emanuele Caratti <wiz@iol.it>
7  *
8  * $Id: packet-tacacs.c,v 1.32 2003/12/19 19:03:13 guy Exp $
9  *
10  * Ethereal - Network traffic analyzer
11  * By Gerald Combs <gerald@ethereal.com>
12  * Copyright 1998 Gerald Combs
13  *
14  * Copied from old packet-tacacs.c
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31
32 /* rfc-1492 for tacacs and xtacacs
33  * draft-grant-tacacs-02.txt for tacacs+ (tacplus)
34  */
35
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39
40 #include <stdio.h>
41
42 #include <string.h>
43 #include <glib.h>
44 #include <epan/packet.h>
45
46 #include "prefs.h"
47 #include "crypt-md5.h"
48 #include "packet-tacacs.h"
49
50 static void md5_xor( guint8 *data, char *key, int data_len, guint8 *session_id, guint8 version, guint8 seq_no );
51
52 static int proto_tacacs = -1;
53 static int hf_tacacs_version = -1;
54 static int hf_tacacs_type = -1;
55 static int hf_tacacs_nonce = -1;
56 static int hf_tacacs_userlen = -1;
57 static int hf_tacacs_passlen = -1;
58 static int hf_tacacs_response = -1;
59 static int hf_tacacs_reason = -1;
60 static int hf_tacacs_result1 = -1;
61 static int hf_tacacs_destaddr = -1;
62 static int hf_tacacs_destport = -1;
63 static int hf_tacacs_line = -1;
64 static int hf_tacacs_result2 = -1;
65 static int hf_tacacs_result3 = -1;
66
67 static gint ett_tacacs = -1;
68
69 static char *tacplus_opt_key;
70 static GSList *tacplus_keys = NULL;
71
72 #define VERSION_TACACS  0x00
73 #define VERSION_XTACACS 0x80
74
75 static const value_string tacacs_version_vals[] = {
76         { VERSION_TACACS,  "TACACS" },
77         { VERSION_XTACACS, "XTACACS" },
78         { 0,               NULL }
79 };
80
81 #define TACACS_LOGIN            1
82 #define TACACS_RESPONSE         2
83 #define TACACS_CHANGE           3
84 #define TACACS_FOLLOW           4
85 #define TACACS_CONNECT          5
86 #define TACACS_SUPERUSER        6
87 #define TACACS_LOGOUT           7
88 #define TACACS_RELOAD           8
89 #define TACACS_SLIP_ON          9
90 #define TACACS_SLIP_OFF         10
91 #define TACACS_SLIP_ADDR        11
92 static const value_string tacacs_type_vals[] = {
93         { TACACS_LOGIN,     "Login" },
94         { TACACS_RESPONSE,  "Response" },
95         { TACACS_CHANGE,    "Change" },
96         { TACACS_FOLLOW,    "Follow" },
97         { TACACS_CONNECT,   "Connect" },
98         { TACACS_SUPERUSER, "Superuser" },
99         { TACACS_LOGOUT,    "Logout" },
100         { TACACS_RELOAD,    "Reload" },
101         { TACACS_SLIP_ON,   "SLIP on" },
102         { TACACS_SLIP_OFF,  "SLIP off" },
103         { TACACS_SLIP_ADDR, "SLIP Addr" },
104         { 0,                NULL }};
105
106 static const value_string tacacs_reason_vals[] = {
107         { 0  , "none" },
108         { 1  , "expiring" },
109         { 2  , "password" },
110         { 3  , "denied" },
111         { 4  , "quit" },
112         { 5  , "idle" },
113         { 6  , "drop" },
114         { 7  , "bad" },
115         { 0  , NULL }
116 };
117
118 static const value_string tacacs_resp_vals[] = {
119         { 0  , "this is not a response" },
120         { 1  , "accepted" },
121         { 2  , "rejected" },
122         { 0  , NULL }
123 };
124
125 #define UDP_PORT_TACACS 49
126 #define TCP_PORT_TACACS 49
127
128 static void
129 dissect_tacacs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
130 {
131         proto_tree      *tacacs_tree;
132         proto_item      *ti;
133         guint8          txt_buff[255+1],version,type,userlen,passlen;
134
135         if (check_col(pinfo->cinfo, COL_PROTOCOL))
136                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TACACS");
137         if (check_col(pinfo->cinfo, COL_INFO))
138                 col_clear(pinfo->cinfo, COL_INFO);
139
140         version = tvb_get_guint8(tvb,0);
141         if (version != 0) {
142                 if (check_col(pinfo->cinfo, COL_PROTOCOL))
143                         col_set_str(pinfo->cinfo, COL_PROTOCOL, "XTACACS");
144         }
145
146         type = tvb_get_guint8(tvb,1);
147         if (check_col(pinfo->cinfo, COL_INFO))
148                 col_add_str(pinfo->cinfo, COL_INFO,
149                     val_to_str(type, tacacs_type_vals, "Unknown (0x%02x)"));
150
151         if (tree)
152         {
153                 ti = proto_tree_add_protocol_format(tree, proto_tacacs,
154                  tvb, 0, -1, version==0?"TACACS":"XTACACS");
155                 tacacs_tree = proto_item_add_subtree(ti, ett_tacacs);
156
157                 proto_tree_add_uint(tacacs_tree, hf_tacacs_version, tvb, 0, 1,
158                     version);
159                 proto_tree_add_uint(tacacs_tree, hf_tacacs_type, tvb, 1, 1,
160                     type);
161                 proto_tree_add_item(tacacs_tree, hf_tacacs_nonce, tvb, 2, 2,
162                     FALSE);
163
164         if (version==0)
165             {
166             if (type!=TACACS_RESPONSE)
167                 {
168                 userlen=tvb_get_guint8(tvb,4);
169                 proto_tree_add_uint(tacacs_tree, hf_tacacs_userlen, tvb, 4, 1,
170                     userlen);
171                 passlen=tvb_get_guint8(tvb,5);
172                 proto_tree_add_uint(tacacs_tree, hf_tacacs_passlen, tvb, 5, 1,
173                     passlen);
174                 tvb_get_nstringz0(tvb,6,userlen+1,txt_buff);
175                 proto_tree_add_text(tacacs_tree, tvb, 6, userlen,         "Username: %s",txt_buff);
176                 tvb_get_nstringz0(tvb,6+userlen,passlen+1,txt_buff);
177                 proto_tree_add_text(tacacs_tree, tvb, 6+userlen, passlen, "Password: %s",txt_buff);
178                 }
179             else
180                 {
181                 proto_tree_add_item(tacacs_tree, hf_tacacs_response, tvb, 4, 1,
182                     FALSE);
183                 proto_tree_add_item(tacacs_tree, hf_tacacs_reason, tvb, 5, 1,
184                     FALSE);
185                 }
186             }
187         else
188             {
189             userlen=tvb_get_guint8(tvb,4);
190             proto_tree_add_uint(tacacs_tree, hf_tacacs_userlen, tvb, 4, 1,
191                 userlen);
192             passlen=tvb_get_guint8(tvb,5);
193             proto_tree_add_uint(tacacs_tree, hf_tacacs_passlen, tvb, 5, 1,
194                 passlen);
195             proto_tree_add_item(tacacs_tree, hf_tacacs_response, tvb, 6, 1,
196                 FALSE);
197             proto_tree_add_item(tacacs_tree, hf_tacacs_reason, tvb, 7, 1,
198                 FALSE);
199             proto_tree_add_item(tacacs_tree, hf_tacacs_result1, tvb, 8, 4,
200                 FALSE);
201             proto_tree_add_item(tacacs_tree, hf_tacacs_destaddr, tvb, 12, 4,
202                 FALSE);
203             proto_tree_add_item(tacacs_tree, hf_tacacs_destport, tvb, 16, 2,
204                 FALSE);
205             proto_tree_add_item(tacacs_tree, hf_tacacs_line, tvb, 18, 2,
206                 FALSE);
207             proto_tree_add_item(tacacs_tree, hf_tacacs_result2, tvb, 20, 4,
208                 FALSE);
209             proto_tree_add_item(tacacs_tree, hf_tacacs_result3, tvb, 24, 2,
210                 FALSE);
211             if (type!=TACACS_RESPONSE)
212                 {
213                 tvb_get_nstringz0(tvb,26,userlen+1,txt_buff);
214                 proto_tree_add_text(tacacs_tree, tvb, 26, userlen,  "Username: %s",txt_buff);
215                 tvb_get_nstringz0(tvb,26+userlen,passlen+1,txt_buff);
216                 proto_tree_add_text(tacacs_tree, tvb, 26+userlen, passlen, "Password; %s",txt_buff);
217                 }
218             }
219         }
220 }
221
222 void
223 proto_register_tacacs(void)
224 {
225         static hf_register_info hf[] = {
226           { &hf_tacacs_version,
227             { "Version",           "tacacs.version",
228               FT_UINT8, BASE_HEX, VALS(tacacs_version_vals), 0x0,
229               "Version", HFILL }},
230           { &hf_tacacs_type,
231             { "Type",              "tacacs.type",
232               FT_UINT8, BASE_DEC, VALS(tacacs_type_vals), 0x0,
233               "Type", HFILL }},
234           { &hf_tacacs_nonce,
235             { "Nonce",             "tacacs.nonce",
236               FT_UINT16, BASE_HEX, NULL, 0x0,
237               "Nonce", HFILL }},
238           { &hf_tacacs_userlen,
239             { "Username length",   "tacacs.userlen",
240               FT_UINT8, BASE_DEC, NULL, 0x0,
241               "Username length", HFILL }},
242           { &hf_tacacs_passlen,
243             { "Password length",   "tacacs.passlen",
244               FT_UINT8, BASE_DEC, NULL, 0x0,
245               "Password length", HFILL }},
246           { &hf_tacacs_response,
247             { "Response",          "tacacs.response",
248               FT_UINT8, BASE_DEC, VALS(tacacs_resp_vals), 0x0,
249               "Response", HFILL }},
250           { &hf_tacacs_reason,
251             { "Reason",            "tacacs.reason",
252               FT_UINT8, BASE_DEC, VALS(tacacs_reason_vals), 0x0,
253               "Reason", HFILL }},
254           { &hf_tacacs_result1,
255             { "Result 1",          "tacacs.result1",
256               FT_UINT32, BASE_HEX, NULL, 0x0,
257               "Result 1", HFILL }},
258           { &hf_tacacs_destaddr,
259             { "Destination address", "tacacs.destaddr",
260               FT_IPv4, BASE_NONE, NULL, 0x0,
261               "Destination address", HFILL }},
262           { &hf_tacacs_destport,
263             { "Destination port",  "tacacs.destport",
264               FT_UINT16, BASE_DEC, NULL, 0x0,
265               "Destination port", HFILL }},
266           { &hf_tacacs_line,
267             { "Line",              "tacacs.line",
268               FT_UINT16, BASE_DEC, NULL, 0x0,
269               "Line", HFILL }},
270           { &hf_tacacs_result2,
271             { "Result 2",          "tacacs.result2",
272               FT_UINT32, BASE_HEX, NULL, 0x0,
273               "Result 2", HFILL }},
274           { &hf_tacacs_result3,
275             { "Result 3",          "tacacs.result3",
276               FT_UINT16, BASE_HEX, NULL, 0x0,
277               "Result 3", HFILL }},
278         };
279
280         static gint *ett[] = {
281                 &ett_tacacs,
282         };
283         proto_tacacs = proto_register_protocol("TACACS", "TACACS", "tacacs");
284         proto_register_field_array(proto_tacacs, hf, array_length(hf));
285         proto_register_subtree_array(ett, array_length(ett));
286 }
287
288 void
289 proto_reg_handoff_tacacs(void)
290 {
291         dissector_handle_t tacacs_handle;
292
293         tacacs_handle = create_dissector_handle(dissect_tacacs, proto_tacacs);
294         dissector_add("udp.port", UDP_PORT_TACACS, tacacs_handle);
295 }
296
297 static int proto_tacplus = -1;
298 static int hf_tacplus_response = -1;
299 static int hf_tacplus_request = -1;
300 static int hf_tacplus_majvers = -1;
301 static int hf_tacplus_minvers = -1;
302 static int hf_tacplus_type = -1;
303 static int hf_tacplus_seqno = -1;
304 static int hf_tacplus_flags = -1;
305 static int hf_tacplus_flags_payload_type = -1;
306 static int hf_tacplus_flags_connection_type = -1;
307 static int hf_tacplus_acct_flags = -1;
308 static int hf_tacplus_session_id = -1;
309 static int hf_tacplus_packet_len = -1;
310
311 static gint ett_tacplus = -1;
312 static gint ett_tacplus_body = -1;
313 static gint ett_tacplus_body_chap = -1;
314 static gint ett_tacplus_flags = -1;
315 static gint ett_tacplus_acct_flags = -1;
316
317 typedef struct _tacplus_key_entry {
318         address  *s; /* Server address */
319         address  *c; /* client address */
320         char    *k; /* Key */
321 } tacplus_key_entry;
322
323 static gint 
324 tacplus_decrypted_tvb_setup( tvbuff_t *tvb, tvbuff_t **dst_tvb, packet_info *pinfo, guint32 len, guint8 version, char *key )
325 {
326         guint8  *buff;
327         guint8 session_id[4];
328
329         /* TODO Check the possibility to use pinfo->decrypted_data */
330 /* session_id is in NETWORK Byte Order, and is used as byte array in the md5_xor */
331
332         tvb_memcpy(tvb, (guint8*)session_id, 4,4); 
333
334         buff = tvb_memdup(tvb, TAC_PLUS_HDR_SIZE, len);
335
336
337         md5_xor( buff, key, len, session_id,version, tvb_get_guint8(tvb,2) );
338
339         /* Allocate a new tvbuff, referring to the decrypted data. */
340         *dst_tvb = tvb_new_real_data( buff, len, len );
341
342         /* Arrange that the allocated packet data copy be freed when the
343            tvbuff is freed. */
344         tvb_set_free_cb( *dst_tvb, g_free );
345
346         /* Add the tvbuff to the list of tvbuffs to which the tvbuff we
347            were handed refers, so it'll get cleaned up when that tvbuff
348            is cleaned up. */
349         tvb_set_child_real_data_tvbuff( tvb, *dst_tvb );
350
351         /* Add the decrypted data to the data source list. */
352         add_new_data_source(pinfo, *dst_tvb, "TACACS+ Decrypted");
353
354         return 0;
355 }
356 static void
357 dissect_tacplus_args_list( tvbuff_t *tvb, proto_tree *tree, int data_off, int len_off, int arg_cnt )
358 {
359         int i;
360         guint8  buff[257];
361         for(i=0;i<arg_cnt;i++){
362                 int len=tvb_get_guint8(tvb,len_off+i);
363                 proto_tree_add_text( tree, tvb, len_off+i, 1, "Arg[%d] length: %d", i, len );
364                 tvb_get_nstringz0(tvb, data_off, len+1, buff);
365                 proto_tree_add_text( tree, tvb, data_off, len, "Arg[%d] value: %s", i, buff );
366                 data_off+=len;
367         }
368 }
369
370
371 static int
372 proto_tree_add_tacplus_common_fields( tvbuff_t *tvb, proto_tree *tree,  int offset, int var_off )
373 {
374         int val;
375         guint8 buff[257];
376         /* priv_lvl */
377         proto_tree_add_text( tree, tvb, offset, 1,
378                         "Privilege Level: %d", tvb_get_guint8(tvb,offset) );
379         offset++;
380
381         /* authen_type */
382         val=tvb_get_guint8(tvb,offset);
383         proto_tree_add_text( tree, tvb, offset, 1,
384                         "Authentication type: %s",
385                         val_to_str( val, tacplus_authen_type_vals, "Unknown Packet" ) );
386         offset++;
387
388         /* service */
389         val=tvb_get_guint8(tvb,offset);
390         proto_tree_add_text( tree, tvb, offset, 1,
391                         "Service: %s",
392                         val_to_str( val, tacplus_authen_service_vals, "Unknown Packet" ) );
393         offset++;
394
395         /* user_len && user */
396         val=tvb_get_guint8(tvb,offset);
397         proto_tree_add_text( tree, tvb, offset, 1, "User len: %d", val );
398         if( val ){
399                 tvb_get_nstringz0(tvb, var_off, val+1, buff);
400                 proto_tree_add_text( tree, tvb, var_off, val, "User: %s", buff );
401                 var_off+=val;
402         }
403         offset++;
404
405
406         /* port_len &&  port */
407         val=tvb_get_guint8(tvb,offset);
408         proto_tree_add_text( tree, tvb, offset, 1, "Port len: %d", val );
409         if( val ){
410                 tvb_get_nstringz0(tvb, var_off, val+1, buff);
411                 proto_tree_add_text( tree, tvb, var_off, val, "Port: %s", buff );
412                 var_off+=val;
413         }
414         offset++;
415
416         /* rem_addr_len && rem_addr */
417         val=tvb_get_guint8(tvb,offset);
418         proto_tree_add_text( tree, tvb, offset, 1, "Remaddr len: %d", val );
419         if( val ){
420                 tvb_get_nstringz0(tvb, var_off, val+1, buff);
421                 proto_tree_add_text( tree, tvb, var_off, val, "Remote Address: %s", buff );
422                 var_off+=val;
423         }
424         return var_off;
425 }
426
427 static void
428 dissect_tacplus_body_authen_req_login( tvbuff_t* tvb, proto_tree *tree, int var_off )
429 {
430         guint8 buff[257];
431         guint8 val;
432         val=tvb_get_guint8( tvb, AUTHEN_S_DATA_LEN_OFF );
433
434         switch ( tvb_get_guint8(tvb, AUTHEN_S_AUTHEN_TYPE_OFF ) ) { /* authen_type */
435
436                 case TAC_PLUS_AUTHEN_TYPE_ASCII:
437                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "Data: %d (not used)", val );
438                         if( val )
439                                 proto_tree_add_text( tree, tvb, var_off, val, "Data" );
440                         break;
441
442                 case TAC_PLUS_AUTHEN_TYPE_PAP:
443                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "Password Length %d", val );
444                         if( val ) {
445                                 tvb_get_nstringz0( tvb, var_off, val+1, buff );
446                                 proto_tree_add_text( tree, tvb, var_off, val, "Password: %s", buff );
447                         }
448                         break;
449
450                 case TAC_PLUS_AUTHEN_TYPE_CHAP:
451                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "CHAP Data Length %d", val );
452                         if( val ) {
453                                 proto_item      *pi;
454                                 proto_tree  *pt;
455                                 guint8 chal_len=val-(1+16); /* Response field alwayes 16 octets */
456                                 pi = proto_tree_add_text(tree, tvb, var_off, val, "CHAP Data" );
457                                 pt = proto_item_add_subtree( pi, ett_tacplus_body_chap );
458                                 val= tvb_get_guint8( tvb, var_off );
459                                 proto_tree_add_text( pt, tvb, var_off, 1, "ID: %d", val );
460                                 var_off++;
461                                 tvb_get_nstringz0( tvb, var_off, chal_len+1, buff );
462                                 proto_tree_add_text( pt, tvb, var_off, chal_len, "Challenge: %s", buff );
463                                 var_off+=chal_len;
464                                 tvb_get_nstringz0( tvb, var_off, 16+1, buff );
465                                 proto_tree_add_text( pt, tvb, var_off, 16 , "Response: %s", buff );
466                         }
467                         break;
468                 case TAC_PLUS_AUTHEN_TYPE_MSCHAP:
469                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "MSCHAP Data Length %d", val );
470                         if( val ) {
471                                 proto_item      *pi;
472                                 proto_tree  *pt;
473                                 guint8 chal_len=val-(1+49);  /* Response field alwayes 49 octets */
474                                 pi = proto_tree_add_text(tree, tvb, var_off, val, "MSCHAP Data" );
475                                 pt = proto_item_add_subtree( pi, ett_tacplus_body_chap );
476                                 val= tvb_get_guint8( tvb, var_off );
477                                 proto_tree_add_text( pt, tvb, var_off, 1, "ID: %d", val );
478                                 var_off++;
479                                 tvb_get_nstringz0( tvb, var_off, chal_len+1, buff );
480                                 proto_tree_add_text( pt, tvb, var_off, chal_len, "Challenge: %s", buff );
481                                 var_off+=chal_len;
482                                 tvb_get_nstringz0( tvb, var_off, 49+1, buff );
483                                 proto_tree_add_text( pt, tvb, var_off, 49 , "Response: %s", buff );
484                         }
485                         break;
486                 case TAC_PLUS_AUTHEN_TYPE_ARAP:
487                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "ARAP Data Length %d", val );
488                         if( val ) {
489                                 proto_item      *pi;
490                                 proto_tree  *pt;
491                                 pi = proto_tree_add_text(tree, tvb, var_off, val, "ARAP Data" );
492                                 pt = proto_item_add_subtree( pi, ett_tacplus_body_chap );
493
494                                 tvb_get_nstringz0( tvb, var_off, 8+1, buff );
495                                 proto_tree_add_text( pt, tvb, var_off, 8, "Nas Challenge: %s", buff );
496                                 var_off+=8;
497                                 tvb_get_nstringz0( tvb, var_off, 8+1, buff );
498                                 proto_tree_add_text( pt, tvb, var_off, 8, "Remote Challenge: %s", buff );
499                                 var_off+=8;
500                                 tvb_get_nstringz0( tvb, var_off, 8+1, buff );
501                                 proto_tree_add_text( pt, tvb, var_off, 8, "Remote Response: %s", buff );
502                                 var_off+=8;
503                         }
504                         break;
505
506                 default: /* Should not be reached */
507                         proto_tree_add_text( tree, tvb, AUTHEN_S_DATA_LEN_OFF, 1, "Data: %d", val );
508                         if( val ){
509                                 proto_tree_add_text( tree, tvb, var_off, val, "Data" );
510                         }
511         }
512 }
513
514 static void
515 dissect_tacplus_body_authen_req( tvbuff_t* tvb, proto_tree *tree )
516 {
517         guint8 val;
518         int var_off=AUTHEN_S_VARDATA_OFF;
519
520         /* Action */
521         val=tvb_get_guint8( tvb, AUTHEN_S_ACTION_OFF );
522         proto_tree_add_text( tree, tvb,
523                         AUTHEN_S_ACTION_OFF, 1, 
524                         "Action: %s", 
525                         val_to_str( val, tacplus_authen_action_vals, "Unknown Packet" ) );
526
527         var_off=proto_tree_add_tacplus_common_fields( tvb, tree , AUTHEN_S_PRIV_LVL_OFF, AUTHEN_S_VARDATA_OFF );
528
529         switch( val ) {
530                 case TAC_PLUS_AUTHEN_LOGIN:
531                         dissect_tacplus_body_authen_req_login( tvb, tree, var_off );
532                         break;
533                 case TAC_PLUS_AUTHEN_SENDAUTH:
534                         break;
535         }
536 }
537
538 static void
539 dissect_tacplus_body_authen_req_cont( tvbuff_t *tvb, proto_tree *tree )
540 {
541         int val;
542         int var_off=AUTHEN_C_VARDATA_OFF;
543         guint8 *buff=NULL;
544
545         val=tvb_get_guint8( tvb, AUTHEN_C_FLAGS_OFF );
546         proto_tree_add_text( tree, tvb,
547                         AUTHEN_R_FLAGS_OFF, 1, "Flags: 0x%02x %s",
548                         val,
549                         (val&TAC_PLUS_CONTINUE_FLAG_ABORT?"(Abort)":"") );
550
551
552         val=tvb_get_ntohs( tvb, AUTHEN_C_USER_LEN_OFF ); 
553         proto_tree_add_text( tree, tvb, AUTHEN_C_USER_LEN_OFF, 2 , "User length: %d", val );
554         if( val ){
555                 buff=tvb_get_string( tvb, var_off, val );
556                 proto_tree_add_text( tree, tvb, var_off, val, "User: %s", buff );
557                 var_off+=val;
558                 g_free(buff);
559         }
560
561         val=tvb_get_ntohs( tvb, AUTHEN_C_DATA_LEN_OFF ); 
562         proto_tree_add_text( tree, tvb, AUTHEN_C_DATA_LEN_OFF, 2 ,
563                         "Data length: %d", val );
564         if( val ){
565                 proto_tree_add_text( tree, tvb, var_off, val, "Data" );
566         }
567
568 }
569
570 /* Server REPLY */
571 static void
572 dissect_tacplus_body_authen_rep( tvbuff_t *tvb, proto_tree *tree )
573 {
574         int val;
575         int var_off=AUTHEN_R_VARDATA_OFF;
576         guint8 *buff=NULL;
577
578         val=tvb_get_guint8( tvb, AUTHEN_R_STATUS_OFF );
579         proto_tree_add_text(tree, tvb,
580                         AUTHEN_R_STATUS_OFF, 1, "Status: 0x%01x (%s)", val,
581                         val_to_str( val, tacplus_reply_status_vals, "Unknown Packet" )  );
582
583         val=tvb_get_guint8( tvb, AUTHEN_R_FLAGS_OFF );
584         proto_tree_add_text(tree, tvb,
585                         AUTHEN_R_FLAGS_OFF, 1, "Flags: 0x%02x %s",
586                         val, (val&TAC_PLUS_REPLY_FLAG_NOECHO?"(NoEcho)":"") );
587         
588
589         val=tvb_get_ntohs(tvb, AUTHEN_R_SRV_MSG_LEN_OFF );
590         proto_tree_add_text( tree, tvb, AUTHEN_R_SRV_MSG_LEN_OFF, 2 ,
591                                 "Server message length: %d", val );
592         if( val ) {
593                 buff=tvb_get_string(tvb, var_off, val );
594                 proto_tree_add_text(tree, tvb, var_off, val, "Server message: %s", buff );
595                 var_off+=val;
596                 g_free(buff);
597         }
598
599         val=tvb_get_ntohs(tvb, AUTHEN_R_DATA_LEN_OFF );
600         proto_tree_add_text( tree, tvb, AUTHEN_R_DATA_LEN_OFF, 2 ,
601                                 "Data length: %d", val );
602         if( val ){
603                 proto_tree_add_text(tree, tvb, var_off, val, "Data" );
604         }
605 }
606
607 static void
608 dissect_tacplus_body_author_req( tvbuff_t* tvb, proto_tree *tree )
609 {
610         int val;
611         int var_off;
612
613         val=tvb_get_guint8( tvb, AUTHOR_Q_AUTH_METH_OFF ) ;
614         proto_tree_add_text( tree, tvb, AUTHOR_Q_AUTH_METH_OFF, 1, 
615                         "Auth Method: %s", val_to_str( val, tacplus_authen_method, "Unknown Authen Method" ) );
616
617         val=tvb_get_guint8( tvb, AUTHOR_Q_ARGC_OFF );
618         var_off=proto_tree_add_tacplus_common_fields( tvb, tree ,
619                         AUTHOR_Q_PRIV_LVL_OFF,
620                         AUTHOR_Q_VARDATA_OFF + val );
621
622         proto_tree_add_text( tree, tvb, AUTHOR_Q_ARGC_OFF, 1, "Arg count: %d", val );
623         
624 /* var_off points after rem_addr */
625
626         dissect_tacplus_args_list( tvb, tree, var_off, AUTHOR_Q_VARDATA_OFF, val );
627 }
628
629 static void
630 dissect_tacplus_body_author_rep( tvbuff_t* tvb, proto_tree *tree )
631 {
632         int offset=AUTHOR_R_VARDATA_OFF;
633         int val=tvb_get_guint8( tvb, AUTHOR_R_STATUS_OFF        ) ;
634
635
636         proto_tree_add_text( tree, tvb, AUTHOR_R_STATUS_OFF     , 1, 
637                         "Auth Status: 0x%01x (%s)", val,
638                         val_to_str( val, tacplus_author_status, "Unknown Authorization Status" ));
639
640         val=tvb_get_ntohs( tvb, AUTHOR_R_SRV_MSG_LEN_OFF );
641         offset+=val;
642         proto_tree_add_text( tree, tvb, AUTHOR_R_SRV_MSG_LEN_OFF, 2, "Server Msg length: %d", val );
643
644         val=tvb_get_ntohs( tvb, AUTHOR_R_DATA_LEN_OFF );
645         offset+=val;
646         proto_tree_add_text( tree, tvb, AUTHOR_R_DATA_LEN_OFF, 2, "Data length: %d", val );
647
648         val=tvb_get_guint8( tvb, AUTHOR_R_ARGC_OFF);
649         offset+=val;
650         proto_tree_add_text( tree, tvb, AUTHOR_R_ARGC_OFF, 1, "Arg count: %d", val );
651
652         dissect_tacplus_args_list( tvb, tree, offset, AUTHOR_R_VARDATA_OFF, val );
653 }
654
655 static void
656 dissect_tacplus_body_acct_req( tvbuff_t* tvb, proto_tree *tree )
657 {
658         int val, var_off;
659
660         proto_item *tf;
661         proto_tree *flags_tree;
662
663         val=tvb_get_guint8( tvb, ACCT_Q_FLAGS_OFF ); 
664         tf = proto_tree_add_uint( tree, hf_tacplus_acct_flags, tvb, ACCT_Q_FLAGS_OFF, 1, val );
665
666         flags_tree = proto_item_add_subtree( tf, ett_tacplus_acct_flags );
667         proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
668                         decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_MORE, 8,
669                                 "More: Set", "More: Not set" ) );
670         proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
671                         decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_START, 8,
672                                 "Start: Set", "Start: Not set" ) );
673         proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
674                         decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_STOP, 8,
675                                 "Stop: Set", "Stop: Not set" ) );
676         proto_tree_add_text( flags_tree, tvb, ACCT_Q_FLAGS_OFF, 1, "%s",
677                         decode_boolean_bitfield( val, TAC_PLUS_ACCT_FLAG_WATCHDOG, 8,
678                                 "Watchdog: Set", "Watchdog: Not set" ) );
679
680         val=tvb_get_guint8( tvb, ACCT_Q_METHOD_OFF );
681         proto_tree_add_text( tree, tvb, ACCT_Q_METHOD_OFF, 1, 
682                         "Authen Method: 0x%01x (%s)",  
683                         val, val_to_str( val, tacplus_authen_method, "Unknown Authen Method" ) );
684
685         val=tvb_get_guint8( tvb, ACCT_Q_ARG_CNT_OFF );
686
687         /* authen_type */
688         var_off=proto_tree_add_tacplus_common_fields( tvb, tree ,
689                         ACCT_Q_PRIV_LVL_OFF,
690                         ACCT_Q_VARDATA_OFF+val
691                         );
692
693         proto_tree_add_text( tree, tvb, ACCT_Q_ARG_CNT_OFF, 1,
694                         "Arg Cnt: %d", val  );
695
696         dissect_tacplus_args_list( tvb, tree, var_off, ACCT_Q_VARDATA_OFF, val );
697
698
699 }
700
701 static void
702 dissect_tacplus_body_acct_rep( tvbuff_t* tvb, proto_tree *tree )
703 {
704         int val, var_off=ACCT_Q_VARDATA_OFF;
705
706         guint8 *buff=NULL;
707
708         /* Status */
709         val=tvb_get_guint8( tvb, ACCT_R_STATUS_OFF );
710         proto_tree_add_text( tree, tvb, ACCT_R_STATUS_OFF, 1, "Status: 0x%02x (%s)", val,
711                                 val_to_str( val, tacplus_acct_status, "Bogus status..") );
712
713         /* Server Message */
714         val=tvb_get_ntohs( tvb, ACCT_R_SRV_MSG_LEN_OFF );
715         proto_tree_add_text( tree, tvb, ACCT_R_SRV_MSG_LEN_OFF, 2 ,
716                                 "Server message length: %d", val );
717         if( val ) {
718                 buff=tvb_get_string( tvb, var_off, val );
719                 proto_tree_add_text( tree, tvb, var_off,
720                                 val, "Server message: %s", buff );
721                 var_off+=val;
722                 g_free(buff);
723         }
724
725         /*  Data */
726         val=tvb_get_ntohs( tvb, ACCT_R_DATA_LEN_OFF );
727         proto_tree_add_text( tree, tvb, ACCT_R_DATA_LEN_OFF, 2 ,
728                                 "Data length: %d", val );
729         if( val ) {
730                 buff= tvb_get_string( tvb, var_off, val );
731                 proto_tree_add_text( tree, tvb, var_off,
732                                 val, "Data: %s", buff );
733                 g_free(buff);
734         }
735 }
736
737
738
739 static void
740 dissect_tacplus_body(tvbuff_t * hdr_tvb, tvbuff_t * tvb, proto_tree * tree )
741 {
742         int type = tvb_get_guint8( hdr_tvb, H_TYPE_OFF );
743         int seq_no = tvb_get_guint8( hdr_tvb, H_SEQ_NO_OFF );
744
745         switch (type) {
746           case TAC_PLUS_AUTHEN:
747                 if (  seq_no & 0x01) {
748                         if ( seq_no == 1 )
749                                 dissect_tacplus_body_authen_req( tvb, tree );
750                         else
751                                 dissect_tacplus_body_authen_req_cont( tvb, tree );
752                 } else {
753                         dissect_tacplus_body_authen_rep( tvb, tree );
754                 }
755                 return;
756                 break;
757           case TAC_PLUS_AUTHOR:
758                 if ( seq_no & 0x01)
759                         dissect_tacplus_body_author_req( tvb, tree );
760                 else 
761                         dissect_tacplus_body_author_rep( tvb, tree );
762                 return;
763                 break;
764           case TAC_PLUS_ACCT:
765                 if ( seq_no & 0x01)
766                         dissect_tacplus_body_acct_req( tvb, tree ); 
767                 else
768                         dissect_tacplus_body_acct_rep( tvb, tree );
769                 return;
770                 break;
771         }
772         proto_tree_add_text( tree, tvb, 0, tvb_length( tvb ), "Bogus..");
773 }
774
775 #ifdef DEB_TACPLUS
776 static void
777 tacplus_print_key_entry( gpointer data, gpointer user_data )
778 {
779         tacplus_key_entry *tacplus_data=(tacplus_key_entry *)data;
780         if( user_data ) {
781                 printf("%s:%s=%s\n", address_to_str( tacplus_data->s ),
782                                 address_to_str( tacplus_data->c ), tacplus_data->k );
783         } else {
784                 printf("%s:%s\n", address_to_str( tacplus_data->s ),
785                                 address_to_str( tacplus_data->c ) );
786         }
787 }
788 #endif
789 static int
790 cmp_conv_address( gconstpointer p1, gconstpointer p2 )
791 {
792         tacplus_key_entry *a1=(tacplus_key_entry*)p1;
793         tacplus_key_entry *a2=(tacplus_key_entry*)p2;
794         gint32  ret;
795         /*
796         printf("p1=>");
797         tacplus_print_key_entry( p1, NULL );
798         printf("p2=>");
799         tacplus_print_key_entry( p2, NULL );
800         */
801         ret=CMP_ADDRESS( a1->s, a2->s );
802         if( !ret ) {
803                 ret=CMP_ADDRESS( a1->c, a2->c );
804                 /*
805                 if(ret)
806                         printf("No Client found!"); */
807         } else {
808                 /* printf("No Server found!"); */
809         }
810         return ret;
811 }
812
813 static char*
814 find_key( address *srv, address *cln )
815 {
816         tacplus_key_entry data;
817         GSList *match;
818
819         data.s=srv;
820         data.c=cln;
821 /*      printf("Looking for: ");
822         tacplus_print_key_entry( (gconstpointer)&data, NULL ); */
823         match=g_slist_find_custom( tacplus_keys, (gpointer)&data, cmp_conv_address );
824 /*      printf("Finished (%p)\n", match);  */
825         if( match ) 
826                 return ((tacplus_key_entry*)match->data)->k;
827
828         return (tacplus_keys?NULL:tacplus_opt_key);
829 }
830 #define AF_INET 2
831 int inet_pton(int , const char*, void*);
832
833 static void
834 mkipv4_address( address **addr, char *str_addr )
835 {
836         *addr=g_malloc( sizeof(address) );
837         (*addr)->type=AT_IPv4;
838         (*addr)->len=4;
839         (*addr)->data=g_malloc( 4 );
840         inet_pton( AF_INET, (const char*)str_addr, (void*)(*addr)->data );
841 }
842 static void
843 parse_tuple( char *key_from_option )
844 {
845         char *client,*key;
846         tacplus_key_entry *tacplus_data=g_malloc( sizeof(tacplus_key_entry) );
847         /*
848         printf("keys: %s\n", key_from_option );
849         */
850         client=strchr(key_from_option,'/');
851         if(!client)
852                 return;
853         *client++='\0';
854         key=strchr(client,'=');
855         if(!key)
856                 return;
857         *key++='\0';
858         /*
859         printf("%s %s => %s\n", key_from_option, client, key );
860         */
861         mkipv4_address( &tacplus_data->s, key_from_option );
862         mkipv4_address( &tacplus_data->c, client );
863         tacplus_data->k=strdup( key );
864         tacplus_keys = g_slist_prepend( tacplus_keys, tacplus_data );
865 }
866
867 static
868 void
869 free_tacplus_keys( gpointer data, gpointer user_data _U_ )
870 {
871         g_free( ((tacplus_key_entry *)data)->k );
872 }
873
874 static 
875 void
876 parse_tacplus_keys( char *keys_from_option )
877 {
878         char *s1,*s;
879
880         /* Drop old keys */
881         if( tacplus_keys ) {
882                 g_slist_foreach( tacplus_keys, free_tacplus_keys, NULL );
883                 g_slist_free( tacplus_keys );
884                 tacplus_keys=NULL;
885         }
886
887         if( !strchr( keys_from_option, '/' ) ){
888                 /* option not in client/server=key format */
889                 return ;
890         }
891         s=strdup(keys_from_option);
892         s1=s;
893         keys_from_option = s;
894         while(keys_from_option){
895                 if( (s=strchr( keys_from_option, ' ' )) != NULL )
896                         *s++='\0';
897                 parse_tuple( keys_from_option );
898                 keys_from_option=s;
899         }
900         g_free( s1 );
901 #ifdef DEB_TACPLUS
902         g_slist_foreach( tacplus_keys, tacplus_print_key_entry, GINT_TO_POINTER(1) );
903 #endif
904 }
905
906 static void
907 dissect_tacplus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
908 {
909         tvbuff_t        *new_tvb=NULL;
910         proto_tree      *tacplus_tree;
911         proto_item      *ti;
912         guint8          version,flags;
913         proto_tree      *flags_tree;
914         proto_item      *tf;
915         proto_item      *tmp_pi;
916         guint32         len;
917         gboolean        request=( pinfo->destport == TCP_PORT_TACACS );
918         char            *key=NULL;
919
920         if( request ) {
921                 key=find_key( &pinfo->dst, &pinfo->src );
922         } else {
923                 key=find_key(  &pinfo->src, &pinfo->dst );
924         }
925         if (check_col(pinfo->cinfo, COL_PROTOCOL))
926                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TACACS+");
927
928         if (check_col(pinfo->cinfo, COL_INFO))
929         {
930                 int type = tvb_get_guint8(tvb,1);
931                 col_add_fstr( pinfo->cinfo, COL_INFO, "%s: %s", 
932                                 request ? "Q" : "R",
933                                 val_to_str(type, tacplus_type_vals, "Unknown (0x%02x)"));
934         }
935
936         if (tree)
937         {
938                 ti = proto_tree_add_protocol_format(tree, proto_tacplus,
939                  tvb, 0, -1, "TACACS+");
940
941                 tacplus_tree = proto_item_add_subtree(ti, ett_tacplus);
942                 if (pinfo->match_port == pinfo->destport)
943                 {
944                         proto_tree_add_boolean_hidden(tacplus_tree,
945                             hf_tacplus_request, tvb, 0, 0, TRUE);
946                 }
947                 else
948                 {
949                         proto_tree_add_boolean_hidden(tacplus_tree,
950                             hf_tacplus_response, tvb, 0, 0, TRUE);
951                 }
952                 version = tvb_get_guint8(tvb,0);
953                 proto_tree_add_uint_format(tacplus_tree, hf_tacplus_majvers, tvb, 0, 1,
954                     version,
955                     "Major version: %s",
956                     (version&0xf0)==0xc0?"TACACS+":"Unknown Version");
957                 proto_tree_add_uint(tacplus_tree, hf_tacplus_minvers, tvb, 0, 1,
958                     version&0xf);
959                 proto_tree_add_item(tacplus_tree, hf_tacplus_type, tvb, 1, 1,
960                     FALSE);
961                 proto_tree_add_item(tacplus_tree, hf_tacplus_seqno, tvb, 2, 1,
962                     FALSE);
963                 flags = tvb_get_guint8(tvb,3);
964                 tf = proto_tree_add_uint_format(tacplus_tree, hf_tacplus_flags,
965                     tvb, 3, 1, flags,
966                     "Flags: 0x%02x (%s payload, %s)",
967                         flags,
968                     (flags&FLAGS_UNENCRYPTED) ? "Unencrypted" :
969                                                 "Encrypted",
970                     (flags&FLAGS_SINGLE) ? "Single connection" :
971                                            "Multiple Connections" );
972                 flags_tree = proto_item_add_subtree(tf, ett_tacplus_flags);
973                 proto_tree_add_boolean(flags_tree, hf_tacplus_flags_payload_type,
974                     tvb, 3, 1, flags);
975                 proto_tree_add_boolean(flags_tree, hf_tacplus_flags_connection_type,
976                     tvb, 3, 1, flags);
977                 proto_tree_add_item(tacplus_tree, hf_tacplus_session_id, tvb, 4, 4,
978                     FALSE);
979                 len = tvb_get_ntohl(tvb,8);
980                 proto_tree_add_uint(tacplus_tree, hf_tacplus_packet_len, tvb, 8, 4,
981                     len);
982
983                 tmp_pi = proto_tree_add_text(tacplus_tree, tvb, TAC_PLUS_HDR_SIZE, len, "%s%s",
984                                         ((flags&FLAGS_UNENCRYPTED)?"":"Encrypted "), request?"Request":"Reply" );
985
986                 if( flags&FLAGS_UNENCRYPTED ) {
987                         new_tvb = tvb_new_subset( tvb, TAC_PLUS_HDR_SIZE, len, len );
988                 }  else {
989                         new_tvb=NULL;
990                         if( key && *key ){
991                                 tacplus_decrypted_tvb_setup( tvb, &new_tvb, pinfo, len, version, key );
992                         }
993                 }
994                 if( new_tvb ) {
995                         /* Check to see if I've a decrypted tacacs packet */
996                         if( !(flags&FLAGS_UNENCRYPTED) ){       
997                                 tmp_pi = proto_tree_add_text(tacplus_tree, new_tvb, 0, len, "Decrypted %s",
998                                                         request?"Request":"Reply" );
999                         }
1000                         dissect_tacplus_body( tvb, new_tvb, proto_item_add_subtree( tmp_pi, ett_tacplus_body ));
1001                 }
1002         }
1003 }
1004
1005 void
1006 tacplus_pref_cb(void)
1007 {
1008         parse_tacplus_keys( tacplus_opt_key );
1009 }
1010
1011 void
1012 proto_register_tacplus(void)
1013 {
1014         static hf_register_info hf[] = {
1015           { &hf_tacplus_response,
1016             { "Response",           "tacplus.response",
1017               FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1018               "TRUE if TACACS+ response", HFILL }},
1019           { &hf_tacplus_request,
1020             { "Request",            "tacplus.request",
1021               FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1022               "TRUE if TACACS+ request", HFILL }},
1023           { &hf_tacplus_majvers,
1024             { "Major version",      "tacplus.majvers",
1025               FT_UINT8, BASE_DEC, NULL, 0x0,
1026               "Major version number", HFILL }},
1027           { &hf_tacplus_minvers,
1028             { "Minor version",      "tacplus.minvers",
1029               FT_UINT8, BASE_DEC, NULL, 0x0,
1030               "Minor version number", HFILL }},
1031           { &hf_tacplus_type,
1032             { "Type",               "tacplus.type",
1033               FT_UINT8, BASE_DEC, VALS(tacplus_type_vals), 0x0,
1034               "Type", HFILL }},
1035           { &hf_tacplus_seqno,
1036             { "Sequence number",    "tacplus.seqno",
1037               FT_UINT8, BASE_DEC, NULL, 0x0,
1038               "Sequence number", HFILL }},
1039           { &hf_tacplus_flags,
1040             { "Flags",              "tacplus.flags",
1041               FT_UINT8, BASE_HEX, NULL, 0x0,
1042               "Flags", HFILL }},
1043           { &hf_tacplus_flags_payload_type,
1044             { "Unencrypted",       "tacplus.flags.unencrypted",
1045               FT_BOOLEAN, 8, TFS(&flags_set_truth), FLAGS_UNENCRYPTED,
1046               "Is payload unencrypted?", HFILL }},
1047           { &hf_tacplus_flags_connection_type,
1048             { "Single Connection",    "tacplus.flags.singleconn",
1049               FT_BOOLEAN, 8, TFS(&flags_set_truth), FLAGS_SINGLE,
1050               "Is this a single connection?", HFILL }},
1051           { &hf_tacplus_acct_flags,
1052             { "Flags",    "tacplus.acct.flags",
1053               FT_UINT8, BASE_HEX, NULL, 0x0,
1054               "Flags", HFILL }},
1055           { &hf_tacplus_session_id,
1056             { "Session ID",         "tacplus.session_id",
1057               FT_UINT32, BASE_DEC, NULL, 0x0,
1058               "Session ID", HFILL }},
1059           { &hf_tacplus_packet_len,
1060             { "Packet length",      "tacplus.packet_len",
1061               FT_UINT32, BASE_DEC, NULL, 0x0,
1062               "Packet length", HFILL }}
1063         };
1064         static gint *ett[] = {
1065                 &ett_tacplus,
1066                 &ett_tacplus_flags,
1067                 &ett_tacplus_acct_flags,
1068                 &ett_tacplus_body,
1069                 &ett_tacplus_body_chap, 
1070         };
1071         module_t *tacplus_module;
1072
1073         proto_tacplus = proto_register_protocol("TACACS+", "TACACS+", "tacplus");
1074         proto_register_field_array(proto_tacplus, hf, array_length(hf));
1075         proto_register_subtree_array(ett, array_length(ett));
1076         tacplus_module = prefs_register_protocol (proto_tacplus, tacplus_pref_cb );
1077         prefs_register_string_preference ( tacplus_module, "key",
1078         "TACACS+ Encryption Key", "TACACS+ Encryption Key", &tacplus_opt_key );
1079 }
1080
1081 void
1082 proto_reg_handoff_tacplus(void)
1083 {
1084         dissector_handle_t tacplus_handle;
1085
1086         tacplus_handle = create_dissector_handle(dissect_tacplus,
1087             proto_tacplus);
1088         dissector_add("tcp.port", TCP_PORT_TACACS, tacplus_handle);
1089 }
1090
1091
1092 #define MD5_LEN 16
1093
1094 static void
1095 md5_xor( guint8 *data, char *key, int data_len, guint8 *session_id, guint8 version, guint8 seq_no )
1096 {
1097         int i,j,md5_len;
1098         md5_byte_t *md5_buff;
1099         md5_byte_t hash[MD5_LEN];                                       /* the md5 hash */
1100         md5_byte_t *mdp;
1101         md5_state_t mdcontext;
1102
1103         md5_len = 4 /* sizeof(session_id) */ + strlen(key)
1104                         + sizeof(version) + sizeof(seq_no);
1105         
1106         md5_buff = (md5_byte_t*)g_malloc(md5_len+MD5_LEN);
1107
1108
1109         mdp = md5_buff;
1110         *(guint32*)mdp = *(guint32*)session_id;
1111         mdp += 4 ;
1112         memcpy(mdp, key, strlen(key));
1113         mdp += strlen(key);
1114         *mdp++ = version;
1115         *mdp++ = seq_no;
1116
1117
1118         md5_init(&mdcontext);
1119         md5_append(&mdcontext, md5_buff, md5_len);
1120         md5_finish(&mdcontext,hash);
1121         md5_len += MD5_LEN;
1122         for (i = 0; i < data_len; i += 16) {
1123
1124                 for (j = 0; j < 16; j++) {
1125                         if ((i + j) >= data_len)  {
1126                                 i = data_len+1; /* To exit from the external loop  */
1127                                 break;
1128                         }
1129                         data[i + j] ^= hash[j];
1130                 }
1131                 memcpy(mdp, hash, MD5_LEN);
1132                 md5_init(&mdcontext);
1133                 md5_append(&mdcontext, md5_buff, md5_len);
1134                 md5_finish(&mdcontext,hash);
1135         }
1136         g_free( md5_buff );
1137 }