2 * Routines for Telnet packet dissection; see RFC 854 and RFC 855
3 * Copyright 1999, Richard Sharpe <rsharpe@ns.aus.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * Copied from packet-pop.c
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 /* Telnet authentication options as per RFC2941
28 * Kerberos v5 telnet authentication as per RFC2942
38 #include <epan/packet.h>
39 #include <epan/strutil.h>
40 #include <epan/emem.h>
41 #include <epan/asn1.h>
42 #include "packet-kerberos.h"
44 static int proto_telnet = -1;
45 static int hf_telnet_auth_cmd = -1;
46 static int hf_telnet_auth_name = -1;
47 static int hf_telnet_auth_type = -1;
48 static int hf_telnet_auth_mod_who = -1;
49 static int hf_telnet_auth_mod_how = -1;
50 static int hf_telnet_auth_mod_cred_fwd = -1;
51 static int hf_telnet_auth_mod_enc = -1;
52 static int hf_telnet_auth_krb5_type = -1;
54 static int hf_telnet_enc_cmd = -1;
55 static int hf_telnet_enc_type = -1;
57 static gint ett_telnet = -1;
58 static gint ett_telnet_subopt = -1;
59 static gint ett_status_subopt = -1;
60 static gint ett_rcte_subopt = -1;
61 static gint ett_olw_subopt = -1;
62 static gint ett_ops_subopt = -1;
63 static gint ett_crdisp_subopt = -1;
64 static gint ett_htstops_subopt = -1;
65 static gint ett_htdisp_subopt = -1;
66 static gint ett_ffdisp_subopt = -1;
67 static gint ett_vtstops_subopt = -1;
68 static gint ett_vtdisp_subopt = -1;
69 static gint ett_lfdisp_subopt = -1;
70 static gint ett_extasc_subopt = -1;
71 static gint ett_bytemacro_subopt = -1;
72 static gint ett_det_subopt = -1;
73 static gint ett_supdupout_subopt = -1;
74 static gint ett_sendloc_subopt = -1;
75 static gint ett_termtype_subopt = -1;
76 static gint ett_tacacsui_subopt = -1;
77 static gint ett_outmark_subopt = -1;
78 static gint ett_tlocnum_subopt = -1;
79 static gint ett_tn3270reg_subopt = -1;
80 static gint ett_x3pad_subopt = -1;
81 static gint ett_naws_subopt = -1;
82 static gint ett_tspeed_subopt = -1;
83 static gint ett_rfc_subopt = -1;
84 static gint ett_linemode_subopt = -1;
85 static gint ett_xdpyloc_subopt = -1;
86 static gint ett_env_subopt = -1;
87 static gint ett_auth_subopt = -1;
88 static gint ett_enc_subopt = -1;
89 static gint ett_newenv_subopt = -1;
90 static gint ett_tn3270e_subopt = -1;
91 static gint ett_xauth_subopt = -1;
92 static gint ett_charset_subopt = -1;
93 static gint ett_rsp_subopt = -1;
94 static gint ett_comport_subopt = -1;
97 /* Some defines for Telnet */
99 #define TCP_PORT_TELNET 23
124 NO_LENGTH, /* option has no data, hence no length */
125 FIXED_LENGTH, /* option always has the same length */
126 VARIABLE_LENGTH /* option is variable-length - optlen is minimum */
129 /* Member of table of IP or TCP options. */
130 typedef struct tn_opt {
131 const char *name; /* name of option */
132 gint *subtree_index; /* pointer to subtree index for option */
133 tn_opt_len_type len_type; /* type of option length field */
134 int optlen; /* value length should be (minimum if VARIABLE) */
135 void (*dissect)(packet_info *pinfo, const char *, tvbuff_t *, int, int, proto_tree *);
136 /* routine to dissect option */
140 dissect_string_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
145 cmd = tvb_get_guint8(tvb, offset);
149 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
153 proto_tree_add_text(tree, tvb, offset, len, "Value: %s",
154 tvb_format_text(tvb, offset, len));
159 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
163 proto_tree_add_text(tree, tvb, offset, len, "Extra data");
167 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
172 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
178 dissect_outmark_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
179 int len, proto_tree *tree)
182 int gs_offset, datalen;
185 cmd = tvb_get_guint8(tvb, offset);
189 proto_tree_add_text(tree, tvb, offset, 1, "ACK");
193 proto_tree_add_text(tree, tvb, offset, 1, "NAK");
197 proto_tree_add_text(tree, tvb, offset, 1, "Default");
201 proto_tree_add_text(tree, tvb, offset, 1, "Top");
205 proto_tree_add_text(tree, tvb, offset, 1, "Bottom");
209 proto_tree_add_text(tree, tvb, offset, 1, "Left");
213 proto_tree_add_text(tree, tvb, offset, 1, "Right");
217 proto_tree_add_text(tree, tvb, offset, 1, "Bogus value: %u", cmd);
224 gs_offset = tvb_find_guint8(tvb, offset, len, 29);
225 if (gs_offset == -1) {
226 /* None found - run to the end of the packet. */
227 gs_offset = offset + len;
229 datalen = gs_offset - offset;
231 proto_tree_add_text(tree, tvb, offset, datalen, "Banner: %s",
232 tvb_format_text(tvb, offset, datalen));
240 dissect_htstops_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
246 cmd = tvb_get_guint8(tvb, offset);
250 proto_tree_add_text(tree, tvb, offset, 1, "Here's my %s", optname);
256 proto_tree_add_text(tree, tvb, offset, 1, "Send your %s", optname);
262 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
267 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
272 tabval = tvb_get_guint8(tvb, offset);
276 proto_tree_add_text(tree, tvb, offset, 1,
277 "Sender wants to handle tab stops");
281 proto_tree_add_text(tree, tvb, offset, 1,
282 "Sender wants receiver to handle tab stop at %u",
290 proto_tree_add_text(tree, tvb, offset, 1,
291 "Invalid value: %u", tabval);
295 proto_tree_add_text(tree, tvb, offset, 1,
296 "Sender wants receiver to handle tab stops");
305 dissect_naws_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
306 int len _U_, proto_tree *tree)
308 proto_tree_add_text(tree, tvb, offset, 2, "Width: %u",
309 tvb_get_ntohs(tvb, offset));
311 proto_tree_add_text(tree, tvb, offset, 2, "Height: %u",
312 tvb_get_ntohs(tvb, offset));
315 /* BEGIN RFC-2217 (COM Port Control) Definitions */
317 #define TNCOMPORT_SIGNATURE 0
318 #define TNCOMPORT_SETBAUDRATE 1
319 #define TNCOMPORT_SETDATASIZE 2
320 #define TNCOMPORT_SETPARITY 3
321 #define TNCOMPORT_SETSTOPSIZE 4
322 #define TNCOMPORT_SETCONTROL 5
323 #define TNCOMPORT_NOTIFYLINESTATE 6
324 #define TNCOMPORT_NOTIFYMODEMSTATE 7
325 #define TNCOMPORT_FLOWCONTROLSUSPEND 8
326 #define TNCOMPORT_FLOWCONTROLRESUME 9
327 #define TNCOMPORT_SETLINESTATEMASK 10
328 #define TNCOMPORT_SETMODEMSTATEMASK 11
329 #define TNCOMPORT_PURGEDATA 12
331 /* END RFC-2217 (COM Port Control) Definitions */
334 dissect_comport_subopt(packet_info *pinfo _U_, const char *optname, tvbuff_t *tvb, int offset, int len,
336 {static const char *datasizes[] = {
347 static const char *parities[] = {
355 static const char *stops[] = {
361 static const char *control[] = {
362 "Output Flow Control Request",
364 "Output Flow: XON/XOFF",
365 "Output Flow: CTS/RTS",
375 "Input Flow Control Request",
377 "Input Flow: XON/XOFF",
378 "Input Flow: CTS/RTS",
383 static const char *linestate_bits[] = {
389 "Transfer Holding Register Empty",
390 "Transfer Shift Register Empty",
393 static const char *modemstate_bits[] = {
403 static const char *purges[] = {
414 cmd = tvb_get_guint8(tvb, offset);
415 isservercmd = cmd > 99;
416 cmd = (isservercmd) ? (cmd - 100) : cmd;
417 source = (isservercmd) ? "Server" : "Client";
420 case TNCOMPORT_SIGNATURE:
423 proto_tree_add_text(tree, tvb, offset, 1, "%s Requests Signature",source);
425 guint8 *sig = tvb_get_ephemeral_string(tvb, offset + 1, len);
426 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s Signature: %s",source, sig);
430 case TNCOMPORT_SETBAUDRATE:
433 guint32 baud = tvb_get_ntohl(tvb, offset+1);
435 proto_tree_add_text(tree, tvb, offset, 5, "%s Requests Baud Rate",source);
437 proto_tree_add_text(tree, tvb, offset, 5, "%s Baud Rate: %d",source,baud);
440 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Baud Rate Packet>",source);
444 case TNCOMPORT_SETDATASIZE:
447 guint8 datasize = tvb_get_guint8(tvb, offset+1);
448 const char *ds = (datasize > 8) ? "<invalid>" : datasizes[datasize];
449 proto_tree_add_text(tree, tvb, offset, 2, "%s Data Size: %s",source,ds);
451 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Data Size Packet>",source);
455 case TNCOMPORT_SETPARITY:
458 guint8 parity = tvb_get_guint8(tvb, offset+1);
459 const char *pr = (parity > 5) ? "<invalid>" : parities[parity];
460 proto_tree_add_text(tree, tvb, offset, 2, "%s Parity: %s",source,pr);
462 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Parity Packet>",source);
466 case TNCOMPORT_SETSTOPSIZE:
469 guint8 stop = tvb_get_guint8(tvb, offset+1);
470 const char *st = (stop > 3) ? "<invalid>" : stops[stop];
471 proto_tree_add_text(tree, tvb, offset, 2, "%s Stop: %s",source,st);
473 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Stop Packet>",source);
477 case TNCOMPORT_SETCONTROL:
480 guint8 crt = tvb_get_guint8(tvb, offset+1);
481 const char *c = (crt > 19) ? "Control: <invalid>" : control[crt];
482 proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,c);
484 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Control Packet>",source);
488 case TNCOMPORT_SETLINESTATEMASK:
489 case TNCOMPORT_NOTIFYLINESTATE:
492 const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
493 "%s Set Linestate Mask: %s" : "%s Linestate: %s";
495 guint8 ls = tvb_get_guint8(tvb, offset+1);
499 for (idx = 0; idx < 8; idx++) {
502 if (print_count != 0) {
503 g_strlcat(ls_buffer,", ",512);
505 g_strlcat(ls_buffer,linestate_bits[idx], 512);
510 proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ls_buffer);
512 const char *print_pattern = (cmd == TNCOMPORT_SETLINESTATEMASK) ?
513 "%s <Invalid Linestate Mask>" : "%s <Invalid Linestate Packet>";
514 proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
518 case TNCOMPORT_SETMODEMSTATEMASK:
519 case TNCOMPORT_NOTIFYMODEMSTATE:
522 const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
523 "%s Set Modemstate Mask: %s" : "%s Modemstate: %s";
525 guint8 ms = tvb_get_guint8(tvb, offset+1);
529 for (idx = 0; idx < 8; idx++) {
532 if (print_count != 0) {
533 g_strlcat(ms_buffer,", ",256);
535 g_strlcat(ms_buffer,modemstate_bits[idx],256);
540 proto_tree_add_text(tree, tvb, offset, 2, print_pattern, source, ms_buffer);
542 const char *print_pattern = (cmd == TNCOMPORT_SETMODEMSTATEMASK) ?
543 "%s <Invalid Modemstate Mask>" : "%s <Invalid Modemstate Packet>";
544 proto_tree_add_text(tree, tvb, offset, 1 + len, print_pattern, source);
548 case TNCOMPORT_FLOWCONTROLSUSPEND:
550 proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Suspend",source);
553 case TNCOMPORT_FLOWCONTROLRESUME:
555 proto_tree_add_text(tree, tvb, offset, 1, "%s Flow Control Resume",source);
558 case TNCOMPORT_PURGEDATA:
561 guint8 purge = tvb_get_guint8(tvb, offset+1);
562 const char *p = (purge > 3) ? "<Purge invalid>" : purges[purge];
563 proto_tree_add_text(tree, tvb, offset, 2, "%s %s",source,p);
565 proto_tree_add_text(tree, tvb, offset, 1 + len, "%s <Invalid Purge Packet>",source);
570 proto_tree_add_text(tree, tvb, offset, 1, "Invalid %s subcommand %u",
575 proto_tree_add_text(tree, tvb, offset, len, "Subcommand data");
581 static const value_string rfc_opt_vals[] = {
584 { 2, "RESTART-ANY" },
585 { 3, "RESTART-XON" },
590 dissect_rfc_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset,
591 int len _U_, proto_tree *tree)
595 cmd = tvb_get_guint8(tvb, offset);
596 proto_tree_add_text(tree, tvb, offset, 2, "%s",
597 val_to_str(cmd, rfc_opt_vals, "Unknown (%u)"));
601 #define TN_ENC_SUPPORT 1
602 #define TN_ENC_REPLY 2
603 #define TN_ENC_START 3
605 #define TN_ENC_REQUEST_START 5
606 #define TN_ENC_REQUEST_END 6
607 #define TN_ENC_ENC_KEYID 7
608 #define TN_ENC_DEC_KEYID 8
609 static const value_string enc_cmd_vals[] = {
611 { TN_ENC_SUPPORT, "SUPPORT" },
612 { TN_ENC_REPLY, "REPLY" },
613 { TN_ENC_START, "START" },
614 { TN_ENC_END, "END" },
615 { TN_ENC_REQUEST_START, "REQUEST-START" },
616 { TN_ENC_REQUEST_END, "REQUEST-END" },
617 { TN_ENC_ENC_KEYID, "ENC_KEYID" },
618 { TN_ENC_DEC_KEYID, "DEC_KEYID" },
622 #define TN_ENCTYPE_NULL 0
623 #define TN_ENCTYPE_DES_CFB64 1
624 #define TN_ENCTYPE_DES_OFB64 2
625 #define TN_ENCTYPE_DES3_CFB64 3
626 #define TN_ENCTYPE_DES3_OFB64 4
627 #define TN_ENCTYPE_CAST5_40_CFB64 8
628 #define TN_ENCTYPE_CAST5_40_OFB64 9
629 #define TN_ENCTYPE_CAST128_CFB64 10
630 #define TN_ENCTYPE_CAST128_OFB64 11
631 static const value_string enc_type_vals[] = {
632 { TN_ENCTYPE_NULL, "NULL" },
633 { TN_ENCTYPE_DES_CFB64, "DES_CFB64" },
634 { TN_ENCTYPE_DES_OFB64, "DES_OFB64" },
635 { TN_ENCTYPE_DES3_CFB64, "DES3_CFB64" },
636 { TN_ENCTYPE_DES3_OFB64, "DES3_OFB64" },
637 { TN_ENCTYPE_CAST5_40_CFB64, "CAST5_40_CFB64" },
638 { TN_ENCTYPE_CAST5_40_OFB64, "CAST5_40_OFB64" },
639 { TN_ENCTYPE_CAST128_CFB64, "CAST128_CFB64" },
640 { TN_ENCTYPE_CAST128_OFB64, "CAST128_OFB64" },
647 #define TN_AC_REPLY 2
649 static const value_string auth_cmd_vals[] = {
651 { TN_AC_SEND, "SEND" },
652 { TN_AC_REPLY, "REPLY" },
653 { TN_AC_NAME, "NAME" },
665 #define TN_AT_LOKI 10
667 #define TN_AT_KEA_SJ 12
668 #define TN_AT_KEA_SJ_INTEG 13
670 #define TN_AT_NTLM 15
671 static const value_string auth_type_vals[] = {
672 { TN_AT_NULL, "NULL" },
673 { TN_AT_KRB4, "Kerberos v4" },
674 { TN_AT_KRB5, "Kerberos v5" },
675 { TN_AT_SPX, "SPX" },
676 { TN_AT_MINK, "MINK" },
677 { TN_AT_SRP, "SRP" },
678 { TN_AT_RSA, "RSA" },
679 { TN_AT_SSL, "SSL" },
680 { TN_AT_LOKI, "LOKI" },
681 { TN_AT_SSA, "SSA" },
682 { TN_AT_KEA_SJ, "KEA_SJ" },
683 { TN_AT_KEA_SJ_INTEG, "KEA_SJ_INTEG" },
684 { TN_AT_DSS, "DSS" },
685 { TN_AT_NTLM, "NTLM" },
688 static const true_false_string auth_mod_cred_fwd = {
689 "Client WILL forward auth creds",
690 "Client will NOT forward auth creds"
692 static const true_false_string auth_mod_who = {
693 "Mask server to client",
694 "Mask client to server"
696 static const true_false_string auth_mod_how = {
697 "MUTUAL authentication",
698 "One Way authentication"
700 #define TN_AM_OFF 0x00
701 #define TN_AM_USING_TELOPT 0x01
702 #define TN_AM_AFTER_EXCHANGE 0x02
703 #define TN_AM_RESERVED 0x04
704 static const value_string auth_mod_enc[] = {
705 { TN_AM_OFF, "Off" },
706 { TN_AM_USING_TELOPT, "Telnet Options" },
707 { TN_AM_AFTER_EXCHANGE, "After Exchange" },
708 { TN_AM_RESERVED, "Reserved" },
711 #define TN_KRB5_TYPE_AUTH 0
712 #define TN_KRB5_TYPE_REJECT 1
713 #define TN_KRB5_TYPE_ACCEPT 2
714 #define TN_KRB5_TYPE_RESPONSE 3
715 #define TN_KRB5_TYPE_FORWARD 4
716 #define TN_KRB5_TYPE_FORWARD_ACCEPT 5
717 #define TN_KRB5_TYPE_FORWARD_REJECT 6
718 static const value_string auth_krb5_types[] = {
719 { TN_KRB5_TYPE_AUTH, "Auth" },
720 { TN_KRB5_TYPE_REJECT, "Reject" },
721 { TN_KRB5_TYPE_ACCEPT, "Accept" },
722 { TN_KRB5_TYPE_RESPONSE, "Response" },
723 { TN_KRB5_TYPE_FORWARD, "Forward" },
724 { TN_KRB5_TYPE_FORWARD_ACCEPT, "Forward Accept" },
725 { TN_KRB5_TYPE_FORWARD_REJECT, "Forward Reject" },
729 dissect_authentication_type_pair(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, proto_tree *tree)
733 type=tvb_get_guint8(tvb, offset);
734 proto_tree_add_uint(tree, hf_telnet_auth_type, tvb, offset, 1, type);
736 mod=tvb_get_guint8(tvb, offset+1);
737 proto_tree_add_uint(tree, hf_telnet_auth_mod_enc, tvb, offset+1, 1, mod);
738 proto_tree_add_boolean(tree, hf_telnet_auth_mod_cred_fwd, tvb, offset+1, 1, mod);
739 proto_tree_add_boolean(tree, hf_telnet_auth_mod_how, tvb, offset+1, 1, mod);
740 proto_tree_add_boolean(tree, hf_telnet_auth_mod_who, tvb, offset+1, 1, mod);
743 /* no kerberos blobs are ever >10kb ? (arbitrary limit) */
744 #define MAX_KRB5_BLOB_LEN 10240
747 unescape_and_tvbuffify_telnet_option(packet_info *pinfo, tvbuff_t *tvb, int offset, int len)
755 if(len>=MAX_KRB5_BLOB_LEN)
758 spos=tvb_get_ptr(tvb, offset, len);
764 if((spos[0]==0xff) && (spos[1]==0xff)){
774 krb5_tvb = tvb_new_child_real_data(tvb, buf, len-skip, len-skip);
775 tvb_set_free_cb(krb5_tvb, g_free);
776 add_new_data_source(pinfo, krb5_tvb, "Unpacked Telnet Option");
784 dissect_krb5_authentication_data(packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_tree *tree, guint8 acmd)
789 dissect_authentication_type_pair(pinfo, tvb, offset, tree);
794 krb5_cmd=tvb_get_guint8(tvb, offset);
795 proto_tree_add_uint(tree, hf_telnet_auth_krb5_type, tvb, offset, 1, krb5_cmd);
800 /* IAC SB AUTHENTICATION IS <authentication-type-pair> AUTH <Kerberos V5 KRB_AP_REQ message> IAC SE */
801 if((acmd==TN_AC_IS)&&(krb5_cmd==TN_KRB5_TYPE_AUTH)){
803 krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
805 dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE, NULL);
807 proto_tree_add_text(tree, tvb, offset, len, "Kerberos blob (too long to dissect - length %u > %u",
808 len, MAX_KRB5_BLOB_LEN);
814 /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> ACCEPT IAC SE */
815 /* nothing more to dissect */
819 /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> REJECT <optional reason for rejection> IAC SE*/
823 /* IAC SB AUTHENTICATION REPLY <authentication-type-pair> RESPONSE <KRB_AP_REP message> IAC SE */
824 if((acmd==TN_AC_REPLY)&&(krb5_cmd==TN_KRB5_TYPE_RESPONSE)){
826 krb5_tvb=unescape_and_tvbuffify_telnet_option(pinfo, tvb, offset, len);
827 dissect_kerberos_main(krb5_tvb, pinfo, tree, FALSE, NULL);
832 /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD <KRB_CRED message> IAC SE */
833 /* XXX unclear what this one looks like */
836 /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_ACCEPT IAC SE */
837 /* nothing more to dissect */
841 /* IAC SB AUTHENTICATION <authentication-type-pair> FORWARD_REJECT */
842 /* nothing more to dissect */
846 dissect_authentication_subopt(packet_info *pinfo, const char *optname _U_, tvbuff_t *tvb, int offset, int len, proto_tree *tree)
851 /* XXX here we should really split it up in a conversation struct keeping
852 track of what method we actually use and not just assume it is always
855 acmd=tvb_get_guint8(tvb, offset);
856 proto_tree_add_uint(tree, hf_telnet_auth_cmd, tvb, offset, 1, acmd);
863 /* XXX here we shouldnt just assume it is krb5 */
864 dissect_krb5_authentication_data(pinfo, tvb, offset, len, tree, acmd);
868 dissect_authentication_type_pair(pinfo, tvb, offset, tree);
876 tvb_memcpy(tvb, (guint8*)name, offset, len);
879 name="<...name too long...>";
881 proto_tree_add_string(tree, hf_telnet_auth_name, tvb, offset, len, name);
886 /* This function only uses the octet in the buffer at 'offset' */
887 static void dissect_encryption_type(tvbuff_t *tvb, int offset, proto_tree *tree) {
889 etype = tvb_get_guint8(tvb, offset);
890 proto_tree_add_uint(tree, hf_telnet_enc_type, tvb, offset, 1, etype);
894 dissect_encryption_subopt(packet_info *pinfo _U_, const char *optname _U_, tvbuff_t *tvb, int offset, int len, proto_tree *tree)
896 guint8 ecmd, key_first_octet;
898 ecmd = tvb_get_guint8(tvb, offset);
899 proto_tree_add_uint(tree, hf_telnet_enc_cmd, tvb, offset, 1, ecmd);
907 /* encryption type, type-specific data ... */
909 dissect_encryption_type(tvb, offset, tree);
912 proto_tree_add_text(tree, tvb, offset, len, "Type-specific data");
917 /* list of encryption types ... */
919 dissect_encryption_type(tvb, offset, tree);
928 key_first_octet = tvb_get_guint8(tvb, offset);
929 proto_tree_add_text(tree, tvb, offset, len, (key_first_octet == 0) ? "Default key" : "Key ID");
937 case TN_ENC_REQUEST_START:
938 /* (optional) keyid */
940 proto_tree_add_text(tree, tvb, offset, len, "Key ID (advisory)");
943 case TN_ENC_REQUEST_END:
947 case TN_ENC_ENC_KEYID:
948 case TN_ENC_DEC_KEYID:
949 /* (optional) keyid - if not supplied, there are no more known keys */
951 proto_tree_add_text(tree, tvb, offset, len, "Key ID");
955 proto_tree_add_text(tree, tvb, offset, len, "Unknown command");
959 static tn_opt options[] = {
961 "Binary Transmission", /* RFC 856 */
962 NULL, /* no suboption negotiation */
968 "Echo", /* RFC 857 */
969 NULL, /* no suboption negotiation */
975 "Reconnection", /* DOD Protocol Handbook */
982 "Suppress Go Ahead", /* RFC 858 */
983 NULL, /* no suboption negotiation */
989 "Approx Message Size Negotiation", /* Ethernet spec(!) */
996 "Status", /* RFC 859 */
1000 NULL /* XXX - fill me in */
1003 "Timing Mark", /* RFC 860 */
1004 NULL, /* no suboption negotiation */
1010 "Remote Controlled Trans and Echo", /* RFC 726 */
1014 NULL /* XXX - fill me in */
1017 "Output Line Width", /* DOD Protocol Handbook */
1019 VARIABLE_LENGTH, /* XXX - fill me in */
1020 0, /* XXX - fill me in */
1021 NULL /* XXX - fill me in */
1024 "Output Page Size", /* DOD Protocol Handbook */
1026 VARIABLE_LENGTH, /* XXX - fill me in */
1027 0, /* XXX - fill me in */
1028 NULL /* XXX - fill me in */
1031 "Output Carriage-Return Disposition", /* RFC 652 */
1035 NULL /* XXX - fill me in */
1038 "Output Horizontal Tab Stops", /* RFC 653 */
1039 &ett_htstops_subopt,
1042 dissect_htstops_subopt
1045 "Output Horizontal Tab Disposition", /* RFC 654 */
1049 NULL /* XXX - fill me in */
1052 "Output Formfeed Disposition", /* RFC 655 */
1056 NULL /* XXX - fill me in */
1059 "Output Vertical Tabstops", /* RFC 656 */
1060 &ett_vtstops_subopt,
1063 NULL /* XXX - fill me in */
1066 "Output Vertical Tab Disposition", /* RFC 657 */
1070 NULL /* XXX - fill me in */
1073 "Output Linefeed Disposition", /* RFC 658 */
1077 NULL /* XXX - fill me in */
1080 "Extended ASCII", /* RFC 698 */
1084 NULL /* XXX - fill me in */
1087 "Logout", /* RFC 727 */
1088 NULL, /* no suboption negotiation */
1094 "Byte Macro", /* RFC 735 */
1095 &ett_bytemacro_subopt,
1098 NULL /* XXX - fill me in */
1101 "Data Entry Terminal", /* RFC 732, RFC 1043 */
1105 NULL /* XXX - fill me in */
1108 "SUPDUP", /* RFC 734, RFC 736 */
1109 NULL, /* no suboption negotiation */
1115 "SUPDUP Output", /* RFC 749 */
1116 &ett_supdupout_subopt,
1119 NULL /* XXX - fill me in */
1122 "Send Location", /* RFC 779 */
1123 &ett_sendloc_subopt,
1126 NULL /* XXX - fill me in */
1129 "Terminal Type", /* RFC 1091 */
1130 &ett_termtype_subopt,
1133 dissect_string_subopt
1136 "End of Record", /* RFC 885 */
1137 NULL, /* no suboption negotiation */
1143 "TACACS User Identification", /* RFC 927 */
1144 &ett_tacacsui_subopt,
1147 NULL /* XXX - fill me in */
1150 "Output Marking", /* RFC 933 */
1151 &ett_outmark_subopt,
1154 dissect_outmark_subopt,
1157 "Terminal Location Number", /* RFC 946 */
1158 &ett_tlocnum_subopt,
1161 NULL /* XXX - fill me in */
1164 "Telnet 3270 Regime", /* RFC 1041 */
1165 &ett_tn3270reg_subopt,
1168 NULL /* XXX - fill me in */
1171 "X.3 PAD", /* RFC 1053 */
1175 NULL /* XXX - fill me in */
1178 "Negotiate About Window Size", /* RFC 1073, DW183 */
1185 "Terminal Speed", /* RFC 1079 */
1189 NULL /* XXX - fill me in */
1192 "Remote Flow Control", /* RFC 1372 */
1199 "Linemode", /* RFC 1184 */
1200 &ett_linemode_subopt,
1203 NULL /* XXX - fill me in */
1206 "X Display Location", /* RFC 1096 */
1207 &ett_xdpyloc_subopt,
1210 dissect_string_subopt
1213 "Environment Option", /* RFC 1408, RFC 1571 */
1217 NULL /* XXX - fill me in */
1220 "Authentication Option", /* RFC 2941 */
1224 dissect_authentication_subopt
1227 "Encryption Option", /* RFC 2946 */
1231 dissect_encryption_subopt
1234 "New Environment Option", /* RFC 1572 */
1238 NULL /* XXX - fill me in */
1241 "TN3270E", /* RFC 1647 */
1242 &ett_tn3270e_subopt,
1245 NULL /* XXX - fill me in */
1248 "XAUTH", /* XAUTH */
1252 NULL /* XXX - fill me in */
1255 "CHARSET", /* CHARSET */
1256 &ett_charset_subopt,
1259 NULL /* XXX - fill me in */
1262 "Remote Serial Port", /* Remote Serial Port */
1266 NULL /* XXX - fill me in */
1269 "COM Port Control", /* RFC 2217 */
1270 &ett_comport_subopt,
1273 dissect_comport_subopt
1278 #define NOPTIONS (sizeof options / sizeof options[0])
1281 telnet_sub_option(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1283 proto_tree *ti, *option_tree;
1284 int offset = start_offset;
1288 gint ett = ett_telnet_subopt;
1291 tvbuff_t * unescaped_tvb;
1292 void (*dissect)(packet_info *, const char *, tvbuff_t *, int, int, proto_tree *);
1297 * As data with value iac (0xff) is possible, this value must be escaped
1298 * with iac (rfc 854).
1302 offset += 2; /* skip IAC and SB */
1304 /* Get the option code */
1305 opt_byte = tvb_get_guint8(tvb, offset);
1306 if (opt_byte >= NOPTIONS) {
1307 opt = "<unknown option>";
1310 opt = options[opt_byte].name;
1311 if (options[opt_byte].subtree_index != NULL)
1312 ett = *(options[opt_byte].subtree_index);
1313 dissect = options[opt_byte].dissect;
1317 /* Search for an unescaped IAC. */
1318 cur_offset = offset;
1320 len = tvb_length_remaining(tvb, offset);
1322 iac_offset = tvb_find_guint8(tvb, cur_offset, len, TN_IAC);
1324 if (iac_offset == -1) {
1325 /* None found - run to the end of the packet. */
1328 if (((guint)(iac_offset + 1) >= len) ||
1329 (tvb_get_guint8(tvb, iac_offset + 1) != TN_IAC)) {
1330 /* We really found a single IAC, so we're done */
1331 offset = iac_offset;
1334 * We saw an escaped IAC, so we have to move ahead to the
1338 cur_offset = iac_offset + 2;
1343 } while (!iac_found);
1345 subneg_len = offset - start_offset;
1347 ti = proto_tree_add_text(telnet_tree, tvb, start_offset, subneg_len,
1348 "Suboption Begin: %s", opt);
1349 option_tree = proto_item_add_subtree(ti, ett);
1350 start_offset += 3; /* skip IAC, SB, and option code */
1353 if (subneg_len > 0) {
1355 /* Now dissect the suboption parameters. */
1356 if (dissect != NULL) {
1358 switch (options[opt_byte].len_type) {
1361 /* There isn't supposed to *be* sub-option negotiation for this. */
1362 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1363 "Bogus suboption data");
1367 /* Make sure the length is what it's supposed to be. */
1368 if (subneg_len - iac_data != options[opt_byte].optlen) {
1369 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1370 "Suboption parameter length is %d, should be %d",
1371 subneg_len, options[opt_byte].optlen);
1376 case VARIABLE_LENGTH:
1377 /* Make sure the length is greater than the minimum. */
1378 if (subneg_len - iac_data < options[opt_byte].optlen) {
1379 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1380 "Suboption parameter length is %d, should be at least %d",
1381 subneg_len, options[opt_byte].optlen);
1387 /* We have a dissector for this suboption's parameters; call it. */
1389 /* Data is escaped, we have to unescape it. */
1390 unescaped_tvb = unescape_and_tvbuffify_telnet_option(pinfo, tvb, start_offset, subneg_len);
1391 (*dissect)(pinfo, opt, unescaped_tvb, 0, subneg_len - iac_data, option_tree);
1393 (*dissect)(pinfo, opt, tvb, start_offset, subneg_len, option_tree);
1396 /* We don't have a dissector for them; just show them as data. */
1398 /* Data is escaped, we have to unescape it. */
1399 unescaped_tvb = unescape_and_tvbuffify_telnet_option(pinfo, tvb, start_offset, subneg_len);
1400 proto_tree_add_text(option_tree, unescaped_tvb, 0, subneg_len - iac_data,
1403 proto_tree_add_text(option_tree, tvb, start_offset, subneg_len,
1412 telnet_will_wont_do_dont(proto_tree *telnet_tree, tvbuff_t *tvb,
1413 int start_offset, const char *type)
1415 int offset = start_offset;
1419 offset += 2; /* skip IAC and WILL,WONT,DO,DONT} */
1420 opt_byte = tvb_get_guint8(tvb, offset);
1421 if (opt_byte >= NOPTIONS)
1422 opt = "<unknown option>";
1424 opt = options[opt_byte].name;
1427 proto_tree_add_text(telnet_tree, tvb, start_offset, 3,
1428 "Command: %s %s", type, opt);
1433 telnet_command(packet_info *pinfo, proto_tree *telnet_tree, tvbuff_t *tvb, int start_offset)
1435 int offset = start_offset;
1438 offset += 1; /* skip IAC */
1439 optcode = tvb_get_guint8(tvb, offset);
1444 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1445 "Command: End of File");
1449 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1450 "Command: Suspend Current Process");
1454 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1455 "Command: Abort Process");
1459 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1460 "Command: End of Record");
1464 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1465 "Command: Suboption End");
1469 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1470 "Command: No Operation");
1474 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1475 "Command: Data Mark");
1479 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1484 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1485 "Command: Interrupt Process");
1489 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1490 "Command: Abort Output");
1494 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1495 "Command: Are You There?");
1499 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1500 "Command: Escape Character");
1504 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1505 "Command: Erase Line");
1509 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1510 "Command: Go Ahead");
1514 offset = telnet_sub_option(pinfo, telnet_tree, tvb, start_offset);
1518 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1523 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1528 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1533 offset = telnet_will_wont_do_dont(telnet_tree, tvb, start_offset,
1538 proto_tree_add_text(telnet_tree, tvb, start_offset, 2,
1539 "Command: Unknown (0x%02x)", optcode);
1547 telnet_add_text(proto_tree *tree, tvbuff_t *tvb, int offset, int len)
1552 gboolean last_char_was_cr;
1554 while (len != 0 && tvb_offset_exists(tvb, offset)) {
1556 * Find the end of the line.
1558 linelen = tvb_find_line_end(tvb, offset, len, &next_offset, FALSE);
1559 len -= next_offset - offset; /* subtract out the line's characters */
1562 * In Telnet, CR NUL is the way you send a CR by itself in the
1563 * default ASCII mode; don't treat CR by itself as a line ending,
1564 * treat only CR NUL, CR LF, or LF by itself as a line ending.
1566 if (next_offset == offset + linelen + 1 && len >= 1) {
1568 * Well, we saw a one-character line ending, so either it's a CR
1569 * or an LF; we have at least two characters left, including the
1572 * If the line ending is a CR, skip all subsequent CRs; at
1573 * least one capture appeared to have multiple CRs at the end of
1576 if (tvb_get_guint8(tvb, offset + linelen) == '\r') {
1577 last_char_was_cr = TRUE;
1578 while (len != 0 && tvb_offset_exists(tvb, next_offset)) {
1579 c = tvb_get_guint8(tvb, next_offset);
1580 next_offset++; /* skip over that character */
1582 if (c == '\n' || (c == '\0' && last_char_was_cr)) {
1584 * LF is a line ending, whether preceded by CR or not.
1585 * NUL is a line ending if preceded by CR.
1589 last_char_was_cr = (c == '\r');
1595 * Now compute the length of the line *including* the end-of-line
1596 * indication, if any; we display it all.
1598 linelen = next_offset - offset;
1600 proto_tree_add_text(tree, tvb, offset, linelen,
1602 tvb_format_text(tvb, offset, linelen));
1603 offset = next_offset;
1608 dissect_telnet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1610 proto_tree *telnet_tree, *ti;
1613 if (check_col(pinfo->cinfo, COL_PROTOCOL))
1614 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TELNET");
1616 if (check_col(pinfo->cinfo, COL_INFO))
1617 col_set_str(pinfo->cinfo, COL_INFO, "Telnet Data ...");
1625 ti = proto_tree_add_item(tree, proto_telnet, tvb, offset, -1, FALSE);
1626 telnet_tree = proto_item_add_subtree(ti, ett_telnet);
1629 * Scan through the buffer looking for an IAC byte.
1631 while ((len = tvb_length_remaining(tvb, offset)) > 0) {
1632 iac_offset = tvb_find_guint8(tvb, offset, len, TN_IAC);
1633 if (iac_offset != -1) {
1635 * We found an IAC byte.
1636 * If there's any data before it, add that data to the
1637 * tree, a line at a time.
1639 data_len = iac_offset - offset;
1641 telnet_add_text(telnet_tree, tvb, offset, data_len);
1644 * Now interpret the command.
1646 offset = telnet_command(pinfo, telnet_tree, tvb, iac_offset);
1650 * We found no IAC byte, so what remains in the buffer
1651 * is the last of the data in the packet.
1652 * Add it to the tree, a line at a time, and then quit.
1654 telnet_add_text(telnet_tree, tvb, offset, len);
1662 proto_register_telnet(void)
1664 static hf_register_info hf[] = {
1665 { &hf_telnet_auth_name,
1666 { "Name", "telnet.auth.name", FT_STRING, BASE_NONE,
1667 NULL, 0, "Name of user being authenticated", HFILL }},
1668 { &hf_telnet_auth_cmd,
1669 { "Auth Cmd", "telnet.auth.cmd", FT_UINT8, BASE_DEC,
1670 VALS(auth_cmd_vals), 0, "Authentication Command", HFILL }},
1671 { &hf_telnet_auth_type,
1672 { "Auth Type", "telnet.auth.type", FT_UINT8, BASE_DEC,
1673 VALS(auth_type_vals), 0, "Authentication Type", HFILL }},
1674 { &hf_telnet_auth_mod_cred_fwd,
1675 { "Cred Fwd", "telnet.auth.mod.cred_fwd", FT_BOOLEAN, 8,
1676 TFS(&auth_mod_cred_fwd), 0x08, "Modifier: Whether client will forward creds or not", HFILL }},
1677 { &hf_telnet_auth_mod_who,
1678 { "Who", "telnet.auth.mod.who", FT_BOOLEAN, 8,
1679 TFS(&auth_mod_who), 0x01, "Modifier: Who to mask", HFILL }},
1680 { &hf_telnet_auth_mod_how,
1681 { "How", "telnet.auth.mod.how", FT_BOOLEAN, 8,
1682 TFS(&auth_mod_how), 0x02, "Modifier: How to mask", HFILL }},
1683 { &hf_telnet_auth_mod_enc,
1684 { "Encrypt", "telnet.auth.mod.enc", FT_UINT8, BASE_DEC,
1685 VALS(auth_mod_enc), 0x14, "Modifier: How to enable Encryption", HFILL }},
1686 { &hf_telnet_auth_krb5_type,
1687 { "Command", "telnet.auth.krb5.cmd", FT_UINT8, BASE_DEC,
1688 VALS(auth_krb5_types), 0, "Krb5 Authentication sub-command", HFILL }},
1689 { &hf_telnet_enc_cmd,
1690 { "Enc Cmd", "telnet.enc.cmd", FT_UINT8, BASE_DEC,
1691 VALS(enc_cmd_vals), 0, "Encryption command", HFILL }},
1692 { &hf_telnet_enc_type,
1693 { "Enc Type", "telnet.enc.type", FT_UINT8, BASE_DEC,
1694 VALS(enc_type_vals), 0, "Encryption type", HFILL }},
1696 static gint *ett[] = {
1704 &ett_htstops_subopt,
1707 &ett_vtstops_subopt,
1711 &ett_bytemacro_subopt,
1713 &ett_supdupout_subopt,
1714 &ett_sendloc_subopt,
1715 &ett_termtype_subopt,
1716 &ett_tacacsui_subopt,
1717 &ett_outmark_subopt,
1718 &ett_tlocnum_subopt,
1719 &ett_tn3270reg_subopt,
1724 &ett_linemode_subopt,
1725 &ett_xdpyloc_subopt,
1730 &ett_tn3270e_subopt,
1732 &ett_charset_subopt,
1737 proto_telnet = proto_register_protocol("Telnet", "TELNET", "telnet");
1738 proto_register_field_array(proto_telnet, hf, array_length(hf));
1739 proto_register_subtree_array(ett, array_length(ett));
1743 proto_reg_handoff_telnet(void)
1745 dissector_handle_t telnet_handle;
1747 telnet_handle = create_dissector_handle(dissect_telnet, proto_telnet);
1748 dissector_add("tcp.port", TCP_PORT_TELNET, telnet_handle);