From: David van Moolenbroek Date: Wed, 15 Feb 2017 17:41:07 +0000 (+0000) Subject: Import NetBSD telnet(1) X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=refs%2Fchanges%2F62%2F3462%2F1;p=minix.git Import NetBSD telnet(1) Change-Id: Ib58b43cc9baabe183a59410212827f65ec117277 --- diff --git a/distrib/sets/lists/minix-base/mi b/distrib/sets/lists/minix-base/mi index b4c340517..6c02eb5cc 100644 --- a/distrib/sets/lists/minix-base/mi +++ b/distrib/sets/lists/minix-base/mi @@ -536,6 +536,7 @@ ./usr/bin/tcpdp minix-base obsolete ./usr/bin/tcpstat minix-base obsolete ./usr/bin/tee minix-base +./usr/bin/telnet minix-base ./usr/bin/term minix-base ./usr/bin/termcap minix-base ./usr/bin/texi2dvi minix-base diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index 949bb1c01..abfe3e624 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -427,6 +427,7 @@ ./usr/libdata/debug/usr/bin/tcpdp.debug minix-debug debug,obsolete ./usr/libdata/debug/usr/bin/tcpstat.debug minix-debug debug,obsolete ./usr/libdata/debug/usr/bin/tee.debug minix-debug debug +./usr/libdata/debug/usr/bin/telnet.debug minix-debug debug ./usr/libdata/debug/usr/bin/term.debug minix-debug debug ./usr/libdata/debug/usr/bin/termcap.debug minix-debug debug ./usr/libdata/debug/usr/bin/texindex.debug minix-debug debug diff --git a/distrib/sets/lists/minix-man/mi b/distrib/sets/lists/minix-man/mi index ebe1ab2bb..c8019ee63 100644 --- a/distrib/sets/lists/minix-man/mi +++ b/distrib/sets/lists/minix-man/mi @@ -366,7 +366,7 @@ ./usr/man/man1/tail.1 minix-man ./usr/man/man1/tar.1 minix-man ./usr/man/man1/tee.1 minix-man -./usr/man/man1/telnet.1 minix-man obsolete +./usr/man/man1/telnet.1 minix-man ./usr/man/man1/template.1 minix-man obsolete ./usr/man/man1/term.1 minix-man ./usr/man/man1/termcap.1 minix-man diff --git a/lib/Makefile b/lib/Makefile index d46144741..2ac7b4419 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -80,7 +80,7 @@ SUBDIR+= \ libintl libkvm libm \ libpci libprop \ libpuffs librmt \ - libterminfo \ + libtelnet libterminfo \ libutil libwrap libz .if !defined(BSD_MK_COMPAT_FILE) diff --git a/lib/libtelnet/Makefile b/lib/libtelnet/Makefile new file mode 100644 index 000000000..fde8b8dc0 --- /dev/null +++ b/lib/libtelnet/Makefile @@ -0,0 +1,38 @@ +# from: @(#)Makefile 8.2 (Berkeley) 12/15/93 +# $NetBSD: Makefile,v 1.36 2012/08/10 12:20:10 joerg Exp $ + +USE_FORT?= yes # network protocol library + +LIBISPRIVATE= yes + +.include + +WARNS?= 5 + +LIB= telnet +SRCS= auth.c encrypt.c genget.c getent.c misc.c + +CPPFLAGS+= -DHAS_CGETENT +CPPFLAGS+= -I${.CURDIR} + +.if ${MKCRYPTO} != "no" +SRCS+= enc_des.c +CPPFLAGS+= -DENCRYPTION -DAUTHENTICATION +CPPFLAGS+= -DDES_ENCRYPTION +.endif + +.if ${USE_KERBEROS} != "no" +SRCS+= kerberos5.c +CPPFLAGS+= -DKRB5 +.endif + +.if ${USE_PAM} != "no" && ${MKCRYPTO} != "no" +SRCS+= sra.c pk.c +CPPFLAGS+= -DSRA +.endif + +.for f in auth enc_des kerberos5 pk +COPTS.${f}.c+= -Wno-pointer-sign +.endfor + +.include diff --git a/lib/libtelnet/auth-proto.h b/lib/libtelnet/auth-proto.h new file mode 100644 index 000000000..4262553a7 --- /dev/null +++ b/lib/libtelnet/auth-proto.h @@ -0,0 +1,103 @@ +/* $NetBSD: auth-proto.h,v 1.15 2006/03/20 21:23:47 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)auth-proto.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef _LIBTELNET_AUTH_PROTO_H_ +#define _LIBTELNET_AUTH_PROTO_H_ +#include + +#ifdef AUTHENTICATION +Authenticator *findauthenticator(int, int); + +void auth_init(const char *, int); +int auth_cmd(int, char **); +void auth_request(void); +void auth_send(unsigned char *, int); +void auth_send_retry(void); +void auth_is(unsigned char *, int); +void auth_reply(unsigned char *, int); +void auth_disable_name(char *); +void auth_gen_printsub(unsigned char *, int, unsigned char *, int); + +int getauthmask(char *, int *); +int auth_enable(char *); +int auth_disable(char *); +int auth_onoff(char *, int); +int auth_togdebug(int); +int auth_status(char *); +void auth_name(unsigned char *, int); +int auth_sendname(unsigned char *, int); +void auth_finished(Authenticator *, int); +int auth_wait(char *, size_t); +void auth_debug(int); +void auth_printsub(unsigned char *, int, unsigned char *, int); + +#ifdef KRB5 +int kerberos5_init(Authenticator *, int); +int kerberos5_send(Authenticator *); +void kerberos5_is(Authenticator *, unsigned char *, int); +void kerberos5_reply(Authenticator *, unsigned char *, int); +int kerberos5_status(Authenticator *, char *, size_t, int); +void kerberos5_printsub(unsigned char *, int, unsigned char *, int); +#endif + +#ifdef SRA +int sra_init(Authenticator *, int); +int sra_send(Authenticator *); +void sra_is(Authenticator *, unsigned char *, int); +void sra_reply(Authenticator *, unsigned char *, int); +int sra_status(Authenticator *, char *, size_t, int); +void sra_printsub(unsigned char *, int, unsigned char *, int); +#endif + +#endif /* AUTHENTICATION */ +#endif /* _LIBTELNET_AUTH_PROTO_H_ */ diff --git a/lib/libtelnet/auth.c b/lib/libtelnet/auth.c new file mode 100644 index 000000000..3db63333f --- /dev/null +++ b/lib/libtelnet/auth.c @@ -0,0 +1,617 @@ +/* $NetBSD: auth.c,v 1.21 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)auth.c 8.3 (Berkeley) 5/30/95" +#else +__RCSID("$NetBSD: auth.c,v 1.21 2012/03/21 05:33:27 matt Exp $"); +#endif +#endif /* not lint */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + + +#ifdef AUTHENTICATION +#include +#include +#include +#define AUTH_NAMES +#include +#include +#include +#ifdef NO_STRING_H +#include +#else +#include +#endif + +#include "encrypt.h" +#include "auth.h" +#include "misc-proto.h" +#include "auth-proto.h" + +#define typemask(x) (1<<((x)-1)) + +#ifdef RSA_ENCPWD +extern rsaencpwd_init(); +extern rsaencpwd_send(); +extern rsaencpwd_is(); +extern rsaencpwd_reply(); +extern rsaencpwd_status(); +extern rsaencpwd_printsub(); +#endif + +int auth_debug_mode = 0; +static const char *Name = "Noname"; +static int Server = 0; +static Authenticator *authenticated = 0; +static int authenticating = 0; +static int validuser = 0; +static unsigned char _auth_send_data[256]; +static unsigned char *auth_send_data; +static int auth_send_cnt = 0; + +static void auth_intr(int); + +/* + * Authentication types supported. Plese note that these are stored + * in priority order, i.e. try the first one first. + */ +Authenticator authenticators[] = { +#ifdef SPX + { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL, + spx_init, + spx_send, + spx_is, + spx_reply, + spx_status, + spx_printsub }, + { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY, + spx_init, + spx_send, + spx_is, + spx_reply, + spx_status, + spx_printsub }, +#endif +#ifdef KRB5 +# ifdef ENCRYPTION + { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL, + kerberos5_init, + kerberos5_send, + kerberos5_is, + kerberos5_reply, + kerberos5_status, + kerberos5_printsub }, +# endif /* ENCRYPTION */ + { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY, + kerberos5_init, + kerberos5_send, + kerberos5_is, + kerberos5_reply, + kerberos5_status, + kerberos5_printsub }, +#endif +#ifdef RSA_ENCPWD + { AUTHTYPE_RSA_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY, + rsaencpwd_init, + rsaencpwd_send, + rsaencpwd_is, + rsaencpwd_reply, + rsaencpwd_status, + rsaencpwd_printsub }, +#endif +#ifdef SRA + { AUTHTYPE_SRA, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY, + sra_init, + sra_send, + sra_is, + sra_reply, + sra_status, + sra_printsub }, + +#endif + { 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +static Authenticator NoAuth = { .type = 0 }; + +static int i_support = 0; +static int i_wont_support = 0; + +Authenticator * +findauthenticator(int type, int way) +{ + Authenticator *ap = authenticators; + + while (ap->type && (ap->type != type || ap->way != way)) + ++ap; + return(ap->type ? ap : 0); +} + +void +auth_init(const char *name, int server) +{ + Authenticator *ap = authenticators; + + Server = server; + Name = name; + + i_support = 0; + authenticated = 0; + authenticating = 0; + while (ap->type) { + if (!ap->init || (*ap->init)(ap, server)) { + i_support |= typemask(ap->type); + if (auth_debug_mode) + printf(">>>%s: I support auth type %d %d\r\n", + Name, + ap->type, ap->way); + } + else if (auth_debug_mode) + printf(">>>%s: Init failed: auth type %d %d\r\n", + Name, ap->type, ap->way); + ++ap; + } +} + +void +auth_disable_name(char *name) +{ + int x; + for (x = 0; x < AUTHTYPE_CNT; ++x) { + if (AUTHTYPE_NAME(x) && !strcasecmp(name, AUTHTYPE_NAME(x))) { + i_wont_support |= typemask(x); + break; + } + } +} + +int +getauthmask(char *type, int *maskp) +{ + register int x; + + if (AUTHTYPE_NAME(0) && !strcasecmp(type, AUTHTYPE_NAME(0))) { + *maskp = -1; + return(1); + } + + for (x = 1; x < AUTHTYPE_CNT; ++x) { + if (AUTHTYPE_NAME(x) && !strcasecmp(type, AUTHTYPE_NAME(x))) { + *maskp = typemask(x); + return(1); + } + } + return(0); +} + +int +auth_enable(char *type) +{ + return(auth_onoff(type, 1)); +} + +int +auth_disable(char *type) +{ + return(auth_onoff(type, 0)); +} + +int +auth_onoff(char *type, int on) +{ + int i, mask = -1; + Authenticator *ap; + + if (!strcasecmp(type, "?") || !strcasecmp(type, "help")) { + printf("auth %s 'type'\n", on ? "enable" : "disable"); + printf("Where 'type' is one of:\n"); + printf("\t%s\n", AUTHTYPE_NAME(0)); + mask = 0; + for (ap = authenticators; ap->type; ap++) { + if ((mask & (i = typemask(ap->type))) != 0) + continue; + mask |= i; + printf("\t%s\n", AUTHTYPE_NAME(ap->type)); + } + return(0); + } + + if (!getauthmask(type, &mask)) { + printf("%s: invalid authentication type\n", type); + return(0); + } + if (on) + i_wont_support &= ~mask; + else + i_wont_support |= mask; + return(1); +} + +int +auth_togdebug(int on) +{ + if (on < 0) + auth_debug_mode ^= 1; + else + auth_debug_mode = on; + printf("auth debugging %s\n", auth_debug_mode ? "enabled" : "disabled"); + return(1); +} + +int +auth_status(char *s) +{ + Authenticator *ap; + int i, mask; + + if (i_wont_support == -1) + printf("Authentication disabled\n"); + else + printf("Authentication enabled\n"); + + mask = 0; + for (ap = authenticators; ap->type; ap++) { + if ((mask & (i = typemask(ap->type))) != 0) + continue; + mask |= i; + printf("%s: %s\n", AUTHTYPE_NAME(ap->type), + (i_wont_support & typemask(ap->type)) ? + "disabled" : "enabled"); + } + return(1); +} + +/* + * This routine is called by the server to start authentication + * negotiation. + */ +void +auth_request(void) +{ + static unsigned char str_request[64] = { IAC, SB, + TELOPT_AUTHENTICATION, + TELQUAL_SEND, }; + Authenticator *ap = authenticators; + unsigned char *e = str_request + 4; + + if (!authenticating) { + authenticating = 1; + while (ap->type) { + if (i_support & ~i_wont_support & typemask(ap->type)) { + if (auth_debug_mode) { + printf(">>>%s: Sending type %d %d\r\n", + Name, ap->type, ap->way); + } + *e++ = ap->type; + *e++ = ap->way; + } + ++ap; + } + *e++ = IAC; + *e++ = SE; + telnet_net_write(str_request, e - str_request); + printsub('>', &str_request[2], e - str_request - 2); + } +} + +/* + * This is called when an AUTH SEND is received. + * It should never arrive on the server side (as only the server can + * send an AUTH SEND). + * You should probably respond to it if you can... + * + * If you want to respond to the types out of order (i.e. even + * if he sends LOGIN KERBEROS and you support both, you respond + * with KERBEROS instead of LOGIN (which is against what the + * protocol says)) you will have to hack this code... + */ +void +auth_send(unsigned char *data, int cnt) +{ + Authenticator *ap; + static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION, + TELQUAL_IS, AUTHTYPE_NULL, 0, + IAC, SE }; + if (Server) { + if (auth_debug_mode) { + printf(">>>%s: auth_send called!\r\n", Name); + } + return; + } + + if (auth_debug_mode) { + printf(">>>%s: auth_send got:", Name); + printd(data, cnt); printf("\r\n"); + } + + /* + * Save the data, if it is new, so that we can continue looking + * at it if the authorization we try doesn't work + */ + if (data < _auth_send_data || + data > _auth_send_data + sizeof(_auth_send_data)) { + auth_send_cnt = (size_t)cnt > sizeof(_auth_send_data) + ? sizeof(_auth_send_data) + : (size_t)cnt; + memmove(_auth_send_data, data, auth_send_cnt); + auth_send_data = _auth_send_data; + } else { + /* + * This is probably a no-op, but we just make sure + */ + auth_send_data = data; + auth_send_cnt = cnt; + } + while ((auth_send_cnt -= 2) >= 0) { + if (auth_debug_mode) + printf(">>>%s: He supports %d\r\n", + Name, *auth_send_data); + if ((i_support & ~i_wont_support) & typemask(*auth_send_data)) { + ap = findauthenticator(auth_send_data[0], + auth_send_data[1]); + if (ap && ap->send) { + if (auth_debug_mode) + printf(">>>%s: Trying %d %d\r\n", + Name, auth_send_data[0], + auth_send_data[1]); + if ((*ap->send)(ap)) { + /* + * Okay, we found one we like + * and did it. + * we can go home now. + */ + if (auth_debug_mode) + printf(">>>%s: Using type %d\r\n", + Name, *auth_send_data); + auth_send_data += 2; + return; + } + } + /* else + * just continue on and look for the + * next one if we didn't do anything. + */ + } + auth_send_data += 2; + } + telnet_net_write(str_none, sizeof(str_none)); + printsub('>', &str_none[2], sizeof(str_none) - 2); + if (auth_debug_mode) + printf(">>>%s: Sent failure message\r\n", Name); + auth_finished(0, AUTH_REJECT); +#ifdef KANNAN + /* + * We requested strong authentication, however no mechanisms worked. + * Therefore, exit on client end. + */ + printf("Unable to securely authenticate user ... exit\n"); + exit(0); +#endif /* KANNAN */ +} + +void +auth_send_retry(void) +{ + /* + * if auth_send_cnt <= 0 then auth_send will end up rejecting + * the authentication and informing the other side of this. + */ + auth_send(auth_send_data, auth_send_cnt); +} + +void +auth_is(unsigned char *data, int cnt) +{ + Authenticator *ap; + + if (cnt < 2) + return; + + if (data[0] == AUTHTYPE_NULL) { + auth_finished(0, AUTH_REJECT); + return; + } + + if ((ap = findauthenticator(data[0], data[1])) != NULL) { + if (ap->is) + (*ap->is)(ap, data+2, cnt-2); + } else if (auth_debug_mode) + printf(">>>%s: Invalid authentication in IS: %d\r\n", + Name, *data); +} + +void +auth_reply(unsigned char *data, int cnt) +{ + Authenticator *ap; + + if (cnt < 2) + return; + + if ((ap = findauthenticator(data[0], data[1])) != NULL) { + if (ap->reply) + (*ap->reply)(ap, data+2, cnt-2); + } else if (auth_debug_mode) + printf(">>>%s: Invalid authentication in SEND: %d\r\n", + Name, *data); +} + +void +auth_name(unsigned char *data, int cnt) +{ + unsigned char savename[256]; + + if (cnt < 1) { + if (auth_debug_mode) + printf(">>>%s: Empty name in NAME\r\n", Name); + return; + } + if ((size_t)cnt > sizeof(savename) - 1) { + if (auth_debug_mode) + printf(">>>%s: Name in NAME (%d) exceeds %ld length\r\n", + Name, cnt, (long)sizeof(savename)-1); + return; + } + memmove((void *)savename, (void *)data, cnt); + savename[cnt] = '\0'; /* Null terminate */ + if (auth_debug_mode) + printf(">>>%s: Got NAME [%s]\r\n", Name, savename); + auth_encrypt_user(savename); +} + +int +auth_sendname(unsigned char *cp, int len) +{ + static unsigned char str_request[256+6] + = { IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME, }; + register unsigned char *e = str_request + 4; + register unsigned char *ee = &str_request[sizeof(str_request)-2]; + + while (--len >= 0) { + if ((*e++ = *cp++) == IAC) + *e++ = IAC; + if (e >= ee) + return(0); + } + *e++ = IAC; + *e++ = SE; + telnet_net_write(str_request, e - str_request); + printsub('>', &str_request[2], e - &str_request[2]); + return(1); +} + +void +auth_finished(Authenticator *ap, int result) +{ + if (!(authenticated = ap)) + authenticated = &NoAuth; + validuser = result; +} + + /* ARGSUSED */ +static void +auth_intr(int sig) +{ + auth_finished(0, AUTH_REJECT); +} + +int +auth_wait(char *name, size_t l) +{ + if (auth_debug_mode) + printf(">>>%s: in auth_wait.\r\n", Name); + + if (Server && !authenticating) + return(0); + + (void) signal(SIGALRM, auth_intr); + alarm(30); + while (!authenticated) + if (telnet_spin()) + break; + alarm(0); + (void) signal(SIGALRM, SIG_DFL); + + /* + * Now check to see if the user is valid or not + */ + if (!authenticated || authenticated == &NoAuth) + return(AUTH_REJECT); + + if (validuser == AUTH_VALID) + validuser = AUTH_USER; + + if (authenticated->status) + validuser = (*authenticated->status)(authenticated, + name, l, validuser); + return(validuser); +} + +void +auth_debug(int mode) +{ + auth_debug_mode = mode; +} + +void +auth_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) +{ + Authenticator *ap; + + if ((ap = findauthenticator(data[1], data[2])) && ap->printsub) + (*ap->printsub)(data, cnt, buf, buflen); + else + auth_gen_printsub(data, cnt, buf, buflen); +} + +void +auth_gen_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) +{ + register unsigned char *cp; + unsigned char tbuf[16]; + + cnt -= 3; + data += 3; + buf[buflen-1] = '\0'; + buf[buflen-2] = '*'; + buflen -= 2; + for (; cnt > 0; cnt--, data++) { + snprintf((char *)tbuf, sizeof(tbuf), " %d", *data); + for (cp = tbuf; *cp && buflen > 0; --buflen) + *buf++ = *cp++; + if (buflen <= 0) + return; + } + *buf = '\0'; +} +#endif diff --git a/lib/libtelnet/auth.h b/lib/libtelnet/auth.h new file mode 100644 index 000000000..aab6f9373 --- /dev/null +++ b/lib/libtelnet/auth.h @@ -0,0 +1,80 @@ +/* $NetBSD: auth.h,v 1.11 2005/02/06 05:53:07 perry Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)auth.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef __AUTH__ +#define __AUTH__ + +#define AUTH_REJECT 0 /* Rejected */ +#define AUTH_UNKNOWN 1 /* We don't know who he is, but he's okay */ +#define AUTH_OTHER 2 /* We know him, but not his name */ +#define AUTH_USER 3 /* We know he name */ +#define AUTH_VALID 4 /* We know him, and he needs no password */ + +typedef struct XauthP { + int type; + int way; + int (*init)(struct XauthP *, int); + int (*send)(struct XauthP *); + void (*is)(struct XauthP *, unsigned char *, int); + void (*reply)(struct XauthP *, unsigned char *, int); + int (*status)(struct XauthP *, char *, size_t, int); + void (*printsub)(unsigned char *, int, unsigned char *, int); +} Authenticator; + +#include "auth-proto.h" + +#define OPTS_FORWARD_CREDS 0x00000002 +#define OPTS_FORWARDABLE_CREDS 0x00000001 + +extern int auth_debug_mode; +#endif diff --git a/lib/libtelnet/enc-proto.h b/lib/libtelnet/enc-proto.h new file mode 100644 index 000000000..5e62de060 --- /dev/null +++ b/lib/libtelnet/enc-proto.h @@ -0,0 +1,140 @@ +/* $NetBSD: enc-proto.h,v 1.9 2012/01/09 15:25:33 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)enc-proto.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifdef ENCRYPTION +#include + +Encryptions *findencryption(int); +Encryptions *finddecryption(int); +void encrypt_init(const char *, int); +void encrypt_list_types(void); +int EncryptEnable(char *, char *); +int EncryptDisable(char *, char *); +int EncryptType(char *, char *); +int EncryptStart(char *); +int EncryptStartInput(void); +int EncryptStartOutput(void); +int EncryptStop(char *); +int EncryptStopInput(void); +int EncryptStopOutput(void); +int EncryptStatus(void); +void encrypt_send_support(void); +int EncryptDebug(int); +int EncryptVerbose(int); +int EncryptAutoEnc(int); +int EncryptAutoDec(int); +void encrypt_support(unsigned char *, int); +void encrypt_is(unsigned char *, int); +void encrypt_reply(unsigned char *, int); +void encrypt_start(unsigned char *, int); +void encrypt_session_key(Session_Key *, int); +void encrypt_end(void); +void encrypt_request_end(void); +void encrypt_request_start(unsigned char *, int); +void encrypt_enc_keyid(unsigned char *, int); +void encrypt_dec_keyid(unsigned char *, int); +struct key_info; +void encrypt_keyid(struct key_info *, unsigned char *, int); +void encrypt_send_keyid(int, const unsigned char *, int, int); +void encrypt_auto(int); +void decrypt_auto(int); +void encrypt_start_output(int); +void encrypt_send_end(void); +void encrypt_send_request_start(void); +void encrypt_send_request_end(void); +void encrypt_wait(void); +void encrypt_debug(int); +void encrypt_gen_printsub(unsigned char *, int, unsigned char *, int ); +void encrypt_printsub(unsigned char *, int, unsigned char *, int ); + +#ifdef TELENTD +void encrypt_wait(void); +#else +void printsub(int, unsigned char *, int); +int encrypt_cmd(int, char **); +void encrypt_display(void); +#endif + +void krbdes_encrypt(unsigned char *, int); +int krbdes_decrypt(int); +int krbdes_is(unsigned char *, int); +int krbdes_reply(unsigned char *, int); +void krbdes_init(int); +int krbdes_start(int, int); +void krbdes_session(Session_Key *, int); +void krbdes_printsub(unsigned char *, int, unsigned char *, int); + +void cfb64_encrypt(unsigned char *, int); +int cfb64_decrypt(int); +void cfb64_init(int); +int cfb64_start(int, int); +int cfb64_is(unsigned char *, int); +int cfb64_reply(unsigned char *, int); +void cfb64_session(Session_Key *, int); +int cfb64_keyid(int, unsigned char *, int *); +void cfb64_printsub(unsigned char *, int, unsigned char *, int); + +void ofb64_encrypt(unsigned char *, int); +int ofb64_decrypt(int); +void ofb64_init(int); +int ofb64_start(int, int); +int ofb64_is(unsigned char *, int); +int ofb64_reply(unsigned char *, int); +void ofb64_session(Session_Key *, int); +int ofb64_keyid(int, unsigned char *, int *); +void ofb64_printsub(unsigned char *, int, unsigned char *, int); + +void fb64_printsub(const unsigned char *, int, unsigned char *, int, + const unsigned char *); + +#endif /* ENCRYPTION */ diff --git a/lib/libtelnet/enc_des.c b/lib/libtelnet/enc_des.c new file mode 100644 index 000000000..e1cde4ddf --- /dev/null +++ b/lib/libtelnet/enc_des.c @@ -0,0 +1,665 @@ +/* $NetBSD: enc_des.c,v 1.16 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)enc_des.c 8.3 (Berkeley) 5/30/95"; */ +#else +__RCSID("$NetBSD: enc_des.c,v 1.16 2012/03/21 05:33:27 matt Exp $"); +#endif +#endif /* not lint */ + +#ifdef ENCRYPTION +# ifdef AUTHENTICATION +# ifdef DES_ENCRYPTION +#include +#include +#include +#include + +#include +#include "encrypt.h" +#include "key-proto.h" +#include "misc-proto.h" + +#define CFB 0 +#define OFB 1 + +#define NO_SEND_IV 1 +#define NO_RECV_IV 2 +#define NO_KEYID 4 +#define IN_PROGRESS (NO_SEND_IV|NO_RECV_IV|NO_KEYID) +#define SUCCESS 0 +#define FAILED -1 + + +struct fb { + Block krbdes_key; + Schedule krbdes_sched; + Block temp_feed; + unsigned char fb_feed[64]; + int need_start; + int state[2]; + int keyid[2]; + int once; + struct stinfo { + Block str_output; + Block str_feed; + Block str_iv; + Block str_ikey; + Schedule str_sched; + int str_index; + int str_flagshift; + } streams[2]; +}; + +static struct fb fb[2]; + +struct keyidlist { + const char *keyid; + int keyidlen; + char *key; + int keylen; + int flags; +} keyidlist [] = { + { "\0", 1, 0, 0, 0 }, /* default key of zero */ + { 0, 0, 0, 0, 0 } +}; + +#define KEYFLAG_MASK 03 + +#define KEYFLAG_NOINIT 00 +#define KEYFLAG_INIT 01 +#define KEYFLAG_OK 02 +#define KEYFLAG_BAD 03 + +#define KEYFLAG_SHIFT 2 + +#define SHIFT_VAL(a,b) (KEYFLAG_SHIFT*((a)+((b)*2))) + +#define FB64_IV 1 +#define FB64_IV_OK 2 +#define FB64_IV_BAD 3 + + +void fb64_stream_iv(Block, struct stinfo *); +void fb64_init(struct fb *); +static int fb64_start(struct fb *, int, int); +int fb64_is(unsigned char *, int, struct fb *); +int fb64_reply(unsigned char *, int, struct fb *); +static void fb64_session(Session_Key *, int, struct fb *); +void fb64_stream_key(Block *, struct stinfo *); +int fb64_keyid(int, unsigned char *, int *, struct fb *); + +void +cfb64_init(int server) +{ + fb64_init(&fb[CFB]); + fb[CFB].fb_feed[4] = ENCTYPE_DES_CFB64; + fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, CFB); + fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, CFB); +} + +void +ofb64_init(int server) +{ + fb64_init(&fb[OFB]); + fb[OFB].fb_feed[4] = ENCTYPE_DES_OFB64; + fb[CFB].streams[0].str_flagshift = SHIFT_VAL(0, OFB); + fb[CFB].streams[1].str_flagshift = SHIFT_VAL(1, OFB); +} + +void +fb64_init(register struct fb *fbp) +{ + memset((void *)fbp, 0, sizeof(*fbp)); + fbp->state[0] = fbp->state[1] = FAILED; + fbp->fb_feed[0] = IAC; + fbp->fb_feed[1] = SB; + fbp->fb_feed[2] = TELOPT_ENCRYPT; + fbp->fb_feed[3] = ENCRYPT_IS; +} + +/* + * Returns: + * -1: some error. Negotiation is done, encryption not ready. + * 0: Successful, initial negotiation all done. + * 1: successful, negotiation not done yet. + * 2: Not yet. Other things (like getting the key from + * Kerberos) have to happen before we can continue. + */ +int +cfb64_start(int dir, int server) +{ + return(fb64_start(&fb[CFB], dir, server)); +} + +int +ofb64_start(int dir, int server) +{ + return(fb64_start(&fb[OFB], dir, server)); +} + +static int +fb64_start(struct fb *fbp, int dir, int server) +{ + size_t x; + unsigned char *p; + register int state; + + switch (dir) { + case DIR_DECRYPT: + /* + * This is simply a request to have the other side + * start output (our input). He will negotiate an + * IV so we need not look for it. + */ + state = fbp->state[dir-1]; + if (state == FAILED) + state = IN_PROGRESS; + break; + + case DIR_ENCRYPT: + state = fbp->state[dir-1]; + if (state == FAILED) + state = IN_PROGRESS; + else if ((state & NO_SEND_IV) == 0) + break; + + if (!VALIDKEY(fbp->krbdes_key)) { + fbp->need_start = 1; + break; + } + state &= ~NO_SEND_IV; + state |= NO_RECV_IV; + if (encrypt_debug_mode) + printf("Creating new feed\r\n"); + /* + * Create a random feed and send it over. + */ + des_new_random_key(&fbp->temp_feed); + des_ecb_encrypt(&fbp->temp_feed, &fbp->temp_feed, + fbp->krbdes_sched, 1); + p = fbp->fb_feed + 3; + *p++ = ENCRYPT_IS; + p++; + *p++ = FB64_IV; + for (x = 0; x < sizeof(Block); ++x) { + if ((*p++ = fbp->temp_feed[x]) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]); + telnet_net_write(fbp->fb_feed, p - fbp->fb_feed); + break; + default: + return(FAILED); + } + return(fbp->state[dir-1] = state); +} + +/* + * Returns: + * -1: some error. Negotiation is done, encryption not ready. + * 0: Successful, initial negotiation all done. + * 1: successful, negotiation not done yet. + */ +int +cfb64_is(unsigned char *data, int cnt) +{ + return(fb64_is(data, cnt, &fb[CFB])); +} +int +ofb64_is(unsigned char *data, int cnt) +{ + return(fb64_is(data, cnt, &fb[OFB])); +} + +int +fb64_is(unsigned char *data, int cnt, struct fb *fbp) +{ + unsigned char *p; + register int state = fbp->state[DIR_DECRYPT-1]; + + if (cnt-- < 1) + goto failure; + + switch (*data++) { + case FB64_IV: + if (cnt != sizeof(Block)) { + if (encrypt_debug_mode) + printf("CFB64: initial vector failed on size\r\n"); + state = FAILED; + goto failure; + } + + if (encrypt_debug_mode) + printf("CFB64: initial vector received\r\n"); + + if (encrypt_debug_mode) + printf("Initializing Decrypt stream\r\n"); + + fb64_stream_iv((void *)data, &fbp->streams[DIR_DECRYPT-1]); + + p = fbp->fb_feed + 3; + *p++ = ENCRYPT_REPLY; + p++; + *p++ = FB64_IV_OK; + *p++ = IAC; + *p++ = SE; + printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]); + telnet_net_write(fbp->fb_feed, p - fbp->fb_feed); + + state = fbp->state[DIR_DECRYPT-1] = IN_PROGRESS; + break; + + default: + if (encrypt_debug_mode) { + printf("Unknown option type: %d\r\n", *(data-1)); + printd(data, cnt); + printf("\r\n"); + } + /* FALL THROUGH */ + failure: + /* + * We failed. Send an FB64_IV_BAD option + * to the other side so it will know that + * things failed. + */ + p = fbp->fb_feed + 3; + *p++ = ENCRYPT_REPLY; + p++; + *p++ = FB64_IV_BAD; + *p++ = IAC; + *p++ = SE; + printsub('>', &fbp->fb_feed[2], p - &fbp->fb_feed[2]); + telnet_net_write(fbp->fb_feed, p - fbp->fb_feed); + + break; + } + return(fbp->state[DIR_DECRYPT-1] = state); +} + +/* + * Returns: + * -1: some error. Negotiation is done, encryption not ready. + * 0: Successful, initial negotiation all done. + * 1: successful, negotiation not done yet. + */ +int +cfb64_reply(unsigned char *data, int cnt) +{ + return(fb64_reply(data, cnt, &fb[CFB])); +} +int +ofb64_reply(unsigned char *data, int cnt) +{ + return(fb64_reply(data, cnt, &fb[OFB])); +} + +int +fb64_reply(unsigned char *data, int cnt, struct fb *fbp) +{ + register int state = fbp->state[DIR_ENCRYPT-1]; + + if (cnt-- < 1) + goto failure; + + switch (*data++) { + case FB64_IV_OK: + fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); + if (state == FAILED) + state = IN_PROGRESS; + state &= ~NO_RECV_IV; + encrypt_send_keyid(DIR_ENCRYPT, (const unsigned char *)"\0", 1, 1); + break; + + case FB64_IV_BAD: + memset(fbp->temp_feed, 0, sizeof(Block)); + fb64_stream_iv(fbp->temp_feed, &fbp->streams[DIR_ENCRYPT-1]); + state = FAILED; + break; + + default: + if (encrypt_debug_mode) { + printf("Unknown option type: %d\r\n", data[-1]); + printd(data, cnt); + printf("\r\n"); + } + /* FALL THROUGH */ + failure: + state = FAILED; + break; + } + return(fbp->state[DIR_ENCRYPT-1] = state); +} + +void +cfb64_session(Session_Key *key, int server) +{ + fb64_session(key, server, &fb[CFB]); +} + +void +ofb64_session( Session_Key *key, int server) +{ + fb64_session(key, server, &fb[OFB]); +} + +static void +fb64_session(Session_Key *key, int server, struct fb *fbp) +{ + + if (!key || key->type != SK_DES) { + if (encrypt_debug_mode) + printf("Can't set krbdes's session key (%d != %d)\r\n", + key ? key->type : -1, SK_DES); + return; + } + memmove((void *)fbp->krbdes_key, (void *)key->data, sizeof(Block)); + + fb64_stream_key(&fbp->krbdes_key, &fbp->streams[DIR_ENCRYPT-1]); + fb64_stream_key(&fbp->krbdes_key, &fbp->streams[DIR_DECRYPT-1]); + + if (fbp->once == 0) { + des_init_random_number_generator(&fbp->krbdes_key); + fbp->once = 1; + } + des_key_sched(&fbp->krbdes_key, fbp->krbdes_sched); + /* + * Now look to see if krbdes_start() was waiting for the key to + * show up. If so, go ahead an call it now that we have the key. + */ + if (fbp->need_start) { + fbp->need_start = 0; + fb64_start(fbp, DIR_ENCRYPT, server); + } +} + +/* + * We only accept a keyid of 0. If we get a keyid of + * 0, then mark the state as SUCCESS. + */ +int +cfb64_keyid(int dir, unsigned char *kp, int *lenp) +{ + return(fb64_keyid(dir, kp, lenp, &fb[CFB])); +} + + int +ofb64_keyid(int dir, unsigned char *kp, int *lenp) +{ + return(fb64_keyid(dir, kp, lenp, &fb[OFB])); +} + +int +fb64_keyid(int dir, unsigned char *kp, int *lenp, struct fb *fbp) +{ + register int state = fbp->state[dir-1]; + + if (*lenp != 1 || (*kp != '\0')) { + *lenp = 0; + return(state); + } + + if (state == FAILED) + state = IN_PROGRESS; + + state &= ~NO_KEYID; + + return(fbp->state[dir-1] = state); +} + +void +fb64_printsub(const unsigned char *data, int cnt, unsigned char *buf, + int buflen, const unsigned char *type) +{ + char lbuf[32]; + register int i; + char *cp; + + buf[buflen-1] = '\0'; /* make sure it's NULL terminated */ + buflen -= 1; + + switch(data[2]) { + case FB64_IV: + snprintf(lbuf, sizeof(lbuf), "%s_IV", type); + cp = lbuf; + goto common; + + case FB64_IV_OK: + snprintf(lbuf, sizeof(lbuf), "%s_IV_OK", type); + cp = lbuf; + goto common; + + case FB64_IV_BAD: + snprintf(lbuf, sizeof(lbuf), "%s_IV_BAD", type); + cp = lbuf; + goto common; + + default: + snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[2]); + cp = lbuf; + common: + for (; (buflen > 0) && (*buf = *cp++); buf++) + buflen--; + for (i = 3; i < cnt; i++) { + snprintf(lbuf, sizeof(lbuf), " %d", data[i]); + for (cp = lbuf; (buflen > 0) && (*buf = *cp++); buf++) + buflen--; + } + break; + } +} + +void +cfb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) +{ + fb64_printsub(data, cnt, buf, buflen, "CFB64"); +} + + void +ofb64_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) +{ + fb64_printsub(data, cnt, buf, buflen, "OFB64"); +} + +void +fb64_stream_iv(Block seed, struct stinfo *stp) +{ + + memmove((void *)stp->str_iv, (void *)seed, sizeof(Block)); + memmove((void *)stp->str_output, (void *)seed, sizeof(Block)); + + des_key_sched(&stp->str_ikey, stp->str_sched); + + stp->str_index = sizeof(Block); +} + +void +fb64_stream_key(Block *key, struct stinfo *stp) +{ + memmove((void *)stp->str_ikey, (void *)key, sizeof(Block)); + des_key_sched(key, stp->str_sched); + + memmove((void *)stp->str_output, (void *)stp->str_iv, sizeof(Block)); + + stp->str_index = sizeof(Block); +} + +/* + * DES 64 bit Cipher Feedback + * + * key --->+-----+ + * +->| DES |--+ + * | +-----+ | + * | v + * INPUT --(--------->(+)+---> DATA + * | | + * +-------------+ + * + * + * Given: + * iV: Initial vector, 64 bits (8 bytes) long. + * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). + * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. + * + * V0 = DES(iV, key) + * On = Dn ^ Vn + * V(n+1) = DES(On, key) + */ + +void +cfb64_encrypt(unsigned char *s, int c) +{ + register struct stinfo *stp = &fb[CFB].streams[DIR_ENCRYPT-1]; + register int idx; + + idx = stp->str_index; + while (c-- > 0) { + if (idx == sizeof(Block)) { + Block b; + des_ecb_encrypt(&stp->str_output, &b, stp->str_sched, 1); + memmove((void *)stp->str_feed, (void *)b, sizeof(Block)); + idx = 0; + } + + /* On encryption, we store (feed ^ data) which is cypher */ + *s = stp->str_output[idx] = (stp->str_feed[idx] ^ *s); + s++; + idx++; + } + stp->str_index = idx; +} + +int +cfb64_decrypt(int data) +{ + register struct stinfo *stp = &fb[CFB].streams[DIR_DECRYPT-1]; + int idx; + + if (data == -1) { + /* + * Back up one byte. It is assumed that we will + * never back up more than one byte. If we do, this + * may or may not work. + */ + if (stp->str_index) + --stp->str_index; + return(0); + } + + idx = stp->str_index++; + if (idx == sizeof(Block)) { + Block b; + des_ecb_encrypt(&stp->str_output, &b, stp->str_sched, 1); + memmove((void *)stp->str_feed, (void *)b, sizeof(Block)); + stp->str_index = 1; /* Next time will be 1 */ + idx = 0; /* But now use 0 */ + } + + /* On decryption we store (data) which is cypher. */ + stp->str_output[idx] = data; + return(data ^ stp->str_feed[idx]); +} + +/* + * DES 64 bit Output Feedback + * + * key --->+-----+ + * +->| DES |--+ + * | +-----+ | + * +-----------+ + * v + * INPUT -------->(+) ----> DATA + * + * Given: + * iV: Initial vector, 64 bits (8 bytes) long. + * Dn: the nth chunk of 64 bits (8 bytes) of data to encrypt (decrypt). + * On: the nth chunk of 64 bits (8 bytes) of encrypted (decrypted) output. + * + * V0 = DES(iV, key) + * V(n+1) = DES(Vn, key) + * On = Dn ^ Vn + */ +void +ofb64_encrypt(unsigned char *s, int c) +{ + register struct stinfo *stp = &fb[OFB].streams[DIR_ENCRYPT-1]; + register int idx; + + idx = stp->str_index; + while (c-- > 0) { + if (idx == sizeof(Block)) { + Block b; + des_ecb_encrypt(&stp->str_feed, &b, stp->str_sched, 1); + memmove((void *)stp->str_feed, (void *)b, sizeof(Block)); + idx = 0; + } + *s++ ^= stp->str_feed[idx]; + idx++; + } + stp->str_index = idx; +} + +int +ofb64_decrypt(int data) +{ + register struct stinfo *stp = &fb[OFB].streams[DIR_DECRYPT-1]; + int idx; + + if (data == -1) { + /* + * Back up one byte. It is assumed that we will + * never back up more than one byte. If we do, this + * may or may not work. + */ + if (stp->str_index) + --stp->str_index; + return(0); + } + + idx = stp->str_index++; + if (idx == sizeof(Block)) { + Block b; + des_ecb_encrypt(&stp->str_feed, &b, stp->str_sched, 1); + memmove((void *)stp->str_feed, (void *)b, sizeof(Block)); + stp->str_index = 1; /* Next time will be 1 */ + idx = 0; /* But now use 0 */ + } + + return(data ^ stp->str_feed[idx]); +} +# endif /* DES_ENCRYPTION */ +# endif /* AUTHENTICATION */ +#endif /* ENCRYPTION */ diff --git a/lib/libtelnet/encrypt.c b/lib/libtelnet/encrypt.c new file mode 100644 index 000000000..5aed35f2f --- /dev/null +++ b/lib/libtelnet/encrypt.c @@ -0,0 +1,960 @@ +/* $NetBSD: encrypt.c,v 1.17 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if 0 +static char sccsid[] = "@(#)encrypt.c 8.2 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: encrypt.c,v 1.17 2012/03/21 05:33:27 matt Exp $"); +#endif /* not lint */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifdef ENCRYPTION + +#include +#define ENCRYPT_NAMES +#include + +#include "encrypt.h" +#include "misc.h" + +#include +#ifdef NO_STRING_H +#include +#else +#include +#endif + +/* + * These functions pointers point to the current routines + * for encrypting and decrypting data. + */ +void (*encrypt_output)(unsigned char *, int); +int (*decrypt_input)(int); + +int encrypt_debug_mode = 0; +static int decrypt_mode = 0; +static int encrypt_mode = 0; +static int encrypt_verbose = 0; +static int autoencrypt = 0; +static int autodecrypt = 0; +static int havesessionkey = 0; +static int Server = 0; +static const char *Name = "Noname"; + +#define typemask(x) ((x) > 0 ? 1 << ((x)-1) : 0) + +static long i_support_encrypt = typemask(ENCTYPE_DES_CFB64) + | typemask(ENCTYPE_DES_OFB64); +static long i_support_decrypt = typemask(ENCTYPE_DES_CFB64) + | typemask(ENCTYPE_DES_OFB64); +static long i_wont_support_encrypt = 0; +static long i_wont_support_decrypt = 0; +#define I_SUPPORT_ENCRYPT (i_support_encrypt & ~i_wont_support_encrypt) +#define I_SUPPORT_DECRYPT (i_support_decrypt & ~i_wont_support_decrypt) + +static long remote_supports_encrypt = 0; +static long remote_supports_decrypt = 0; + +static Encryptions encryptions[] = { +#ifdef DES_ENCRYPTION + { "DES_CFB64", ENCTYPE_DES_CFB64, + cfb64_encrypt, + cfb64_decrypt, + cfb64_init, + cfb64_start, + cfb64_is, + cfb64_reply, + cfb64_session, + cfb64_keyid, + cfb64_printsub }, + { "DES_OFB64", ENCTYPE_DES_OFB64, + ofb64_encrypt, + ofb64_decrypt, + ofb64_init, + ofb64_start, + ofb64_is, + ofb64_reply, + ofb64_session, + ofb64_keyid, + ofb64_printsub }, +#endif /* DES_ENCRYPTION */ + { .name = 0 }, +}; + +static unsigned char str_send[64] = { IAC, SB, TELOPT_ENCRYPT, + ENCRYPT_SUPPORT }; +static unsigned char str_suplen = 0; +static unsigned char str_start[72] = { IAC, SB, TELOPT_ENCRYPT }; +static unsigned char str_end[] = { IAC, SB, TELOPT_ENCRYPT, 0, IAC, SE }; + +Encryptions * +findencryption(int type) +{ + Encryptions *ep = encryptions; + + if (!(I_SUPPORT_ENCRYPT & remote_supports_decrypt & typemask(type))) + return(0); + while (ep->type && ep->type != type) + ++ep; + return(ep->type ? ep : 0); +} + +Encryptions * +finddecryption(int type) +{ + Encryptions *ep = encryptions; + + if (!(I_SUPPORT_DECRYPT & remote_supports_encrypt & typemask(type))) + return(0); + while (ep->type && ep->type != type) + ++ep; + return(ep->type ? ep : 0); +} + +#define MAXKEYLEN 64 + +static struct key_info { + unsigned char keyid[MAXKEYLEN]; + int keylen; + int dir; + int *modep; + Encryptions *(*getcrypt)(int); +} ki[2] = { + { { 0 }, 0, DIR_ENCRYPT, &encrypt_mode, findencryption }, + { { 0 }, 0, DIR_DECRYPT, &decrypt_mode, finddecryption }, +}; + +void +encrypt_init(const char *name, int server) +{ + Encryptions *ep = encryptions; + + Name = name; + Server = server; + i_support_encrypt = i_support_decrypt = 0; + remote_supports_encrypt = remote_supports_decrypt = 0; + encrypt_mode = 0; + decrypt_mode = 0; + encrypt_output = 0; + decrypt_input = 0; +#ifdef notdef + encrypt_verbose = !server; +#endif + + str_suplen = 4; + + while (ep->type) { + if (encrypt_debug_mode) + printf(">>>%s: I will support %s\r\n", + Name, ENCTYPE_NAME(ep->type)); + i_support_encrypt |= typemask(ep->type); + i_support_decrypt |= typemask(ep->type); + if ((i_wont_support_decrypt & typemask(ep->type)) == 0) + if ((str_send[str_suplen++] = ep->type) == IAC) + str_send[str_suplen++] = IAC; + if (ep->init) + (*ep->init)(Server); + ++ep; + } + str_send[str_suplen++] = IAC; + str_send[str_suplen++] = SE; +} + +void +encrypt_list_types(void) +{ + Encryptions *ep = encryptions; + + printf("Valid encryption types:\n"); + while (ep->type) { + printf("\t%s (%d)\r\n", ENCTYPE_NAME(ep->type), ep->type); + ++ep; + } +} + +int +EncryptEnable(char *type, char *mode) +{ + if (isprefix(type, "help") || isprefix(type, "?")) { + printf("Usage: encrypt enable [input|output]\n"); + encrypt_list_types(); + return(0); + } + if (EncryptType(type, mode)) + return(EncryptStart(mode)); + return(0); +} + +int +EncryptDisable(char *type, char *mode) +{ + register Encryptions *ep; + int ret = 0; + + if (isprefix(type, "help") || isprefix(type, "?")) { + printf("Usage: encrypt disable [input|output]\n"); + encrypt_list_types(); + } else if ((ep = (Encryptions *)genget(type, (char **)encryptions, + sizeof(Encryptions))) == 0) { + printf("%s: invalid encryption type\n", type); + } else if (Ambiguous(ep)) { + printf("Ambiguous type '%s'\n", type); + } else { + if ((mode == 0) || (isprefix(mode, "input") ? 1 : 0)) { + if (decrypt_mode == ep->type) + EncryptStopInput(); + i_wont_support_decrypt |= typemask(ep->type); + ret = 1; + } + if ((mode == 0) || (isprefix(mode, "output"))) { + if (encrypt_mode == ep->type) + EncryptStopOutput(); + i_wont_support_encrypt |= typemask(ep->type); + ret = 1; + } + if (ret == 0) + printf("%s: invalid encryption mode\n", mode); + } + return(ret); +} + +int +EncryptType(char *type, char *mode) +{ + register Encryptions *ep; + int ret = 0; + + if (isprefix(type, "help") || isprefix(type, "?")) { + printf("Usage: encrypt type [input|output]\n"); + encrypt_list_types(); + } else if ((ep = (Encryptions *)genget(type, (char **)encryptions, + sizeof(Encryptions))) == 0) { + printf("%s: invalid encryption type\n", type); + } else if (Ambiguous(ep)) { + printf("Ambiguous type '%s'\n", type); + } else { + if ((mode == 0) || isprefix(mode, "input")) { + decrypt_mode = ep->type; + i_wont_support_decrypt &= ~typemask(ep->type); + ret = 1; + } + if ((mode == 0) || isprefix(mode, "output")) { + encrypt_mode = ep->type; + i_wont_support_encrypt &= ~typemask(ep->type); + ret = 1; + } + if (ret == 0) + printf("%s: invalid encryption mode\n", mode); + } + return(ret); +} + +int +EncryptStart(char *mode) +{ + register int ret = 0; + if (mode) { + if (isprefix(mode, "input")) + return(EncryptStartInput()); + if (isprefix(mode, "output")) + return(EncryptStartOutput()); + if (isprefix(mode, "help") || isprefix(mode, "?")) { + printf("Usage: encrypt start [input|output]\n"); + return(0); + } + printf("%s: invalid encryption mode 'encrypt start ?' for help\n", mode); + return(0); + } + ret += EncryptStartInput(); + ret += EncryptStartOutput(); + return(ret); +} + +int +EncryptStartInput(void) +{ + if (decrypt_mode) { + encrypt_send_request_start(); + return(1); + } + printf("No previous decryption mode, decryption not enabled\r\n"); + return(0); +} + +int +EncryptStartOutput(void) +{ + if (encrypt_mode) { + encrypt_start_output(encrypt_mode); + return(1); + } + printf("No previous encryption mode, encryption not enabled\r\n"); + return(0); +} + +int +EncryptStop(char *mode) +{ + int ret = 0; + if (mode) { + if (isprefix(mode, "input")) + return(EncryptStopInput()); + if (isprefix(mode, "output")) + return(EncryptStopOutput()); + if (isprefix(mode, "help") || isprefix(mode, "?")) { + printf("Usage: encrypt stop [input|output]\n"); + return(0); + } + printf("%s: invalid encryption mode 'encrypt stop ?' for help\n", mode); + return(0); + } + ret += EncryptStopInput(); + ret += EncryptStopOutput(); + return(ret); +} + +int +EncryptStopInput(void) +{ + encrypt_send_request_end(); + return(1); +} + +int +EncryptStopOutput(void) +{ + encrypt_send_end(); + return(1); +} + +void +encrypt_display(void) +{ + if (encrypt_output) + printf("Currently encrypting output with %s\r\n", + ENCTYPE_NAME(encrypt_mode)); + if (decrypt_input) + printf("Currently decrypting input with %s\r\n", + ENCTYPE_NAME(decrypt_mode)); +} + +int +EncryptStatus(void) +{ + if (encrypt_output) + printf("Currently encrypting output with %s\r\n", + ENCTYPE_NAME(encrypt_mode)); + else if (encrypt_mode) { + printf("Currently output is clear text.\r\n"); + printf("Last encryption mode was %s\r\n", + ENCTYPE_NAME(encrypt_mode)); + } + if (decrypt_input) { + printf("Currently decrypting input with %s\r\n", + ENCTYPE_NAME(decrypt_mode)); + } else if (decrypt_mode) { + printf("Currently input is clear text.\r\n"); + printf("Last decryption mode was %s\r\n", + ENCTYPE_NAME(decrypt_mode)); + } + return 1; +} + +void +encrypt_send_support(void) +{ + if (str_suplen) { + /* + * If the user has requested that decryption start + * immediatly, then send a "REQUEST START" before + * we negotiate the type. + */ + if (!Server && autodecrypt) + encrypt_send_request_start(); + telnet_net_write(str_send, str_suplen); + printsub('>', &str_send[2], str_suplen - 2); + str_suplen = 0; + } +} + +int +EncryptDebug(int on) +{ + if (on < 0) + encrypt_debug_mode ^= 1; + else + encrypt_debug_mode = on; + printf("Encryption debugging %s\r\n", + encrypt_debug_mode ? "enabled" : "disabled"); + return(1); +} + +int +EncryptVerbose(int on) +{ + if (on < 0) + encrypt_verbose ^= 1; + else + encrypt_verbose = on; + printf("Encryption %s verbose\r\n", + encrypt_verbose ? "is" : "is not"); + return(1); +} + +int +EncryptAutoEnc(int on) +{ + encrypt_auto(on); + printf("Automatic encryption of output is %s\r\n", + autoencrypt ? "enabled" : "disabled"); + return(1); +} + +int +EncryptAutoDec(int on) +{ + decrypt_auto(on); + printf("Automatic decryption of input is %s\r\n", + autodecrypt ? "enabled" : "disabled"); + return(1); +} + +/* + * Called when ENCRYPT SUPPORT is received. + */ +void +encrypt_support(unsigned char *typelist, int cnt) +{ + register int type, use_type = 0; + Encryptions *ep; + + /* + * Forget anything the other side has previously told us. + */ + remote_supports_decrypt = 0; + + while (cnt-- > 0) { + type = *typelist++; + if (encrypt_debug_mode) + printf(">>>%s: He is supporting %s (%d)\r\n", + Name, + ENCTYPE_NAME(type), type); + if ((type < ENCTYPE_CNT) && + (I_SUPPORT_ENCRYPT & typemask(type))) { + remote_supports_decrypt |= typemask(type); + if (use_type == 0) + use_type = type; + } + } + if (use_type) { + ep = findencryption(use_type); + if (!ep) + return; + type = ep->start ? (*ep->start)(DIR_ENCRYPT, Server) : 0; + if (encrypt_debug_mode) + printf(">>>%s: (*ep->start)() returned %d\r\n", + Name, type); + if (type < 0) + return; + encrypt_mode = use_type; + if (type == 0) + encrypt_start_output(use_type); + } +} + +void +encrypt_is(unsigned char *data, int cnt) +{ + Encryptions *ep; + register int type, ret; + + if (--cnt < 0) + return; + type = *data++; + if (type < ENCTYPE_CNT) + remote_supports_encrypt |= typemask(type); + if (!(ep = finddecryption(type))) { + if (encrypt_debug_mode) + printf(">>>%s: Can't find type %s (%d) for initial negotiation\r\n", + Name, + ENCTYPE_NAME_OK(type) + ? ENCTYPE_NAME(type) : "(unknown)", + type); + return; + } + if (!ep->is) { + if (encrypt_debug_mode) + printf(">>>%s: No initial negotiation needed for type %s (%d)\r\n", + Name, + ENCTYPE_NAME_OK(type) + ? ENCTYPE_NAME(type) : "(unknown)", + type); + ret = 0; + } else { + ret = (*ep->is)(data, cnt); + if (encrypt_debug_mode) + printf("(*ep->is)(%p, %d) returned %s(%d)\n", data, cnt, + (ret < 0) ? "FAIL " : + (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); + } + if (ret < 0) { + autodecrypt = 0; + } else { + decrypt_mode = type; + if (ret == 0 && autodecrypt) + encrypt_send_request_start(); + } +} + +void +encrypt_reply(unsigned char *data, int cnt) +{ + Encryptions *ep; + register int ret, type; + + if (--cnt < 0) + return; + type = *data++; + if (!(ep = findencryption(type))) { + if (encrypt_debug_mode) + printf(">>>%s: Can't find type %s (%d) for initial negotiation\r\n", + Name, + ENCTYPE_NAME_OK(type) + ? ENCTYPE_NAME(type) : "(unknown)", + type); + return; + } + if (!ep->reply) { + if (encrypt_debug_mode) + printf(">>>%s: No initial negotiation needed for type %s (%d)\r\n", + Name, + ENCTYPE_NAME_OK(type) + ? ENCTYPE_NAME(type) : "(unknown)", + type); + ret = 0; + } else { + ret = (*ep->reply)(data, cnt); + if (encrypt_debug_mode) + printf("(*ep->reply)(%p, %d) returned %s(%d)\n", + data, cnt, + (ret < 0) ? "FAIL " : + (ret == 0) ? "SUCCESS " : "MORE_TO_DO ", ret); + } + if (encrypt_debug_mode) + printf(">>>%s: encrypt_reply returned %d\n", Name, ret); + if (ret < 0) { + autoencrypt = 0; + } else { + encrypt_mode = type; + if (ret == 0 && autoencrypt) + encrypt_start_output(type); + } +} + +/* + * Called when a ENCRYPT START command is received. + */ +void +encrypt_start(unsigned char *data, int cnt) +{ + Encryptions *ep; + + if (!decrypt_mode) { + /* + * Something is wrong. We should not get a START + * command without having already picked our + * decryption scheme. Send a REQUEST-END to + * attempt to clear the channel... + */ + printf("%s: Warning, Cannot decrypt input stream!!!\r\n", Name); + encrypt_send_request_end(); + return; + } + + if ((ep = finddecryption(decrypt_mode)) != NULL) { + decrypt_input = ep->input; + if (encrypt_verbose) + printf("[ Input is now decrypted with type %s ]\r\n", + ENCTYPE_NAME(decrypt_mode)); + if (encrypt_debug_mode) + printf(">>>%s: Start to decrypt input with type %s\r\n", + Name, ENCTYPE_NAME(decrypt_mode)); + } else { + printf("%s: Warning, Cannot decrypt type %s (%d)!!!\r\n", + Name, + ENCTYPE_NAME_OK(decrypt_mode) + ? ENCTYPE_NAME(decrypt_mode) + : "(unknown)", + decrypt_mode); + encrypt_send_request_end(); + } +} + +void +encrypt_session_key(Session_Key *key, int server) +{ + Encryptions *ep = encryptions; + + havesessionkey = 1; + + while (ep->type) { + if (ep->session) + (*ep->session)(key, server); +#ifdef notdef + if (!encrypt_output && autoencrypt && !server) + encrypt_start_output(ep->type); + if (!decrypt_input && autodecrypt && !server) + encrypt_send_request_start(); +#endif + ++ep; + } +} + +/* + * Called when ENCRYPT END is received. + */ +void +encrypt_end(void) +{ + decrypt_input = 0; + if (encrypt_debug_mode) + printf(">>>%s: Input is back to clear text\r\n", Name); + if (encrypt_verbose) + printf("[ Input is now clear text ]\r\n"); +} + +/* + * Called when ENCRYPT REQUEST-END is received. + */ +void +encrypt_request_end(void) +{ + encrypt_send_end(); +} + +/* + * Called when ENCRYPT REQUEST-START is received. If we receive + * this before a type is picked, then that indicates that the + * other side wants us to start encrypting data as soon as we + * can. + */ +void +encrypt_request_start(unsigned char *data, int cnt) +{ + if (encrypt_mode == 0) { + if (Server) + autoencrypt = 1; + return; + } + encrypt_start_output(encrypt_mode); +} + +static unsigned char str_keyid[(MAXKEYLEN*2)+5] = { IAC, SB, TELOPT_ENCRYPT }; + +void +encrypt_enc_keyid(unsigned char *keyid, int len) +{ + encrypt_keyid(&ki[1], keyid, len); +} + +void +encrypt_dec_keyid(unsigned char *keyid, int len) +{ + encrypt_keyid(&ki[0], keyid, len); +} + +void +encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len) +{ + Encryptions *ep; + int dir = kp->dir; + register int ret = 0; + + if (!(ep = (*kp->getcrypt)(*kp->modep))) { + if (len == 0) + return; + kp->keylen = 0; + } else if (len == 0) { + /* + * Empty option, indicates a failure. + */ + if (kp->keylen == 0) + return; + kp->keylen = 0; + if (ep->keyid) + (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen); + + } else if ((size_t)len > sizeof(kp->keyid)) { + return; + } else if ((len != kp->keylen) || + (memcmp(keyid, kp->keyid, len) != 0)) { + /* + * Length or contents are different + */ + kp->keylen = len; + memmove(kp->keyid, keyid, len); + if (ep->keyid) + (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen); + } else { + if (ep->keyid) + ret = (*ep->keyid)(dir, kp->keyid, &kp->keylen); + if ((ret == 0) && (dir == DIR_ENCRYPT) && autoencrypt) + encrypt_start_output(*kp->modep); + return; + } + + encrypt_send_keyid(dir, kp->keyid, kp->keylen, 0); +} + +void +encrypt_send_keyid(int dir, const unsigned char *keyid, int keylen, int saveit) +{ + unsigned char *strp; + + str_keyid[3] = (dir == DIR_ENCRYPT) + ? ENCRYPT_ENC_KEYID : ENCRYPT_DEC_KEYID; + if (saveit) { + struct key_info *kp = &ki[(dir == DIR_ENCRYPT) ? 0 : 1]; + memmove(kp->keyid, keyid, keylen); + kp->keylen = keylen; + } + + for (strp = &str_keyid[4]; keylen > 0; --keylen) { + if ((*strp++ = *keyid++) == IAC) + *strp++ = IAC; + } + *strp++ = IAC; + *strp++ = SE; + telnet_net_write(str_keyid, strp - str_keyid); + printsub('>', &str_keyid[2], strp - str_keyid - 2); +} + +void +encrypt_auto(int on) +{ + if (on < 0) + autoencrypt ^= 1; + else + autoencrypt = on ? 1 : 0; +} + +void +decrypt_auto(int on) +{ + if (on < 0) + autodecrypt ^= 1; + else + autodecrypt = on ? 1 : 0; +} + +void +encrypt_start_output(int type) +{ + Encryptions *ep; + register unsigned char *p; + register int i; + + if (!(ep = findencryption(type))) { + if (encrypt_debug_mode) { + printf(">>>%s: Can't encrypt with type %s (%d)\r\n", + Name, + ENCTYPE_NAME_OK(type) + ? ENCTYPE_NAME(type) : "(unknown)", + type); + } + return; + } + if (ep->start) { + i = (*ep->start)(DIR_ENCRYPT, Server); + if (encrypt_debug_mode) { + printf(">>>%s: Encrypt start: %s (%d) %s\r\n", + Name, + (i < 0) ? "failed" : + "initial negotiation in progress", + i, ENCTYPE_NAME(type)); + } + if (i) + return; + } + p = str_start + 3; + *p++ = ENCRYPT_START; + for (i = 0; i < ki[0].keylen; ++i) { + if ((*p++ = ki[0].keyid[i]) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + telnet_net_write(str_start, p - str_start); + net_encrypt(); + printsub('>', &str_start[2], p - &str_start[2]); + /* + * If we are already encrypting in some mode, then + * encrypt the ring (which includes our request) in + * the old mode, mark it all as "clear text" and then + * switch to the new mode. + */ + encrypt_output = ep->output; + encrypt_mode = type; + if (encrypt_debug_mode) + printf(">>>%s: Started to encrypt output with type %s\r\n", + Name, ENCTYPE_NAME(type)); + if (encrypt_verbose) + printf("[ Output is now encrypted with type %s ]\r\n", + ENCTYPE_NAME(type)); +} + +void +encrypt_send_end(void) +{ + if (!encrypt_output) + return; + + str_end[3] = ENCRYPT_END; + telnet_net_write(str_end, sizeof(str_end)); + net_encrypt(); + printsub('>', &str_end[2], sizeof(str_end) - 2); + /* + * Encrypt the output buffer now because it will not be done by + * netflush... + */ + encrypt_output = 0; + if (encrypt_debug_mode) + printf(">>>%s: Output is back to clear text\r\n", Name); + if (encrypt_verbose) + printf("[ Output is now clear text ]\r\n"); +} + +void +encrypt_send_request_start(void) +{ + register unsigned char *p; + register int i; + + p = &str_start[3]; + *p++ = ENCRYPT_REQSTART; + for (i = 0; i < ki[1].keylen; ++i) { + if ((*p++ = ki[1].keyid[i]) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + telnet_net_write(str_start, p - str_start); + printsub('>', &str_start[2], p - &str_start[2]); + if (encrypt_debug_mode) + printf(">>>%s: Request input to be encrypted\r\n", Name); +} + +void +encrypt_send_request_end(void) +{ + str_end[3] = ENCRYPT_REQEND; + telnet_net_write(str_end, sizeof(str_end)); + printsub('>', &str_end[2], sizeof(str_end) - 2); + + if (encrypt_debug_mode) + printf(">>>%s: Request input to be clear text\r\n", Name); +} + +void +encrypt_wait(void) +{ + if (encrypt_debug_mode) + printf(">>>%s: in encrypt_wait\r\n", Name); + if (!havesessionkey || !(I_SUPPORT_ENCRYPT & remote_supports_decrypt)) + return; + while (autoencrypt && !encrypt_output) + if (telnet_spin()) + return; +} + +void +encrypt_debug(int mode) +{ + encrypt_debug_mode = mode; +} + +void +encrypt_gen_printsub(unsigned char *data, int cnt, + unsigned char *buf, int buflen) +{ + char tbuf[16], *cp; + + cnt -= 2; + data += 2; + buf[buflen-1] = '\0'; + buf[buflen-2] = '*'; + buflen -= 2; + for (; cnt > 0; cnt--, data++) { + snprintf(tbuf, sizeof(tbuf), " %d", *data); + for (cp = tbuf; *cp && buflen > 0; --buflen) + *buf++ = *cp++; + if (buflen <= 0) + return; + } + *buf = '\0'; +} + +void +encrypt_printsub(unsigned char *data, int cnt, + unsigned char *buf, int buflen) +{ + Encryptions *ep; + register int type = data[1]; + + for (ep = encryptions; ep->type && ep->type != type; ep++) + ; + + if (ep->printsub) + (*ep->printsub)(data, cnt, buf, buflen); + else + encrypt_gen_printsub(data, cnt, buf, buflen); +} +#endif /* ENCRYPTION */ diff --git a/lib/libtelnet/encrypt.h b/lib/libtelnet/encrypt.h new file mode 100644 index 000000000..570b83a92 --- /dev/null +++ b/lib/libtelnet/encrypt.h @@ -0,0 +1,101 @@ +/* $NetBSD: encrypt.h,v 1.9 2012/01/09 15:25:33 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)encrypt.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifdef ENCRYPTION +#include + +# ifndef __ENCRYPTION__ +# define __ENCRYPTION__ + +#define DIR_DECRYPT 1 +#define DIR_ENCRYPT 2 + +#define Block des_cblock +typedef unsigned char *BlockT; +#define Schedule des_key_schedule + +#define VALIDKEY(key) ( key[0] | key[1] | key[2] | key[3] | \ + key[4] | key[5] | key[6] | key[7]) + +#define SAMEKEY(k1, k2) (!bcmp((void *)k1, (void *)k2, sizeof(Block))) + +typedef struct { + short type; + int length; + unsigned char *data; +} Session_Key; + + +typedef struct { + const char *name; + int type; + void (*output)(unsigned char *, int); + int (*input)(int); + void (*init)(int); + int (*start)(int, int); + int (*is)(unsigned char *, int); + int (*reply)(unsigned char *, int); + void (*session)(Session_Key *, int); + int (*keyid)(int, unsigned char *, int *); + void (*printsub)(unsigned char *, int, unsigned char *, int); +} Encryptions; + +#define SK_DES 1 /* Matched Kerberos v5 KEYTYPE_DES */ + +#include "enc-proto.h" + +extern int encrypt_debug_mode; +extern int (*decrypt_input)(int); +extern void (*encrypt_output)(unsigned char *, int); +# endif /* __ENCRYPTION__ */ +#endif /* ENCRYPTION */ diff --git a/lib/libtelnet/forward.c b/lib/libtelnet/forward.c new file mode 100644 index 000000000..b97418dbc --- /dev/null +++ b/lib/libtelnet/forward.c @@ -0,0 +1,69 @@ +/* + * appl/telnet/libtelnet/forward.c + */ + +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +/* General-purpose forwarding routines. These routines may be put into */ +/* libkrb5.a to allow widespread use */ + +#if defined(KERBEROS) || defined(KRB5) +#include +#include + +#include "k5-int.h" + +extern char *line; /* see sys_term.c */ + +krb5_error_code rd_and_store_for_creds(krb5_context, krb5_auth_context, krb5_data *, krb5_ticket *); + +/* Decode, decrypt and store the forwarded creds in the local ccache. */ +krb5_error_code +rd_and_store_for_creds(context, auth_context, inbuf, ticket) + krb5_context context; + krb5_auth_context auth_context; + krb5_data *inbuf; + krb5_ticket *ticket; +{ + krb5_creds **creds; + krb5_error_code retval; + char ccname[35]; + krb5_ccache ccache = NULL; + + if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL)) != 0) + return(retval); + + snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_p%d", getpid()); + setenv(KRB5_ENV_CCNAME, ccname, 1); + + if ((retval = krb5_cc_resolve(context, ccname, &ccache)) != 0) + goto cleanup; + + if ((retval = krb5_cc_initialize(context, ccache, ticket->enc_part2->client)) != 0) + goto cleanup; + + if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0) + goto cleanup; + +cleanup: + krb5_free_creds(context, *creds); + return retval; +} + +#endif /* defined(KRB5) && defined(FORWARD) */ diff --git a/lib/libtelnet/genget.c b/lib/libtelnet/genget.c new file mode 100644 index 000000000..49010aa9e --- /dev/null +++ b/lib/libtelnet/genget.c @@ -0,0 +1,105 @@ +/* $NetBSD: genget.c,v 1.13 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)genget.c 8.2 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: genget.c,v 1.13 2012/03/21 05:33:27 matt Exp $"); +#endif +#endif /* not lint */ + + +#include +#include "misc.h" + +#define LOWER(x) (isupper((unsigned char)x) ? tolower((unsigned char)x) : (x)) +/* + * The prefix function returns 0 if *s1 is not a prefix + * of *s2. If *s1 exactly matches *s2, the negative of + * the length is returned. If *s1 is a prefix of *s2, + * the length of *s1 is returned. + */ +int +isprefix(char *s1, const char *s2) +{ + char *os1; + char c1, c2; + + if (*s1 == '\0') + return(-1); + os1 = s1; + c1 = *s1; + c2 = *s2; + while (LOWER(c1) == LOWER(c2)) { + if (c1 == '\0') + break; + c1 = *++s1; + c2 = *++s2; + } + return(*s1 ? 0 : (*s2 ? (s1 - os1) : (os1 - s1))); +} + +static char *ambiguous; /* special return value for command routines */ + +char ** +genget( char *name, /* name to match */ + char **table, /* name entry in table */ + int stlen) +{ + register char **c, **found; + register int n; + + if (name == 0) + return 0; + + found = 0; + for (c = table; *c != 0; c = (char **)((char *)c + stlen)) { + if ((n = isprefix(name, *c)) == 0) + continue; + if (n < 0) /* exact match */ + return(c); + if (found) + return(&ambiguous); + found = c; + } + return(found); +} + +/* + * Function call version of Ambiguous() + */ +int +Ambiguous(void *s) +{ + return(s == &ambiguous); +} diff --git a/lib/libtelnet/getent.c b/lib/libtelnet/getent.c new file mode 100644 index 000000000..566c792f4 --- /dev/null +++ b/lib/libtelnet/getent.c @@ -0,0 +1,76 @@ +/* $NetBSD: getent.c,v 1.11 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)getent.c 8.2 (Berkeley) 12/15/93"; +#else +__RCSID("$NetBSD: getent.c,v 1.11 2012/03/21 05:33:27 matt Exp $"); +#endif +#endif /* not lint */ + +#include +#include "misc-proto.h" + +static char *area; + +int getent(char *, char *); +char *getstr(char *, char **); + +/*ARGSUSED*/ +int +getent(char *cp, char *name) +{ +#ifdef HAS_CGETENT + const char *dba[2]; + + dba[0] = "/etc/gettytab"; + dba[1] = 0; + return((cgetent(&area, dba, name) == 0) ? 1 : 0); +#else + return(0); +#endif +} + +#ifndef SOLARIS +/*ARGSUSED*/ +char * +getstr(char *id, char **cpp) +{ +# ifdef HAS_CGETENT + char *answer; + return((cgetstr(area, id, &answer) > 0) ? answer : 0); +# else + return(0); +# endif +} +#endif diff --git a/lib/libtelnet/kerberos5.c b/lib/libtelnet/kerberos5.c new file mode 100644 index 000000000..39ab14472 --- /dev/null +++ b/lib/libtelnet/kerberos5.c @@ -0,0 +1,731 @@ +/* $NetBSD: kerberos5.c,v 1.20 2014/04/26 22:10:40 joerg Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifdef KRB5 +#include +#include +#include +#include +#include +#include +#include +#include +#define Authenticator k5_Authenticator +#include +#undef Authenticator +/* #include */ + +#include "encrypt.h" +#include "auth.h" +#include "misc.h" + +extern int net; + +int forward_flags; /* Flags get set in telnet/main.c on -f and -F */ +int got_forwarded_creds;/* Tell telnetd to pass -F or -f to login. */ + +int require_hwpreauth; + +const char *get_krb5_err_text(krb5_context, krb5_error_code); +void kerberos5_forward(Authenticator *); + +static unsigned char str_data[1024] = {IAC, SB, TELOPT_AUTHENTICATION, 0, + AUTHTYPE_KERBEROS_V5,}; + +#define KRB_AUTH 0 /* Authentication data follows */ +#define KRB_REJECT 1 /* Rejected (reason might follow) */ +#define KRB_ACCEPT 2 /* Accepted */ +#define KRB_RESPONSE 3 /* Response for mutual auth. */ + +#define KRB_FORWARD 4 /* Forwarded credentials follow */ +#define KRB_FORWARD_ACCEPT 5 /* Forwarded credentials accepted */ +#define KRB_FORWARD_REJECT 6 /* Forwarded credentials rejected */ + +static krb5_data auth; +static krb5_ticket *ticket; + +krb5_context telnet_context; +static krb5_auth_context auth_context; + +static int +Data(Authenticator *ap, int type, const void *d, int c) +{ + unsigned char *p = str_data + 4; + const unsigned char *cd = (const unsigned char *) d; + + if (c == -1) + c = strlen(cd); + + if (auth_debug_mode) { + printf("%s:%d: [%d] (%d)", + str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", + str_data[3], + type, c); + printd(d, c); + printf("\r\n"); + } + *p++ = ap->type; + *p++ = ap->way; + *p++ = type; + while (c-- > 0) { + if ((*p++ = *cd++) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + if (str_data[3] == TELQUAL_IS) + printsub('>', &str_data[2], p - &str_data[2]); + return (telnet_net_write(str_data, p - str_data)); +} + +const char * +get_krb5_err_text(krb5_context ctx, krb5_error_code ret) +{ + static const char *str = NULL; + + if (str) + krb5_free_error_message(ctx, str); + + str = krb5_get_error_message(ctx, ret); + + if (str != NULL) + return str; + + return "unknown"; +} + +int +kerberos5_init(Authenticator *ap, int server) +{ + krb5_error_code ret; + + if (telnet_context == 0) { + ret = krb5_init_context(&telnet_context); + if (ret) + return 0; + } + + if (server) { + krb5_keytab kt; + krb5_kt_cursor cursor; + + ret = krb5_kt_default(telnet_context, &kt); + if (ret) + return 0; + + ret = krb5_kt_start_seq_get(telnet_context, kt, &cursor); + if (ret) { + krb5_kt_close(telnet_context, kt); + return 0; + } + krb5_kt_end_seq_get(telnet_context, kt, &cursor); + krb5_kt_close(telnet_context, kt); + + str_data[3] = TELQUAL_REPLY; + } else + str_data[3] = TELQUAL_IS; + return (1); +} + +int +kerberos5_send(Authenticator *ap) +{ + krb5_error_code ret; + krb5_ccache ccache; + int ap_opts; + krb5_data cksum_data; + char foo[2]; + + printf("[ Trying KERBEROS5 ... ]\r\n"); + + if (!UserNameRequested) { + if (auth_debug_mode) { + printf("Kerberos V5: no user name supplied\r\n"); + } + return (0); + } + ret = krb5_cc_default(telnet_context, &ccache); + if (ret) { + if (auth_debug_mode) { + printf( + "Kerberos V5: could not get default ccache: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + } + return (0); + } + if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) + ap_opts = AP_OPTS_MUTUAL_REQUIRED; + else + ap_opts = 0; + + ap_opts |= AP_OPTS_USE_SUBKEY; + + ret = krb5_auth_con_init(telnet_context, &auth_context); + if (ret) { + if (auth_debug_mode) { + printf( + "Kerberos V5: krb5_auth_con_init failed: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + } + return (0); + } + ret = krb5_auth_con_setaddrs_from_fd(telnet_context, + auth_context, &net); + if (ret) { + if (auth_debug_mode) { + printf("Kerberos V5: " + "krb5_auth_con_setaddrs_from_fd failed: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + } + return (0); + } + krb5_auth_con_setkeytype(telnet_context, auth_context, + ETYPE_DES_CBC_CRC); + + foo[0] = ap->type; + foo[1] = ap->way; + + cksum_data.length = sizeof(foo); + cksum_data.data = foo; + ret = krb5_mk_req(telnet_context, &auth_context, ap_opts, "host", + RemoteHostName, &cksum_data, ccache, &auth); + if (ret) { + if (1 || auth_debug_mode) { + printf("Kerberos V5: mk_req failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + } + return (0); + } + + if (!auth_sendname((unsigned char *) UserNameRequested, + strlen(UserNameRequested))) { + if (auth_debug_mode) + printf("Not enough room for user name\r\n"); + return (0); + } + if (!Data(ap, KRB_AUTH, auth.data, auth.length)) { + if (auth_debug_mode) + printf("Not enough room for authentication data\r\n"); + return (0); + } + if (auth_debug_mode) { + printf("Sent Kerberos V5 credentials to server\r\n"); + } + return (1); +} + +void +kerberos5_is(Authenticator * ap, unsigned char *data, int cnt) +{ + krb5_error_code ret; + krb5_data outbuf; + krb5_keyblock *key_block; + char *name; + krb5_principal server; + int zero = 0; + + if (cnt-- < 1) + return; + switch (*data++) { + case KRB_AUTH: + auth.data = (char *) data; + auth.length = cnt; + + auth_context = NULL; + + ret = krb5_auth_con_init(telnet_context, &auth_context); + if (ret) { + Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + ret = krb5_auth_con_setaddrs_from_fd(telnet_context, + auth_context, &zero); + if (ret) { + Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + ret = krb5_sock_to_principal(telnet_context, 0, "host", + KRB5_NT_SRV_HST, &server); + if (ret) { + Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_sock_to_principal failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + ret = krb5_rd_req(telnet_context, &auth_context, &auth, + server, NULL, NULL, &ticket); + krb5_free_principal(telnet_context, server); + + if (ret) { + char *errbuf; + + asprintf(&errbuf, + "Read req failed: %s", + get_krb5_err_text(telnet_context, ret)); + Data(ap, KRB_REJECT, errbuf, -1); + if (auth_debug_mode) + printf("%s\r\n", errbuf); + free(errbuf); + return; + } { + char foo[2]; + + foo[0] = ap->type; + foo[1] = ap->way; + + ret = krb5_verify_authenticator_checksum(telnet_context, + auth_context, foo, sizeof(foo)); + + if (ret) { + char *errbuf; + asprintf(&errbuf, "Bad checksum: %s", + get_krb5_err_text(telnet_context, ret)); + Data(ap, KRB_REJECT, errbuf, -1); + if (auth_debug_mode) + printf("%s\r\n", errbuf); + free(errbuf); + return; + } + } + ret = krb5_auth_con_getremotesubkey(telnet_context, + auth_context, &key_block); + + if (ret) { + Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_auth_con_getremotesubkey failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + if (key_block == NULL) { + ret = krb5_auth_con_getkey(telnet_context, + auth_context, + &key_block); + } + if (ret) { + Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_auth_con_getkey failed (%s)\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + if (key_block == NULL) { + Data(ap, KRB_REJECT, "no subkey received", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_auth_con_getremotesubkey returned NULL key\r\n"); + return; + } + if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { + ret = krb5_mk_rep(telnet_context, + auth_context, &outbuf); + if (ret) { + Data(ap, KRB_REJECT, + "krb5_mk_rep failed", -1); + auth_finished(ap, AUTH_REJECT); + if (auth_debug_mode) + printf("Kerberos V5: " + "krb5_mk_rep failed (%s)\r\n", + get_krb5_err_text(telnet_context, + ret)); + krb5_free_keyblock(telnet_context, key_block); + return; + } + Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length); + } + if (krb5_unparse_name(telnet_context, ticket->client, &name)) + name = 0; + + if (UserNameRequested && krb5_kuserok(telnet_context, + ticket->client, UserNameRequested)) { + Data(ap, KRB_ACCEPT, name ? name : "", name ? -1 : 0); + if (auth_debug_mode) { + printf("Kerberos5 identifies him as ``%s''\r\n", + name ? name : ""); + } + if (key_block->keytype == ETYPE_DES_CBC_MD5 || + key_block->keytype == ETYPE_DES_CBC_MD4 || + key_block->keytype == ETYPE_DES_CBC_CRC) { + Session_Key skey; + + skey.type = SK_DES; + skey.length = 8; + skey.data = key_block->keyvalue.data; + encrypt_session_key(&skey, 0); + } + } else { + char *msg; + + asprintf(&msg, "user `%s' is not authorized to " + "login as `%s'", + name ? name : "", + UserNameRequested ? UserNameRequested : ""); + if (msg == NULL) + Data(ap, KRB_REJECT, NULL, 0); + else { + Data(ap, KRB_REJECT, (void *) msg, -1); + free(msg); + } + auth_finished(ap, AUTH_REJECT); + krb5_free_keyblock(telnet_context, key_block); + break; + } + auth_finished(ap, AUTH_USER); + krb5_free_keyblock(telnet_context, key_block); + break; + case KRB_FORWARD:{ + struct passwd pws, *pwd; + char pwbuf[1024]; + char ccname[1024]; /* XXX */ + krb5_data inbuf; + krb5_ccache ccache; + inbuf.data = (char *) data; + inbuf.length = cnt; + + if (getpwnam_r(UserNameRequested, &pws, pwbuf, + sizeof(pwbuf), &pwd) != 0 || pwd == NULL) + break; + + snprintf(ccname, sizeof(ccname), + "FILE:/tmp/krb5cc_%u", pwd->pw_uid); + + ret = krb5_cc_resolve(telnet_context, ccname, &ccache); + if (ret) { + if (auth_debug_mode) + printf("Kerberos V5: could not get ccache: %s\r\n", + get_krb5_err_text(telnet_context, + ret)); + break; + } + ret = krb5_cc_initialize(telnet_context, ccache, + ticket->client); + if (ret) { + if (auth_debug_mode) + printf("Kerberos V5: could not init ccache: %s\r\n", + get_krb5_err_text(telnet_context, + ret)); + break; + } + ret = krb5_rd_cred2(telnet_context, auth_context, + ccache, &inbuf); + if (ret) { + char *errbuf; + + asprintf(&errbuf, + "Read forwarded creds failed: %s", + get_krb5_err_text(telnet_context, ret)); + if (errbuf == NULL) + Data(ap, KRB_FORWARD_REJECT, NULL, 0); + else + Data(ap, KRB_FORWARD_REJECT, errbuf, -1); + if (auth_debug_mode) + printf("Could not read forwarded credentials: %s\r\n", + errbuf); + free(errbuf); + } else + Data(ap, KRB_FORWARD_ACCEPT, 0, 0); + chown(ccname + 5, pwd->pw_uid, -1); + if (auth_debug_mode) + printf("Forwarded credentials obtained\r\n"); + break; + } + default: + if (auth_debug_mode) + printf("Unknown Kerberos option %d\r\n", data[-1]); + Data(ap, KRB_REJECT, 0, 0); + break; + } +} + +void +kerberos5_reply(Authenticator * ap, unsigned char *data, int cnt) +{ + static int mutual_complete = 0; + + if (cnt-- < 1) + return; + switch (*data++) { + case KRB_REJECT: + if (cnt > 0) { + printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n", + cnt, data); + } else + printf("[ Kerberos V5 refuses authentication ]\r\n"); + auth_send_retry(); + return; + case KRB_ACCEPT:{ + krb5_error_code ret; + Session_Key skey; + krb5_keyblock *keyblock; + + if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && + !mutual_complete) { + printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n"); + auth_send_retry(); + return; + } + if (cnt) + printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data); + else + printf("[ Kerberos V5 accepts you ]\r\n"); + + ret = krb5_auth_con_getlocalsubkey(telnet_context, + auth_context, &keyblock); + if (ret) + ret = krb5_auth_con_getkey(telnet_context, + auth_context, &keyblock); + if (ret) { + printf("[ krb5_auth_con_getkey: %s ]\r\n", + get_krb5_err_text(telnet_context, ret)); + auth_send_retry(); + return; + } + skey.type = SK_DES; + skey.length = 8; + skey.data = keyblock->keyvalue.data; + encrypt_session_key(&skey, 0); + krb5_free_keyblock(telnet_context, keyblock); + auth_finished(ap, AUTH_USER); + if (forward_flags & OPTS_FORWARD_CREDS) + kerberos5_forward(ap); + break; + } + case KRB_RESPONSE: + if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { + /* the rest of the reply should contain a krb_ap_rep */ + krb5_ap_rep_enc_part *reply; + krb5_data inbuf; + krb5_error_code ret; + + inbuf.length = cnt; + inbuf.data = (char *) data; + + ret = krb5_rd_rep(telnet_context, + auth_context, &inbuf, &reply); + if (ret) { + printf("[ Mutual authentication failed: %s ]\r\n", + get_krb5_err_text(telnet_context, ret)); + auth_send_retry(); + return; + } + krb5_free_ap_rep_enc_part(telnet_context, reply); + mutual_complete = 1; + } + return; + case KRB_FORWARD_ACCEPT: + printf("[ Kerberos V5 accepted forwarded credentials ]\r\n"); + return; + case KRB_FORWARD_REJECT: + printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n", + cnt, data); + return; + default: + if (auth_debug_mode) + printf("Unknown Kerberos option %d\r\n", data[-1]); + return; + } +} + +int +kerberos5_status(Authenticator *ap, char *name, size_t l, int level) +{ + if (level < AUTH_USER) + return (level); + + if (UserNameRequested && + krb5_kuserok(telnet_context, ticket->client, UserNameRequested)) { + strlcpy(name, UserNameRequested, l); + return (AUTH_VALID); + } else + return (AUTH_USER); +} +#define BUMP(buf, len) while (*(buf)) {++(buf), --(len);} +#define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len);} + +void +kerberos5_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen) +{ + int i; + + buf[buflen - 1] = '\0'; /* make sure its NULL terminated */ + buflen -= 1; + + switch (data[3]) { + case KRB_REJECT: /* Rejected (reason might follow) */ + strlcpy((char *) buf, " REJECT ", buflen); + goto common; + + case KRB_ACCEPT: /* Accepted (name might follow) */ + strlcpy((char *) buf, " ACCEPT ", buflen); +common: + BUMP(buf, buflen); + if (cnt <= 4) + break; + ADDC(buf, buflen, '"'); + for (i = 4; i < cnt; i++) + ADDC(buf, buflen, data[i]); + ADDC(buf, buflen, '"'); + ADDC(buf, buflen, '\0'); + break; + + + case KRB_AUTH: /* Authentication data follows */ + strlcpy((char *) buf, " AUTH", buflen); + goto common2; + + case KRB_RESPONSE: + strlcpy((char *) buf, " RESPONSE", buflen); + goto common2; + + case KRB_FORWARD: /* Forwarded credentials follow */ + strlcpy((char *) buf, " FORWARD", buflen); + goto common2; + + case KRB_FORWARD_ACCEPT: /* Forwarded credentials accepted */ + strlcpy((char *) buf, " FORWARD_ACCEPT", buflen); + goto common2; + + case KRB_FORWARD_REJECT: /* Forwarded credentials rejected */ + /* (reason might follow) */ + strlcpy((char *) buf, " FORWARD_REJECT", buflen); + goto common2; + + default: + snprintf(buf, buflen, " %d (unknown)", data[3]); +common2: + BUMP(buf, buflen); + for (i = 4; i < cnt; i++) { + snprintf(buf, buflen, " %d", data[i]); + BUMP(buf, buflen); + } + break; + } +} + +void +kerberos5_forward(Authenticator * ap) +{ + krb5_error_code ret; + krb5_ccache ccache; + krb5_creds creds; + krb5_kdc_flags flags; + krb5_data out_data; + krb5_principal principal; + + ret = krb5_cc_default(telnet_context, &ccache); + if (ret) { + if (auth_debug_mode) + printf("KerberosV5: could not get default ccache: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + ret = krb5_cc_get_principal(telnet_context, ccache, &principal); + if (ret) { + if (auth_debug_mode) + printf("KerberosV5: could not get principal: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + memset(&creds, 0, sizeof(creds)); + + creds.client = principal; + + ret = krb5_build_principal(telnet_context, &creds.server, + strlen(principal->realm), principal->realm, "krbtgt", + principal->realm, NULL); + + if (ret) { + if (auth_debug_mode) + printf("KerberosV5: could not get principal: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + creds.times.endtime = 0; + + flags.i = 0; + flags.b.forwarded = 1; + if (forward_flags & OPTS_FORWARDABLE_CREDS) + flags.b.forwardable = 1; + + ret = krb5_get_forwarded_creds(telnet_context, auth_context, + ccache, flags.i, RemoteHostName, &creds, &out_data); + if (ret) { + if (auth_debug_mode) + printf("Kerberos V5: error getting forwarded creds: %s\r\n", + get_krb5_err_text(telnet_context, ret)); + return; + } + if (!Data(ap, KRB_FORWARD, out_data.data, out_data.length)) { + if (auth_debug_mode) + printf("Not enough room for authentication data\r\n"); + } else { + if (auth_debug_mode) + printf("Forwarded local Kerberos V5 credentials to server\r\n"); + } +} +#endif /* KRB5 */ diff --git a/lib/libtelnet/key-proto.h b/lib/libtelnet/key-proto.h new file mode 100644 index 000000000..038e031ed --- /dev/null +++ b/lib/libtelnet/key-proto.h @@ -0,0 +1,63 @@ +/* $NetBSD: key-proto.h,v 1.6 2005/02/06 05:53:07 perry Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)key-proto.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef __KEY_PROTO__ +#define __KEY_PROTO__ + +#include + +int key_file_exists(void); +void key_lookup(unsigned char *, Block); +void key_stream_init(Block, Block, int); +unsigned char key_stream(int, int); +#endif diff --git a/lib/libtelnet/misc-proto.h b/lib/libtelnet/misc-proto.h new file mode 100644 index 000000000..4d3d52e23 --- /dev/null +++ b/lib/libtelnet/misc-proto.h @@ -0,0 +1,72 @@ +/* $NetBSD: misc-proto.h,v 1.10 2005/02/06 05:53:07 perry Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)misc-proto.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Copyright (C) 1990 by the Massachusetts Institute of Technology + * + * Export of this software from the United States of America is assumed + * to require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#ifndef __MISC_PROTO__ +#define __MISC_PROTO__ + +#include + +void auth_encrypt_init(const char *, const char *, const char *, int); +void auth_encrypt_user(const char *); +void auth_encrypt_connect(int); +void printd(const unsigned char *, int); + +/* + * These functions are imported from the application + */ +int telnet_net_write(unsigned char *, int); +void net_encrypt(void); +int telnet_spin(void); +char *telnet_getenv(char *); +char *telnet_gets(char *, char *, int, int); +#endif diff --git a/lib/libtelnet/misc.c b/lib/libtelnet/misc.c new file mode 100644 index 000000000..32904a040 --- /dev/null +++ b/lib/libtelnet/misc.c @@ -0,0 +1,93 @@ +/* $NetBSD: misc.c,v 1.13 2012/03/21 05:33:27 matt Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: misc.c,v 1.13 2012/03/21 05:33:27 matt Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include "misc.h" +#include "auth.h" +#include "encrypt.h" + +const char *RemoteHostName; +const char *LocalHostName; +char *UserNameRequested = 0; +int ConnectedCount = 0; + +void +auth_encrypt_init(const char *local, const char *remote, const char *name, + int server) +{ + RemoteHostName = remote; + LocalHostName = local; +#ifdef AUTHENTICATION + auth_init(name, server); +#endif +#ifdef ENCRYPTION + encrypt_init(name, server); +#endif /* ENCRYPTION */ + if (UserNameRequested) { + free(UserNameRequested); + UserNameRequested = 0; + } +} + +void +auth_encrypt_user(const char *name) +{ + if (UserNameRequested) + free(UserNameRequested); + UserNameRequested = name ? strdup(name) : 0; +} + +void +auth_encrypt_connect(int cnt) +{ +} + +void +printd(const unsigned char *data, int cnt) +{ + if (cnt > 16) + cnt = 16; + while (cnt-- > 0) { + printf(" %02x", *data); + ++data; + } +} diff --git a/lib/libtelnet/misc.h b/lib/libtelnet/misc.h new file mode 100644 index 000000000..72116505e --- /dev/null +++ b/lib/libtelnet/misc.h @@ -0,0 +1,46 @@ +/* $NetBSD: misc.h,v 1.9 2012/01/09 15:25:34 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)misc.h 8.1 (Berkeley) 6/4/93 + */ + +__BEGIN_DECLS +extern char *UserNameRequested; +extern const char *LocalHostName; +extern const char *RemoteHostName; +extern int ConnectedCount; +extern int ReservedPort; + +int isprefix(char *, const char *); +char **genget(char *, char **, int); +int Ambiguous(void *); +__END_DECLS + +#include "misc-proto.h" diff --git a/lib/libtelnet/pk.c b/lib/libtelnet/pk.c new file mode 100644 index 000000000..75d009b3d --- /dev/null +++ b/lib/libtelnet/pk.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 1991, 1993 + * Dave Safford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include + +#ifdef notdef +__FBSDID("$FreeBSD: src/contrib/telnet/libtelnet/pk.c,v 1.10 2002/08/22 06:19:07 nsayer Exp $"); +#else +__RCSID("$NetBSD: pk.c,v 1.4 2005/02/19 22:55:35 christos Exp $"); +#endif + +/* public key routines */ +/* functions: + genkeys(char *public, char *secret) + common_key(char *secret, char *public, desData *deskey) + pk_encode(char *in, *out, DesData *deskey); + pk_decode(char *in, *out, DesData *deskey); + where + char public[HEXKEYBYTES + 1]; + char secret[HEXKEYBYTES + 1]; + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pk.h" + +static void adjust(char *, const char *); + +/* + * Choose top 128 bits of the common key to use as our idea key. + */ +static void +extractideakey(BIGNUM *ck, IdeaData *ideakey) +{ + BIGNUM *a = BN_new(); + BIGNUM *z = BN_new(); + BIGNUM *base = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + size_t i; + char *k; + + (void)BN_zero(a); + (void)BN_zero(z); + (void)BN_set_word(base, 1 << 8); + BN_add(a, ck, z); + + for (i = 0; i < ((KEYSIZE - 128) / 8); i++) + BN_div(a, z, a, base, ctx); + + k = (char *)(void *)ideakey; + for (i = 0; i < 16; i++) { + BN_div(a, z, a, base, ctx); + *k++ = (char)BN_get_word(z); + } + + BN_CTX_free(ctx); + BN_free(base); + BN_free(z); + BN_free(a); +} + +/* + * Choose middle 64 bits of the common key to use as our des key, possibly + * overwriting the lower order bits by setting parity. + */ +static void +extractdeskey(BIGNUM *ck, DesData *deskey) +{ + BIGNUM *a = BN_new(); + BIGNUM *z = BN_new(); + BIGNUM *base = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + size_t i; + char *k; + + (void)BN_zero(z); + (void)BN_zero(a); + (void)BN_set_word(base, 1 << 8); + BN_add(a, ck, z); + + for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) + BN_div(a, z, a, base, ctx); + + k = (char *)deskey; + for (i = 0; i < 8; i++) { + BN_div(a, z, a, base, ctx); + *k++ = (char)BN_get_word(z); + } + + BN_CTX_free(ctx); + BN_free(base); + BN_free(z); + BN_free(a); +} + +/* + * get common key from my secret key and his public key + */ +void +common_key(char *xsecret, char *xpublic, IdeaData *ideakey, DesData *deskey) +{ + BIGNUM *public = BN_new(); + BIGNUM *secret = BN_new(); + BIGNUM *common = BN_new(); + BIGNUM *modulus = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + + (void)BN_hex2bn(&modulus, HEXMODULUS); + (void)BN_hex2bn(&public, xpublic); + (void)BN_hex2bn(&secret, xsecret); + (void)BN_zero(common); + + BN_mod_exp(common, public, secret, modulus, ctx); + extractdeskey(common, deskey); + extractideakey(common, ideakey); + des_set_odd_parity(deskey); + + BN_CTX_free(ctx); + BN_free(common); + BN_free(secret); + BN_free(public); + BN_free(modulus); +} + +/* + * Generate a seed + */ +static void +getseed(char *seed, size_t seedsize) +{ + size_t i; + + for (i = 0; i < seedsize; i++) + seed[i] = arc4random() & 0xff; +} + +/* + * Generate a random public/secret key pair + */ +void +genkeys(char *public, char *secret) +{ + size_t i; + +# define BASEBITS (8 * sizeof(short) - 1) +# define BASE (1 << BASEBITS) + + BIGNUM *pk = BN_new(); + BIGNUM *sk = BN_new(); + BIGNUM *tmp = BN_new(); + BIGNUM *base = BN_new(); + BIGNUM *root = BN_new(); + BIGNUM *modulus = BN_new(); + BN_CTX *ctx = BN_CTX_new(); + short r; + unsigned short seed[KEYSIZE/BASEBITS + 1]; + char *xkey; + + (void)BN_zero(pk); + (void)BN_zero(sk); + (void)BN_set_word(base, BASE); + (void)BN_set_word(root, PROOT); + (void)BN_hex2bn(&modulus, HEXMODULUS); + + getseed((char *)seed, sizeof(seed)); + for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) { + r = seed[i] % BASE; + (void)BN_set_word(tmp, r); + BN_mul(sk, tmp, sk, ctx); + BN_add(sk, tmp, sk); + } + + (void)BN_zero(tmp); + BN_div(tmp, sk, sk, modulus, ctx); + BN_mod_exp(pk, root, sk, modulus, ctx); + + xkey = BN_bn2hex(sk); + adjust(secret, xkey); + xkey = BN_bn2hex(pk); + adjust(public, xkey); + + BN_CTX_free(ctx); + BN_free(sk); + BN_free(base); + BN_free(pk); + BN_free(tmp); + BN_free(root); + BN_free(modulus); +} + +/* + * Adjust the input key so that it is 0-filled on the left + */ +static void +adjust(char *keyout, const char *keyin) +{ + const char *p; + char *s; + + for (p = keyin; *p; p++) + continue; + for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--) + *s = *p; + while (s >= keyout) + *s-- = '0'; +} + +static const char hextab[] = "0123456789ABCDEF"; + +/* given a DES key, cbc encrypt and translate input to terminated hex */ +void +pk_encode(const char *in, char *out, DesData *key) +{ + char buf[256]; + DesData i; + des_key_schedule k; + size_t l, op, deslen; + + (void)memset(&i, 0, sizeof(i)); + (void)memset(buf, 0, sizeof(buf)); + deslen = ((strlen(in) + 7) / 8) * 8; + des_key_sched(key, k); + des_cbc_encrypt(in, buf, deslen, k, &i, DES_ENCRYPT); + for (l = 0, op = 0; l < deslen; l++) { + out[op++] = hextab[(buf[l] & 0xf0) >> 4]; + out[op++] = hextab[(buf[l] & 0x0f)]; + } + out[op] = '\0'; +} + +/* given a DES key, translate input from hex and decrypt */ +void +pk_decode(const char *in, char *out, DesData *key) +{ + char buf[256]; + DesData i; + des_key_schedule k; + int n1, n2, op; + size_t l; + size_t len = strlen(in) / 2; + + (void)memset(&i, 0, sizeof(i)); + (void)memset(buf, 0, sizeof(buf)); + + for (l = 0, op = 0; l < len; l++, op += 2) { + if (in[op] > '9') + n1 = in[op] - 'A' + 10; + else + n1 = in[op] - '0'; + if (in[op + 1] > '9') + n2 = in[op + 1] - 'A' + 10; + else + n2 = in[op + 1] - '0'; + buf[l] = (char)(n1 * 16 + n2); + } + des_key_sched(key, k); + des_cbc_encrypt(buf, out, len, k, &i, DES_DECRYPT); + out[len] = '\0'; +} diff --git a/lib/libtelnet/pk.h b/lib/libtelnet/pk.h new file mode 100644 index 000000000..f47b447af --- /dev/null +++ b/lib/libtelnet/pk.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 1991, 1993 + * Dave Safford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/contrib/telnet/libtelnet/pk.h,v 1.6 2001/11/30 21:06:34 markm Exp $ + */ + +/* header for the des routines that we will use */ + +typedef unsigned char byte, DesData[ 8], IdeaData[16]; +#define DesKeys des_key_schedule + +#define DES_DECRYPT 0 +#define DES_ENCRYPT 1 + +/* public key routines */ +/* functions: + genkeys(char *public, char *secret) + common_key(char *secret, char *public, desData *deskey) + where + char public[HEXKEYBYTES + 1]; + char secret[HEXKEYBYTES + 1]; + */ + +#define HEXMODULUS "d4a0ba0250b6fd2ec626e7efd637df76c716e22d0944b88b" +#define HEXKEYBYTES 48 +#define KEYSIZE 192 +#define KEYBYTES 24 +#define PROOT 3 + +extern void genkeys(char *public, char *secret); +extern void common_key(char *secret, char *public, IdeaData *common, + DesData *deskey); +extern void pk_encode(const char *in, char *out, DesData *deskey); +extern void pk_decode(const char *in, char *out, DesData *deskey); diff --git a/lib/libtelnet/shlib_version b/lib/libtelnet/shlib_version new file mode 100644 index 000000000..4075bd016 --- /dev/null +++ b/lib/libtelnet/shlib_version @@ -0,0 +1,5 @@ +# $NetBSD: shlib_version,v 1.11 2009/01/11 03:07:50 christos Exp $ +# Remember to update distrib/sets/lists/base/shl.* when changing +# +major=7 +minor=0 diff --git a/lib/libtelnet/spx.c b/lib/libtelnet/spx.c new file mode 100644 index 000000000..054fc513c --- /dev/null +++ b/lib/libtelnet/spx.c @@ -0,0 +1,594 @@ +/* $NetBSD: spx.c,v 1.7 2005/04/19 03:19:46 christos Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)spx.c 8.2 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: spx.c,v 1.7 2005/04/19 03:19:46 christos Exp $"); +#endif +#endif /* not lint */ + +#ifdef SPX +/* + * COPYRIGHT (C) 1990 DIGITAL EQUIPMENT CORPORATION + * ALL RIGHTS RESERVED + * + * "Digital Equipment Corporation authorizes the reproduction, + * distribution and modification of this software subject to the following + * restrictions: + * + * 1. Any partial or whole copy of this software, or any modification + * thereof, must include this copyright notice in its entirety. + * + * 2. This software is supplied "as is" with no warranty of any kind, + * expressed or implied, for any purpose, including any warranty of fitness + * or merchantibility. DIGITAL assumes no responsibility for the use or + * reliability of this software, nor promises to provide any form of + * support for it on any basis. + * + * 3. Distribution of this software is authorized only if no profit or + * remuneration of any kind is received in exchange for such distribution. + * + * 4. This software produces public key authentication certificates + * bearing an expiration date established by DIGITAL and RSA Data + * Security, Inc. It may cease to generate certificates after the expiration + * date. Any modification of this software that changes or defeats + * the expiration date or its effect is unauthorized. + * + * 5. Software that will renew or extend the expiration date of + * authentication certificates produced by this software may be obtained + * from RSA Data Security, Inc., 10 Twin Dolphin Drive, Redwood City, CA + * 94065, (415)595-8782, or from DIGITAL" + * + */ + +#include +#include +#include +#include "gssapi_defs.h" +#include +#ifdef NO_STRING_H +#include +#else +#include +#endif + +#include +#include "encrypt.h" +#include "auth.h" +#include "misc.h" + +extern auth_debug_mode; + +static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0, + AUTHTYPE_SPX, }; +static unsigned char str_name[1024] = { IAC, SB, TELOPT_AUTHENTICATION, + TELQUAL_NAME, }; + +#define SPX_AUTH 0 /* Authentication data follows */ +#define SPX_REJECT 1 /* Rejected (reason might follow) */ +#define SPX_ACCEPT 2 /* Accepted */ + +#ifdef ENCRYPTION +static Block session_key = { 0 }; +#endif /* ENCRYPTION */ +static Schedule sched; +static Block challenge = { 0 }; + + +/*******************************************************************/ + +gss_OID_set actual_mechs; +gss_OID actual_mech_type, output_name_type; +int major_status, status, msg_ctx = 0, new_status; +int req_flags = 0, ret_flags, lifetime_rec; +gss_cred_id_t gss_cred_handle; +gss_ctx_id_t actual_ctxhandle, context_handle; +gss_buffer_desc output_token, input_token, input_name_buffer; +gss_buffer_desc status_string; +gss_name_t desired_targname, src_name; +gss_channel_bindings input_chan_bindings; +char lhostname[GSS_C_MAX_PRINTABLE_NAME]; +char targ_printable[GSS_C_MAX_PRINTABLE_NAME]; +int to_addr=0, from_addr=0; +char *address; +gss_buffer_desc fullname_buffer; +gss_OID fullname_type; +gss_cred_id_t gss_delegated_cred_handle; + +/*******************************************************************/ + + + + static int +Data(ap, type, d, c) + Authenticator *ap; + int type; + void *d; + int c; +{ + unsigned char *p = str_data + 4; + unsigned char *cd = (unsigned char *)d; + + if (c == -1) + c = strlen((char *)cd); + + if (0) { + printf("%s:%d: [%d] (%d)", + str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", + str_data[3], + type, c); + printd(d, c); + printf("\r\n"); + } + *p++ = ap->type; + *p++ = ap->way; + *p++ = type; + while (c-- > 0) { + if ((*p++ = *cd++) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + if (str_data[3] == TELQUAL_IS) + printsub('>', &str_data[2], p - (&str_data[2])); + return(telnet_net_write(str_data, p - str_data)); +} + + int +spx_init(ap, server) + Authenticator *ap; + int server; +{ + gss_cred_id_t tmp_cred_handle; + + if (server) { + str_data[3] = TELQUAL_REPLY; + gethostname(lhostname, sizeof(lhostname)); + strlcpy(targ_printable, "SERVICE:rcmd@", + sizeof(targ_printable)); + strlcat(targ_printable, lhostname, sizeof(targ_printable)); + input_name_buffer.length = strlen(targ_printable); + input_name_buffer.value = targ_printable; + major_status = gss_import_name(&status, + &input_name_buffer, + GSS_C_NULL_OID, + &desired_targname); + major_status = gss_acquire_cred(&status, + desired_targname, + 0, + GSS_C_NULL_OID_SET, + GSS_C_ACCEPT, + &tmp_cred_handle, + &actual_mechs, + &lifetime_rec); + if (major_status != GSS_S_COMPLETE) return(0); + } else { + str_data[3] = TELQUAL_IS; + } + return(1); +} + + int +spx_send(ap) + Authenticator *ap; +{ + Block enckey; + int r; + + gss_OID actual_mech_type, output_name_type; + int msg_ctx = 0, new_status, status; + int req_flags = 0, ret_flags, lifetime_rec, major_status; + gss_buffer_desc output_token, input_token, input_name_buffer; + gss_buffer_desc output_name_buffer, status_string; + gss_name_t desired_targname; + gss_channel_bindings input_chan_bindings; + char targ_printable[GSS_C_MAX_PRINTABLE_NAME]; + int from_addr=0, to_addr=0, myhostlen, j; + int deleg_flag=1, mutual_flag=0, replay_flag=0, seq_flag=0; + char *address; + + printf("[ Trying SPX ... ]\n"); + strlcpy(targ_printable, "SERVICE:rcmd@", sizeof(targ_printable)); + strlcat(targ_printable, RemoteHostName, sizeof(targ_printable)); + + input_name_buffer.length = strlen(targ_printable); + input_name_buffer.value = targ_printable; + + if (!UserNameRequested) { + return(0); + } + + major_status = gss_import_name(&status, + &input_name_buffer, + GSS_C_NULL_OID, + &desired_targname); + + + major_status = gss_display_name(&status, + desired_targname, + &output_name_buffer, + &output_name_type); + + printf("target is '%s'\n", output_name_buffer.value); fflush(stdout); + + major_status = gss_release_buffer(&status, &output_name_buffer); + + input_chan_bindings = (gss_channel_bindings) + malloc(sizeof(gss_channel_bindings_desc)); + + input_chan_bindings->initiator_addrtype = GSS_C_AF_INET; + input_chan_bindings->initiator_address.length = 4; + address = (char *) malloc(4); + input_chan_bindings->initiator_address.value = (char *) address; + address[0] = ((from_addr & 0xff000000) >> 24); + address[1] = ((from_addr & 0xff0000) >> 16); + address[2] = ((from_addr & 0xff00) >> 8); + address[3] = (from_addr & 0xff); + input_chan_bindings->acceptor_addrtype = GSS_C_AF_INET; + input_chan_bindings->acceptor_address.length = 4; + address = (char *) malloc(4); + input_chan_bindings->acceptor_address.value = (char *) address; + address[0] = ((to_addr & 0xff000000) >> 24); + address[1] = ((to_addr & 0xff0000) >> 16); + address[2] = ((to_addr & 0xff00) >> 8); + address[3] = (to_addr & 0xff); + input_chan_bindings->application_data.length = 0; + + req_flags = 0; + if (deleg_flag) req_flags = req_flags | 1; + if (mutual_flag) req_flags = req_flags | 2; + if (replay_flag) req_flags = req_flags | 4; + if (seq_flag) req_flags = req_flags | 8; + + major_status = gss_init_sec_context(&status, /* minor status */ + GSS_C_NO_CREDENTIAL, /* cred handle */ + &actual_ctxhandle, /* ctx handle */ + desired_targname, /* target name */ + GSS_C_NULL_OID, /* mech type */ + req_flags, /* req flags */ + 0, /* time req */ + input_chan_bindings, /* chan binding */ + GSS_C_NO_BUFFER, /* input token */ + &actual_mech_type, /* actual mech */ + &output_token, /* output token */ + &ret_flags, /* ret flags */ + &lifetime_rec); /* time rec */ + + if ((major_status != GSS_S_COMPLETE) && + (major_status != GSS_S_CONTINUE_NEEDED)) { + gss_display_status(&new_status, + status, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &msg_ctx, + &status_string); + printf("%s\n", status_string.value); + return(0); + } + + if (!auth_sendname(UserNameRequested, strlen(UserNameRequested))) { + return(0); + } + + if (!Data(ap, SPX_AUTH, (void *)output_token.value, output_token.length)) { + return(0); + } + + return(1); +} + + void +spx_is(ap, data, cnt) + Authenticator *ap; + unsigned char *data; + int cnt; +{ + Session_Key skey; + Block datablock; + int r; + + if (cnt-- < 1) + return; + switch (*data++) { + case SPX_AUTH: + input_token.length = cnt; + input_token.value = (char *) data; + + gethostname(lhostname, sizeof(lhostname)); + + strlcpy(targ_printable, "SERVICE:rcmd@", + sizeof(targ_printable)); + strlcat(targ_printable, lhostname, sizeof(targ_printable)); + + input_name_buffer.length = strlen(targ_printable); + input_name_buffer.value = targ_printable; + + major_status = gss_import_name(&status, + &input_name_buffer, + GSS_C_NULL_OID, + &desired_targname); + + major_status = gss_acquire_cred(&status, + desired_targname, + 0, + GSS_C_NULL_OID_SET, + GSS_C_ACCEPT, + &gss_cred_handle, + &actual_mechs, + &lifetime_rec); + + major_status = gss_release_name(&status, desired_targname); + + input_chan_bindings = (gss_channel_bindings) + malloc(sizeof(gss_channel_bindings_desc)); + + input_chan_bindings->initiator_addrtype = GSS_C_AF_INET; + input_chan_bindings->initiator_address.length = 4; + address = (char *) malloc(4); + input_chan_bindings->initiator_address.value = (char *) address; + address[0] = ((from_addr & 0xff000000) >> 24); + address[1] = ((from_addr & 0xff0000) >> 16); + address[2] = ((from_addr & 0xff00) >> 8); + address[3] = (from_addr & 0xff); + input_chan_bindings->acceptor_addrtype = GSS_C_AF_INET; + input_chan_bindings->acceptor_address.length = 4; + address = (char *) malloc(4); + input_chan_bindings->acceptor_address.value = (char *) address; + address[0] = ((to_addr & 0xff000000) >> 24); + address[1] = ((to_addr & 0xff0000) >> 16); + address[2] = ((to_addr & 0xff00) >> 8); + address[3] = (to_addr & 0xff); + input_chan_bindings->application_data.length = 0; + + major_status = gss_accept_sec_context(&status, + &context_handle, + gss_cred_handle, + &input_token, + input_chan_bindings, + &src_name, + &actual_mech_type, + &output_token, + &ret_flags, + &lifetime_rec, + &gss_delegated_cred_handle); + + + if (major_status != GSS_S_COMPLETE) { + + major_status = gss_display_name(&status, + src_name, + &fullname_buffer, + &fullname_type); + Data(ap, SPX_REJECT, (void *)"auth failed", -1); + auth_finished(ap, AUTH_REJECT); + return; + } + + major_status = gss_display_name(&status, + src_name, + &fullname_buffer, + &fullname_type); + + + Data(ap, SPX_ACCEPT, (void *)output_token.value, output_token.length); + auth_finished(ap, AUTH_USER); + break; + + default: + Data(ap, SPX_REJECT, 0, 0); + break; + } +} + + + void +spx_reply(ap, data, cnt) + Authenticator *ap; + unsigned char *data; + int cnt; +{ + Session_Key skey; + + if (cnt-- < 1) + return; + switch (*data++) { + case SPX_REJECT: + if (cnt > 0) { + printf("[ SPX refuses authentication because %.*s ]\r\n", + cnt, data); + } else + printf("[ SPX refuses authentication ]\r\n"); + auth_send_retry(); + return; + case SPX_ACCEPT: + printf("[ SPX accepts you ]\n"); + if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { + /* + * Send over the encrypted challenge. + */ + input_token.value = (char *) data; + input_token.length = cnt; + + major_status = gss_init_sec_context(&status, /* minor stat */ + GSS_C_NO_CREDENTIAL, /* cred handle */ + &actual_ctxhandle, /* ctx handle */ + desired_targname, /* target name */ + GSS_C_NULL_OID, /* mech type */ + req_flags, /* req flags */ + 0, /* time req */ + input_chan_bindings, /* chan binding */ + &input_token, /* input token */ + &actual_mech_type, /* actual mech */ + &output_token, /* output token */ + &ret_flags, /* ret flags */ + &lifetime_rec); /* time rec */ + + if (major_status != GSS_S_COMPLETE) { + gss_display_status(&new_status, + status, + GSS_C_MECH_CODE, + GSS_C_NULL_OID, + &msg_ctx, + &status_string); + printf("[ SPX mutual response fails ... '%s' ]\r\n", + status_string.value); + auth_send_retry(); + return; + } + } + auth_finished(ap, AUTH_USER); + return; + + default: + return; + } +} + + int +spx_status(ap, name, l, level) + Authenticator *ap; + char *name; + size_t l; + int level; +{ + + gss_buffer_desc fullname_buffer, acl_file_buffer; + gss_OID fullname_type; + char acl_file[160], fullname[160]; + int major_status, status = 0; + struct passwd pws, *pwd; + char pwbuf[1024]; + + /* + * hard code fullname to + * "SPX:/C=US/O=Digital/OU=LKG/OU=Sphinx/OU=Users/CN=Kannan Alagappan" + * and acl_file to "~kannan/.sphinx" + */ + + if (getpwnam_r(UserNameRequested, &pws, pwbuf, sizeof(pwbuf), &pwd) + != 0 || pwd == NULL) { + return(AUTH_USER); /* not authenticated */ + } + + strlcpy(acl_file, pwd->pw_dir, sizeof(acl_file)); + strlcat(acl_file, "/.sphinx", sizeof(acl_file)); + acl_file_buffer.value = acl_file; + acl_file_buffer.length = strlen(acl_file); + + major_status = gss_display_name(&status, + src_name, + &fullname_buffer, + &fullname_type); + + if (level < AUTH_USER) + return(level); + + major_status = gss__check_acl(&status, &fullname_buffer, + &acl_file_buffer); + + if (major_status == GSS_S_COMPLETE) { + strlcpy(name, UserNameRequested, l); + return(AUTH_VALID); + } else { + return(AUTH_USER); + } + +} + +#define BUMP(buf, len) while (*(buf)) {++(buf), --(len);} +#define ADDC(buf, len, c) if ((len) > 0) {*(buf)++ = (c); --(len);} + + void +spx_printsub(data, cnt, buf, buflen) + unsigned char *data, *buf; + int cnt, buflen; +{ + char lbuf[32]; + register int i; + + buf[buflen-1] = '\0'; /* make sure its NULL terminated */ + buflen -= 1; + + switch(data[3]) { + case SPX_REJECT: /* Rejected (reason might follow) */ + strncpy((char *)buf, " REJECT ", buflen); + goto common; + + case SPX_ACCEPT: /* Accepted (name might follow) */ + strncpy((char *)buf, " ACCEPT ", buflen); + common: + BUMP(buf, buflen); + if (cnt <= 4) + break; + ADDC(buf, buflen, '"'); + for (i = 4; i < cnt; i++) + ADDC(buf, buflen, data[i]); + ADDC(buf, buflen, '"'); + ADDC(buf, buflen, '\0'); + break; + + case SPX_AUTH: /* Authentication data follows */ + strncpy((char *)buf, " AUTH", buflen); + goto common2; + + default: + snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[3]); + strncpy((char *)buf, lbuf, buflen); + common2: + BUMP(buf, buflen); + for (i = 4; i < cnt; i++) { + snprintf(lbuf, sizeof(lbuf), " %d", data[i]); + strncpy((char *)buf, lbuf, buflen); + BUMP(buf, buflen); + } + break; + } +} + +#endif + +#ifdef notdef + +prkey(msg, key) + char *msg; + unsigned char *key; +{ + register int i; + printf("%s:", msg); + for (i = 0; i < 8; i++) + printf(" %3d", key[i]); + printf("\r\n"); +} +#endif diff --git a/lib/libtelnet/sra.c b/lib/libtelnet/sra.c new file mode 100644 index 000000000..fcef2570a --- /dev/null +++ b/lib/libtelnet/sra.c @@ -0,0 +1,637 @@ +/*- + * Copyright (c) 1991, 1993 + * Dave Safford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#ifdef notdef +__FBSDID("$FreeBSD: src/contrib/telnet/libtelnet/sra.c,v 1.16 2002/05/06 09:48:02 markm Exp $"); +#else +__RCSID("$NetBSD: sra.c,v 1.11 2012/01/09 15:25:34 christos Exp $"); +#endif + +#ifdef SRA +#ifdef ENCRYPTION +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NOPAM +#include +#else +#include +#endif + +#include "auth.h" +#include "misc.h" +#include "encrypt.h" +#include "pk.h" + +char pka[HEXKEYBYTES+1], ska[HEXKEYBYTES+1], pkb[HEXKEYBYTES+1]; +char *user, *pass, *xuser, *xpass; +char *passprompt, *xpassprompt; +DesData ck; +IdeaData ik; + +extern int auth_debug_mode; +extern char *line; /* see sys_term.c */ + +static int sra_valid = 0; +static int passwd_sent = 0; + +static unsigned char str_data[1024] = { IAC, SB, TELOPT_AUTHENTICATION, 0, + AUTHTYPE_SRA, }; + +#define SMALL_LEN 256 +#define XSMALL_LEN 513 +#define SRA_KEY 0 +#define SRA_USER 1 +#define SRA_CONTINUE 2 +#define SRA_PASS 3 +#define SRA_ACCEPT 4 +#define SRA_REJECT 5 + +static int check_user(char *, const char *); + +/* support routine to send out authentication message */ +static int +Data(Authenticator *ap, int type, void *d, int c) +{ + unsigned char *p = str_data + 4; + unsigned char *cd = d; + + if (c == -1) + c = strlen(d); + + if (auth_debug_mode) { + printf("%s:%d: [%d] (%d)", + str_data[3] == TELQUAL_IS ? ">>>IS" : ">>>REPLY", + str_data[3], type, c); + printd(d, c); + printf("\r\n"); + } + *p++ = ap->type; + *p++ = ap->way; + *p++ = type; + while (c-- > 0) { + if ((*p++ = *cd++) == IAC) + *p++ = IAC; + } + *p++ = IAC; + *p++ = SE; + if (str_data[3] == TELQUAL_IS) + printsub('>', &str_data[2], p - (&str_data[2])); + return telnet_net_write(str_data, p - str_data); +} + +int +sra_init(Authenticator *ap __unused, int server) +{ + if (server) + str_data[3] = TELQUAL_REPLY; + else + str_data[3] = TELQUAL_IS; + + user = malloc(SMALL_LEN); + xuser = malloc(XSMALL_LEN); + pass = malloc(SMALL_LEN); + xpass = malloc(XSMALL_LEN); + passprompt = malloc(SMALL_LEN); + xpassprompt = malloc(XSMALL_LEN); + + if (user == NULL || xuser == NULL || pass == NULL || xpass == + NULL || passprompt == NULL || xpassprompt == NULL) + return 0; /* malloc failed */ + + passwd_sent = 0; + + genkeys(pka, ska); + return 1; +} + +/* client received a go-ahead for sra */ +int +sra_send(Authenticator *ap) +{ + /* send PKA */ + + if (auth_debug_mode) + printf("Sent PKA to server.\r\n" ); + printf("Trying SRA secure login:\r\n"); + if (!Data(ap, SRA_KEY, (void *)pka, HEXKEYBYTES)) { + if (auth_debug_mode) + printf("Not enough room for authentication data\r\n"); + return 0; + } + + return 1; +} + +/* server received an IS -- could be SRA KEY, USER, or PASS */ +void +sra_is(Authenticator *ap, unsigned char *data, int cnt) +{ + int valid; + Session_Key skey; + + if (cnt-- < 1) + goto bad; + switch (*data++) { + + case SRA_KEY: + if (cnt < HEXKEYBYTES) { + Data(ap, SRA_REJECT, (void *)0, 0); + auth_finished(ap, AUTH_USER); + if (auth_debug_mode) { + printf("SRA user rejected for bad PKB\r\n"); + } + return; + } + if (auth_debug_mode) + printf("Sent pka\r\n"); + if (!Data(ap, SRA_KEY, (void *)pka, HEXKEYBYTES)) { + if (auth_debug_mode) + printf("Not enough room\r\n"); + return; + } + memcpy(pkb, data, HEXKEYBYTES); + pkb[HEXKEYBYTES] = '\0'; + common_key(ska, pkb, &ik, &ck); + return; + + case SRA_USER: + /* decode KAB(u) */ + if (cnt > XSMALL_LEN - 1) /* Attempted buffer overflow */ + break; + memcpy(xuser, data, cnt); + xuser[cnt] = '\0'; + pk_decode(xuser, user, &ck); + auth_encrypt_user(user); +#ifndef NOPAM + (void)check_user(user, "*"); +#endif + pk_encode(passprompt, xpassprompt, &ck); + Data(ap, SRA_CONTINUE, xpassprompt, XSMALL_LEN - 1); + + return; + + case SRA_PASS: + if (cnt > XSMALL_LEN - 1) /* Attempted buffer overflow */ + break; + /* decode KAB(P) */ + memcpy(xpass, data, cnt); + xpass[cnt] = '\0'; + pk_decode(xpass, pass, &ck); + + /* check user's password */ + valid = check_user(user, pass); + + if(valid) { + /* PAM (via check_user()) may have changed 'user' */ + auth_encrypt_user(user); + Data(ap, SRA_ACCEPT, (void *)0, 0); + skey.data = ck; + skey.type = SK_DES; + skey.length = 8; + encrypt_session_key(&skey, 1); + + sra_valid = 1; + auth_finished(ap, AUTH_VALID); + if (auth_debug_mode) { + printf("SRA user accepted\r\n"); + } + } + else { + pk_encode(passprompt, xpassprompt, &ck); + Data(ap, SRA_CONTINUE, (void *)xpassprompt, + XSMALL_LEN - 1); + if (auth_debug_mode) { + printf("SRA user failed\r\n"); + } + } + return; + + default: + if (auth_debug_mode) + printf("Unknown SRA option %d\r\n", data[-1]); + } +bad: + Data(ap, SRA_REJECT, 0, 0); + sra_valid = 0; + auth_finished(ap, AUTH_REJECT); +} + +/* client received REPLY -- could be SRA KEY, CONTINUE, ACCEPT, or REJECT */ +void +sra_reply(Authenticator *ap, unsigned char *data, int cnt) +{ + char uprompt[SMALL_LEN], tuser[SMALL_LEN]; + Session_Key skey; + size_t i; + + if (cnt-- < 1) + return; + switch (*data++) { + + case SRA_KEY: + /* calculate common key */ + if (cnt < HEXKEYBYTES) { + if (auth_debug_mode) { + printf("SRA user rejected for bad PKB\r\n"); + } + return; + } + memcpy(pkb, data, HEXKEYBYTES); + pkb[HEXKEYBYTES] = '\0'; + + common_key(ska, pkb, &ik, &ck); + + enc_user: + + /* encode user */ + memset(tuser, 0, sizeof(tuser)); + snprintf(uprompt, sizeof(uprompt), "User (%s): ", + UserNameRequested); + if (telnet_gets(uprompt, tuser, SMALL_LEN - 1, 1) == NULL) { + printf("\n"); + exit(1); + } + if (tuser[0] == '\n' || tuser[0] == '\r' ) + strlcpy(user, UserNameRequested, SMALL_LEN); + else { + /* telnet_gets leaves the newline on */ + for(i = 0; i < sizeof(tuser); i++) { + if (tuser[i] == '\n') { + tuser[i] = '\0'; + break; + } + } + strlcpy(user, tuser, SMALL_LEN); + } + pk_encode(user, xuser, &ck); + + /* send it off */ + if (auth_debug_mode) + printf("Sent KAB(U)\r\n"); + if (!Data(ap, SRA_USER, (void *)xuser, strlen(xuser))) { + if (auth_debug_mode) + printf("Not enough room\r\n"); + return; + } + break; + + case SRA_CONTINUE: + if (passwd_sent) { + passwd_sent = 0; + printf("[ SRA login failed ]\r\n"); + goto enc_user; + } + if (cnt > XSMALL_LEN - 1) { + break; + } else if (cnt > 0) { + (void)memcpy(xpassprompt, data, cnt); + pk_decode(xpassprompt, passprompt, &ck); + } else { + (void)strlcpy(passprompt, "Password: ", SMALL_LEN); + } + /* encode password */ + memset(pass, 0, SMALL_LEN); + if (telnet_gets(passprompt, pass, SMALL_LEN - 1, 0) == NULL) { + printf("\n"); + exit(1); + } + pk_encode(pass, xpass, &ck); + /* send it off */ + if (auth_debug_mode) + printf("Sent KAB(P)\r\n"); + if (!Data(ap, SRA_PASS, (void *)xpass, strlen(xpass))) { + if (auth_debug_mode) + printf("Not enough room\r\n"); + return; + } + passwd_sent = 1; + break; + + case SRA_REJECT: + printf("[ SRA refuses authentication ]\r\n"); + printf("Trying plaintext login:\r\n"); + auth_finished(0, AUTH_REJECT); + return; + + case SRA_ACCEPT: + printf("[ SRA accepts you ]\r\n"); + skey.data = ck; + skey.type = SK_DES; + skey.length = 8; + encrypt_session_key(&skey, 0); + + auth_finished(ap, AUTH_VALID); + return; + default: + if (auth_debug_mode) + printf("Unknown SRA option %d\r\n", data[-1]); + return; + } +} + +int +sra_status(Authenticator *ap __unused, char *name, size_t len, int level) +{ + if (level < AUTH_USER) + return level; + if (UserNameRequested && sra_valid) { + strlcpy(name, UserNameRequested, len); + return AUTH_VALID; + } else + return AUTH_USER; +} + +#define BUMP(buf, len) while (*(buf)) { ++(buf), --(len); } +#define ADDC(buf, len, c) if ((len) > 0) { *(buf)++ = (c); --(len); } + +void +sra_printsub(unsigned char *data, int cnt, unsigned char *ubuf, int buflen) +{ + char lbuf[32], *buf = (char *)ubuf; + int i; + + buf[buflen - 1] = '\0'; /* make sure its NULL terminated */ + buflen -= 1; + + switch(data[3]) { + + case SRA_CONTINUE: + strncpy(buf, " CONTINUE ", buflen); + goto common; + + case SRA_REJECT: /* Rejected (reason might follow) */ + strncpy(buf, " REJECT ", buflen); + goto common; + + case SRA_ACCEPT: /* Accepted (name might follow) */ + strncpy(buf, " ACCEPT ", buflen); + + common: + BUMP(buf, buflen); + if (cnt <= 4) + break; + ADDC(buf, buflen, '"'); + for (i = 4; i < cnt; i++) + ADDC(buf, buflen, data[i]); + ADDC(buf, buflen, '"'); + ADDC(buf, buflen, '\0'); + break; + + case SRA_KEY: /* Authentication data follows */ + strncpy(buf, " KEY ", buflen); + goto common2; + + case SRA_USER: + strncpy(buf, " USER ", buflen); + goto common2; + + case SRA_PASS: + strncpy(buf, " PASS ", buflen); + goto common2; + + default: + snprintf(lbuf, sizeof(lbuf), " %d (unknown)", data[3]); + strncpy(buf, lbuf, buflen); + common2: + BUMP(buf, buflen); + for (i = 4; i < cnt; i++) { + snprintf(lbuf, sizeof(lbuf), " %d", data[i]); + strncpy(buf, lbuf, buflen); + BUMP(buf, buflen); + } + break; + } +} + +#ifdef NOPAM +static int +isroot(const char *usr) +{ + struct passwd pws, *pwd; + char pwbuf[1024]; + + if (getpwnam_r(usr, &pws, pwbuf, sizeof(pwbuf), &pwd) != 0 || + pwd == NULL) + return 0; + return (!pwd->pw_uid); +} + +static int +rootterm(const char *ttyname) +{ + struct ttyent *t; + const char *ttyn; + + ttyn = ttyname; + if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) + ttyn += sizeof(_PATH_DEV) - 1; + + return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE); +} + +static int +check_user(char *name, const char *cred) +{ + struct passwd pws, *pw; + char pwbuf[1024]; + char *xpasswd, *salt; + + if (isroot(name) && !rootterm(line)) + { + crypt("AA", "*"); /* Waste some time to simulate success */ + return 0; + } + + if (getpwnam_r(name, &pws, pwbuf, sizeof(pwbuf), &pw) == 0 && + pw != NULL) { + if (pw->pw_shell == NULL) { + return 0; + } + + salt = pw->pw_passwd; + xpasswd = crypt(cred, salt); + /* The strcmp does not catch null passwords! */ + if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) { + return 0; + } + return 1; + } + return 0; +} +#else /* !NOPAM */ + +/* + * The following is stolen from ftpd, which stole it from the imap-uw + * PAM module and login.c. It is needed because we can't really + * "converse" with the user, having already gone to the trouble of + * getting their username and password through an encrypted channel. + */ + +#define COPY_STRING(s) (s ? strdup(s) : NULL) + +struct cred_t { + const char *uname; + const char *pass; +}; +typedef struct cred_t cred_t; + +static int +auth_conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata) +{ + int i; + cred_t *cred = appdata; + struct pam_response *reply = malloc(sizeof(*reply) * num_msg); + + if (reply == NULL) + return PAM_BUF_ERR; + + for (i = 0; i < num_msg; i++) { + switch (msg[i]->msg_style) { + case PAM_PROMPT_ECHO_ON: /* assume want user name */ + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = COPY_STRING(cred->uname); + /* PAM frees resp. */ + break; + case PAM_PROMPT_ECHO_OFF: /* assume want password */ + (void)strlcpy(passprompt, msg[i]->msg, SMALL_LEN); + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = COPY_STRING(cred->pass); + /* PAM frees resp. */ + break; + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + reply[i].resp_retcode = PAM_SUCCESS; + reply[i].resp = NULL; + break; + default: /* unknown message style */ + free(reply); + return PAM_CONV_ERR; + } + } + + *resp = reply; + return PAM_SUCCESS; +} + +/* + * The PAM version as a side effect may put a new username in *name. + */ +static int +check_user(char *name, const char *cred) +{ + pam_handle_t *pamh = NULL; + const void *item; + int rval; + int e; + cred_t auth_cred = { name, cred }; + struct pam_conv conv = { &auth_conv, &auth_cred }; + + e = pam_start("telnetd", name, &conv, &pamh); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); + return 0; + } + +#if 0 /* Where can we find this value? */ + e = pam_set_item(pamh, PAM_RHOST, remotehost); + if (e != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", + pam_strerror(pamh, e)); + return 0; + } +#endif + + e = pam_authenticate(pamh, 0); + switch (e) { + case PAM_SUCCESS: + /* + * With PAM we support the concept of a "template" + * user. The user enters a login name which is + * authenticated by PAM, usually via a remote service + * such as RADIUS or TACACS+. If authentication + * succeeds, a different but related "template" name + * is used for setting the credentials, shell, and + * home directory. The name the user enters need only + * exist on the remote authentication server, but the + * template name must be present in the local password + * database. + * + * This is supported by two various mechanisms in the + * individual modules. However, from the application's + * point of view, the template user is always passed + * back as a changed value of the PAM_USER item. + */ + if ((e = pam_get_item(pamh, PAM_USER, &item)) == + PAM_SUCCESS) { + strlcpy(name, item, SMALL_LEN); + } else + syslog(LOG_ERR, "Couldn't get PAM_USER: %s", + pam_strerror(pamh, e)); +#if 0 /* pam_securetty(8) should be used to enforce this */ + if (isroot(name) && !rootterm(line)) + rval = 0; + else +#endif + rval = 1; + break; + + case PAM_AUTH_ERR: + case PAM_USER_UNKNOWN: + case PAM_MAXTRIES: + rval = 0; + break; + + default: + syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); + rval = 0; + break; + } + + if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); + rval = 0; + } + return rval; +} + +#endif /* !NOPAM */ + +#endif /* ENCRYPTION */ +#endif /* SRA */ diff --git a/usr.bin/Makefile b/usr.bin/Makefile index a58fbaed3..7fa904bd2 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -26,7 +26,7 @@ SUBDIR= asa \ \ sdiff sed seq shar shlock \ shuffle sort split stat su \ - tail tee tic time touch \ + tail tee telnet tic time touch \ tput \ tr true tsort tty ul uname unexpand unifdef \ uniq units unvis unzip users \ diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile new file mode 100644 index 000000000..cc93f95f4 --- /dev/null +++ b/usr.bin/telnet/Makefile @@ -0,0 +1,89 @@ +# $NetBSD: Makefile,v 1.50 2012/08/10 12:10:27 joerg Exp $ +# +# Copyright (c) 1990 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 +# + +WARNS?= 4 # XXX -Wshadow etc. fix asap +CWARNFLAGS.clang+= -Wno-tautological-compare -Wno-format-security + +.include + +USE_FORT?= yes # network client + +PROG= telnet + +CPPFLAGS+=-DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK +CPPFLAGS+=-I${.CURDIR} + +LDADD+= -lterminfo ${LIBTELNETDIR}/libtelnet.a +DPADD+= ${LIBTERMINFO} ${LIBTELNETDIR}/libtelnet.a + +SRCS= authenc.c commands.c main.c network.c ring.c sys_bsd.c telnet.c \ + terminal.c tn3270.c utilities.c + +CPPFLAGS+=-I${NETBSDSRCDIR}/lib +.if !defined(__MINIX) +CPPFLAGS+=-DIPSEC +LDADD+= -lipsec +DPADD+= ${LIBIPSEC} +.endif # !defined(__MINIX) + +.if (${USE_INET6} != "no") +CPPFLAGS+=-DINET6 +.endif + +LIBTELNETDIR!= cd ${.CURDIR}/../../lib/libtelnet; ${PRINTOBJDIR} + +.if (${USE_KERBEROS} != "no") +CPPFLAGS+=-DKRB5 -DFORWARD +LDADD+= -lkrb5 -lasn1 -lcom_err -lroken +DPADD+= ${LIBKRB5} ${LIBASN1} ${LIBCOM_ERR} ${LIBROKEN} +.endif + +.if (${MKCRYPTO} != "no") +CPPFLAGS+=-DAUTHENTICATION -DENCRYPTION +LDADD+= -ldes -lcrypto -lcrypt +DPADD+= ${LIBDES} ${LIBCRYPTO} ${LIBCRYPT} +.endif + +.if (${USE_PAM} != "no") +LDADD+= -lpam ${PAM_STATIC_LDADD} +DPADD+= ${LIBPAM} ${PAM_STATIC_DPADD} +.endif + +.for f in commands telnet terminal utilities +COPTS.${f}.c+= -Wno-pointer-sign +.endfor + +.include diff --git a/usr.bin/telnet/README b/usr.bin/telnet/README new file mode 100644 index 000000000..9f662957d --- /dev/null +++ b/usr.bin/telnet/README @@ -0,0 +1,763 @@ + +This is a distribution of both client and server telnet. These programs +have been compiled on: + telnet telnetd + 4.4 BSD-Lite x x + 4.3 BSD Reno X X + UNICOS 9.1 X X + UNICOS 9.0 X X + UNICOS 8.0 X X + BSDI 2.0 X X + Solaris 2.4 x x (no linemode in server) + SunOs 4.1.4 X X (no linemode in server) + Ultrix 4.3 X X (no linemode in server) + Ultrix 4.1 X X (no linemode in server) + +In addition, previous versions have been compiled on the following +machines, but were not available for testing this version. + telnet telnetd + Next1.0 X X + UNICOS 8.3 X X + UNICOS 7.C X X + UNICOS 7.0 X X + SunOs 4.0.3c X X (no linemode in server) + 4.3 BSD X X (no linemode in server) + DYNIX V3.0.12 X X (no linemode in server) + Ultrix 3.1 X X (no linemode in server) + Ultrix 4.0 X X (no linemode in server) + SunOs 3.5 X X (no linemode in server) + SunOs 4.1.3 X X (no linemode in server) + Solaris 2.2 x x (no linemode in server) + Solaris 2.3 x x (no linemode in server) + BSDI 1.0 X X + BSDI 1.1 X X + DYNIX V3.0.17.9 X X (no linemode in server) + HP-UX 8.0 x x (no linemode in server) + +This code should work, but there are no guarantees. + +Oct 23, 1995 + +This is a bugfix release. + + The change in the previous release from using makeutx() to + pututxline() caused problems on SunOS/Solaris. It has been + changed back to using makeutx(). Symptoms include users + getting error messages when logging in about not being able + to open the tty. + + Using memmove() instead of memcpy() caused problems under + SunOS 4.x, since it doesn't have memmove(). Config.generic + has been modified to include mem.o for SunOS 4.x. + + Some new code was added to telnetd to do some environment + variable cleanup before execing login. Thanks to Sam Hartman + at MIT for pointing this out. + + A couple of other minor bugfixes. + +May 30, 1995 + +This release represents what is on the 4.4BSD-Lite2 release, which +should be the final BSD release. I will continue to support of +telnet, The code (without encryption) is available via anonymous ftp +from ftp.cray.com, in src/telnet/telnet.YY.MM.DD.NE.tar.Z, where +YY.MM.DD is replaced with the year, month and day of the release. +If you can't find it at one of these places, at some point in the +near future information about the latest releases should be available +from ftp.borman.com. + +In addition, the version with the encryption code is available via +ftp from net-dist.mit.edu, in the directory /pub/telnet. There +is a README file there that gives further information on how +to get the distribution. + +Questions, comments, bug reports and bug fixes can be sent to +one of these addresses: + dab@borman.com + dab@cray.com + dab@bsdi.com + +This release is mainly bug fixes and code cleanup. + + Replace all calls to bcopy()/bzero() with calls to + memmove()/memset() and all calls to index()/rindex() + with calls to strchr()/strrchr(). + + Add some missing diagnostics for option tracing + to telnetd. + + Add support for BSDI 2.0 and Solaris 2.4. + + Add support for UNICOS 8.0 + + Get rid of expanded tabs and trailing white spaces. + + From Paul Vixie: + Fix for telnet going into an endless spin + when the session dies abnormally. + + From Jef Poskanzer: + Changes to allow telnet to compile + under SunOS 3.5. + + From Philip Guenther: + makeutx() doesn't expand utmpx, + use pututxline() instead. + + From Chris Torek: + Add a sleep(1) before execing login + to avoid race condition that can eat + up the login prompt. + Use terminal speed directly if it is + not an encoded value. + + From Steve Parker: + Fix to realloc() call. Fix for execing + login on solaris with no user name. + +January 19, 1994 + +This is a list of some of the changes since the last tar release +of telnet/telnetd. There are probably other changes that aren't +listed here, but this should hit a lot of the main ones. + + General: + Changed #define for AUTHENTICATE to AUTHENTICATION + Changed #define for ENCRYPT to ENCRYPTION + Changed #define for DES_ENCRYPT to DES_ENCRYPTION + + Added support for SPX authentication: -DSPX + + Added support for Kerberos Version 5 authentication: -DKRB5 + + Added support for ANSI C function prototypes + + Added support for the NEW-ENVIRON option (RFC-1572) + including support for USERVAR. + + Made support for the old Environment Option (RFC-1408) + conditional on -DOLD_ENVIRON + + Added #define ENV_HACK - support for RFC 1571 + + The encryption code is removed from the public distributions. + Domestic 4.4 BSD distributions contain the encryption code. + + ENV_HACK: Code to deal with systems that only implement + the old ENVIRON option, and have reversed definitions + of ENV_VAR and ENV_VAL. Also fixes ENV processing in + client to handle things besides just the default set... + + NO_BSD_SETJMP: UNICOS configuration for + UNICOS 6.1/6.0/5.1/5.0 systems. + + STREAMSPTY: Use /dev/ptmx to get a clean pty. This + is for SVr4 derivatives (Like Solaris) + + UTMPX: For systems that have /etc/utmpx. This is for + SVr4 derivatives (Like Solaris) + + Definitions for BSDI 1.0 + + Definitions for 4.3 Reno and 4.4 BSD. + + Definitions for UNICOS 8.0 and UNICOS 7.C + + Definitions for Solaris 2.0 + + Definitions for HP-UX 8.0 + + Latest Copyright notices from Berkeley. + + FLOW-CONTROL: support for RFC-XXXx + + + Client Specific: + + Fix the "send" command to not send garbage... + + Fix status message for "skiprc" + + Make sure to send NAWS after telnet has been suspended + or an external command has been run, if the window size + has changed. + + sysV88 support. + + Server Specific: + + Support flowcontrol option in non-linemode servers. + + -k Server supports Kludge Linemode, but will default to + either single character mode or real Linemode support. + The user will have to explicitly ask to switch into + kludge linemode. ("stty extproc", or escape back to + to telnet and say "mode line".) + + -u Specify the length of the hostname field in the utmp + file. Hostname longer than this length will be put + into the utmp file in dotted decimal notation, rather + than putting in a truncated hostname. + + -U Registered hosts only. If a reverse hostname lookup + fails, the connection will be refused. + + -f/-F + Allows forwarding of credentials for KRB5. + +February 22, 1991: + + Features: + + This version of telnet/telnetd has support for both + the AUTHENTICATION and ENCRYPTION options. The + AUTHENTICATION option is fairly well defined, and + an option number has been assigned to it. The + ENCRYPTION option is still in a state of flux; an + option number has been assigned to, but it is still + subject to change. The code is provided in this release + for experimental and testing purposes. + + The telnet "send" command can now be used to send + do/dont/will/wont commands, with any telnet option + name. The rules for when do/dont/will/wont are sent + are still followed, so just because the user requests + that one of these be sent doesn't mean that it will + be sent... + + The telnet "getstatus" command no longer requires + that option printing be enabled to see the response + to the "DO STATUS" command. + + A -n flag has been added to telnetd to disable + keepalives. + + A new telnet command, "auth" has been added (if + AUTHENTICATE is defined). It has four sub-commands, + "status", "disable", "enable" and "help". + + A new telnet command, "encrypt" has been added (if + ENCRYPT is defined). It has many sub-commands: + "enable", "type", "start", "stop", "input", + "-input", "output", "-output", "status", and "help". + + The LOGOUT option is now supported by both telnet + and telnetd, a new command, "logout", was added + to support this. + + Several new toggle options were added: + "autoencrypt", "autodecrypt", "autologin", "authdebug", + "encdebug", "skiprc", "verbose_encrypt" + + An "rlogin" interface has been added. If the program + is named "rlogin", or the "-r" flag is given, then + an rlogin type of interface will be used. + ~. Terminates the session + ~ Suspend the session + ~^] Escape to telnet command mode + ~~ Pass through the ~. + BUG: If you type the rlogin escape character + in the middle of a line while in rlogin + mode, you cannot erase it or any characters + before it. Hopefully this can be fixed + in a future release... + + General changes: + + A "libtelnet.a" has now been created. This library + contains code that is common to both telnet and + telnetd. This is also where library routines that + are needed, but are not in the standard C library, + are placed. + + The makefiles have been re-done. All of the site + specific configuration information has now been put + into a single "Config.generic" file, in the top level + directory. Changing this one file will take care of + all three subdirectories. Also, to add a new/local + definition, a "Config.local" file may be created + at the top level; if that file exists, the subdirectories + will use that file instead of "Config.generic". + + Many 1-2 line functions in commands.c have been + removed, and just inserted in-line, or replaced + with a macro. + + Bug Fixes: + + The non-termio code in both telnet and telnetd was + setting/clearing CTLECH in the sg_flags word. This + was incorrect, and has been changed to set/clear the + LCTLECH bit in the local mode word. + + The SRCRT #define has been removed. If IP_OPTIONS + and IPPROTO_IP are defined on the system, then the + source route code is automatically enabled. + + The NO_GETTYTAB #define has been removed; there + is a compatibility routine that can be built into + libtelnet to achieve the same results. + + The server, telnetd, has been switched to use getopt() + for parsing the argument list. + + The code for getting the input/output speeds via + cfgetispeed()/cfgetospeed() was still not quite + right in telnet. Posix says if the ispeed is 0, + then it is really equal to the ospeed. + + The suboption processing code in telnet now has + explicit checks to make sure that we received + the entire suboption (telnetd was already doing this). + + The telnet code for processing the terminal type + could cause a core dump if an existing connection + was closed, and a new connection opened without + exiting telnet. + + Telnetd was doing a TCSADRAIN when setting the new + terminal settings; This is not good, because it means + that the tcsetattr() will hang waiting for output to + drain, and telnetd is the only one that will drain + the output... The fix is to use TCSANOW which does + not wait. + + Telnetd was improperly setting/clearing the ISTRIP + flag in the c_lflag field, it should be using the + c_iflag field. + + When the child process of telnetd was opening the + slave side of the pty, it was re-setting the EXTPROC + bit too early, and some of the other initialization + code was wiping it out. This would cause telnetd + to go out of linemode and into single character mode. + + One instance of leaving linemode in telnetd forgot + to send a WILL ECHO to the client, the net result + would be that the user would see double character + echo. + + If the MODE was being changed several times very + quickly, telnetd could get out of sync with the + state changes and the returning acks; and wind up + being left in the wrong state. + +September 14, 1990: + + Switch the client to use getopt() for parsing the + argument list. The 4.3Reno getopt.c is included for + systems that don't have getopt(). + + Use the posix _POSIX_VDISABLE value for what value + to use when disabling special characters. If this + is undefined, it defaults to 0x3ff. + + For non-termio systems, TIOCSETP was being used to + change the state of the terminal. This causes the + input queue to be flushed, which we don't want. This + is now changed to TIOCSETN. + + Take out the "#ifdef notdef" around the code in the + server that generates a "sync" when the pty output + is flushed. The potential problem is that some older + telnet clients may go into an infinite loop when they + receive a "sync", if so, the server can be compiled + with "NO_URGENT" defined. + + Fix the client where it was setting/clearing the OPOST + bit in the c_lflag field, not the c_oflag field. + + Fix the client where it was setting/clearing the ISTRIP + bit in the c_lflag field, not the c_iflag field. (On + 4.3Reno, this is the ECHOPRT bit in the c_lflag field.) + The client also had its interpretation of WILL BINARY + and DO BINARY reversed. + + Fix a bug in client that would cause a core dump when + attempting to remove the last environment variable. + + In the client, there were a few places were switch() + was being passed a character, and if it was a negative + value, it could get sign extended, and not match + the 8 bit case statements. The fix is to and the + switch value with 0xff. + + Add a couple more printoption() calls in the client, I + don't think there are any more places were a telnet + command can be received and not printed out when + "options" is on. + + A new flag has been added to the client, "-a". Currently, + this just causes the USER name to be sent across, in + the future this may be used to signify that automatic + authentication is requested. + + The USER variable is now only sent by the client if + the "-a" or "-l user" options are explicitly used, or + if the user explicitly asks for the "USER" environment + variable to be exported. In the server, if it receives + the "USER" environment variable, it won't print out the + banner message, so that only "Password:" will be printed. + This makes the semantics more like rlogin, and should be + more familiar to the user. (People are not used to + getting a banner message, and then getting just a + "Password:" prompt.) + + Re-vamp the code for starting up the child login + process. The code was getting ugly, and it was + hard to tell what was really going on. What we + do now is after the fork(), in the child: + 1) make sure we have no controlling tty + 2) open and initialize the tty + 3) do a setsid()/setpgrp() + 4) makes the tty our controlling tty. + On some systems, #2 makes the tty our controlling + tty, and #4 is a no-op. The parent process does + a gets rid of any controlling tty after the child + is fork()ed. + + Use the strdup() library routine in telnet, instead + of the local savestr() routine. If you don't have + strdup(), you need to define NO_STRDUP. + + Add support for ^T (SIGINFO/VSTATUS), found in the + 4.3Reno distribution. This maps to the AYT character. + You need a 4-line bugfix in the kernel to get this + to work properly: + + > *** tty_pty.c.ORG Tue Sep 11 09:41:53 1990 + > --- tty_pty.c Tue Sep 11 17:48:03 1990 + > *************** + > *** 609,613 **** + > if ((tp->t_lflag&NOFLSH) == 0) + > ttyflush(tp, FREAD|FWRITE); + > ! pgsignal(tp->t_pgrp, *(unsigned int *)data); + > return(0); + > } + > --- 609,616 ---- + > if ((tp->t_lflag&NOFLSH) == 0) + > ttyflush(tp, FREAD|FWRITE); + > ! pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); + > ! if ((*(unsigned int *)data == SIGINFO) && + > ! ((tp->t_lflag&NOKERNINFO) == 0)) + > ! ttyinfo(tp); + > return(0); + > } + + The client is now smarter when setting the telnet escape + character; it only sets it to one of VEOL and VEOL2 if + one of them is undefined, and the other one is not already + defined to the telnet escape character. + + Handle TERMIOS systems that have separate input and output + line speed settings embedded in the flags. + + Many other minor bug fixes. + +June 20, 1990: + Re-organize makefiles and source tree. The telnet/Source + directory is now gone, and all the source that was in + telnet/Source is now just in the telnet directory. + + Separate makefile for each system are now gone. There + are two makefiles, Makefile and Makefile.generic. + The "Makefile" has the definitions for the various + system, and "Makefile.generic" does all the work. + There is a variable called "WHAT" that is used to + specify what to make. For example, in the telnet + directory, you might say: + make 4.4bsd WHAT=clean + to clean out the directory. + + Add support for the ENVIRON and XDISPLOC options. + In order for the server to work, login has to have + the "-p" option to preserve environment variables. + + Add the SOFT_TAB and LIT_ECHO modes in the LINEMODE support. + + Add the "-l user" option to command line and open command + (This is passed through the ENVIRON option). + + Add the "-e" command line option, for setting the escape + character. + + Add the "-D", diagnostic, option to the server. This allows + the server to print out debug information, which is very + useful when trying to debug a telnet that doesn't have any + debugging ability. + + Turn off the literal next character when not in LINEMODE. + + Don't recognize ^Y locally, just pass it through. + + Make minor modifications for Sun4.0 and Sun4.1 + + Add support for both FORW1 and FORW2 characters. The + telnet escape character is set to whichever of the + two is not being used. If both are in use, the escape + character is not set, so when in linemode the user will + have to follow the escape character with a or + +libtelnet/Makefile.4.4: +telnet/Makefile.4.4: +telnetd/Makefile.4.4: + These are the makefiles that can be used on a 4.3Reno + system when this software is installed in /usr/src/lib/libtelnet, + /usr/src/libexec/telnetd, and /usr/src/usr.bin/telnet. + + +The following TELNET options are supported: + + LINEMODE: + The LINEMODE option is supported as per RFC1116. The + FORWARDMASK option is not currently supported. + + BINARY: The client has the ability to turn on/off the BINARY + option in each direction. Turning on BINARY from + server to client causes the LITOUT bit to get set in + the terminal driver on both ends, turning on BINARY + from the client to the server causes the PASS8 bit + to get set in the terminal driver on both ends. + + TERMINAL-TYPE: + This is supported as per RFC1091. On the server side, + when a terminal type is received, termcap/terminfo + is consulted to determine if it is a known terminal + type. It keeps requesting terminal types until it + gets one that it recognizes, or hits the end of the + list. The server side looks up the entry in the + termcap/terminfo data base, and generates a list of + names which it then passes one at a time to each + request for a terminal type, duplicating the last + entry in the list before cycling back to the beginning. + + NAWS: The Negotiate about Window Size, as per RFC 1073. + + TERMINAL-SPEED: + Implemented as per RFC 1079 + + TOGGLE-FLOW-CONTROL: + Implemented as per RFC 1080 + + TIMING-MARK: + As per RFC 860 + + SGA: As per RFC 858 + + ECHO: As per RFC 857 + + LOGOUT: As per RFC 727 + + STATUS: + The server will send its current status upon + request. It does not ask for the clients status. + The client will request the servers current status + from the "send getstatus" command. + + ENVIRON: + This option is currently being defined by the IETF + Telnet Working Group, and an RFC has not yet been + issued, but should be in the near future... + + X-DISPLAY-LOCATION: + This functionality can be done through the ENVIRON + option, it is added here for completeness. + + AUTHENTICATION: + This option is currently being defined by the IETF + Telnet Working Group, and an RFC has not yet been + issued. The basic framework is pretty much decided, + but the definitions for the specific authentication + schemes is still in a state of flux. + + ENCRYPTION: + This option is currently being defined by the IETF + Telnet Working Group, and an RFC has not yet been + issued. The draft RFC is still in a state of flux, + so this code may change in the future. diff --git a/usr.bin/telnet/authenc.c b/usr.bin/telnet/authenc.c new file mode 100644 index 000000000..8e7574d87 --- /dev/null +++ b/usr.bin/telnet/authenc.c @@ -0,0 +1,107 @@ +/* $NetBSD: authenc.c,v 1.13 2012/01/09 16:08:55 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: authenc.c,v 1.13 2012/01/09 16:08:55 christos Exp $"); +#endif +#endif /* not lint */ + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) +#include +#include +#include +#include +#include + +#include "general.h" +#include "ring.h" +#include "externs.h" +#include "defines.h" +#include "types.h" + +int +telnet_net_write(unsigned char *str, int len) +{ + if (NETROOM() > len) { + ring_supply_data(&netoring, str, len); + if (str[0] == IAC && str[1] == SE) + printsub('>', &str[2], len-2); + return(len); + } + return(0); +} + +void +net_encrypt(void) +{ +#ifdef ENCRYPTION + if (encrypt_output) + ring_encrypt(&netoring, encrypt_output); + else + ring_clearto(&netoring); +#endif /* ENCRYPTION */ +} + +int +telnet_spin(void) +{ + return(-1); +} + +char * +telnet_getenv(char *val) +{ + return((char *)env_getvalue((unsigned char *)val)); +} + +char * +telnet_gets(char *prmpt, char *result, int length, int echo) +{ + extern int globalmode; + int om = globalmode; + char *res; + + TerminalNewMode(-1); + if (echo) { + printf("%s", prmpt); + res = fgets(result, length, stdin); + } else if ((res = getpass(prmpt)) != NULL) { + strlcpy(result, res, length); + res = result; + } + TerminalNewMode(om); + return(res); +} +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c new file mode 100644 index 000000000..b244b75e8 --- /dev/null +++ b/usr.bin/telnet/commands.c @@ -0,0 +1,2907 @@ +/* $NetBSD: commands.c,v 1.68 2012/01/09 16:08:55 christos Exp $ */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)commands.c 8.4 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: commands.c,v 1.68 2012/01/09 16:08:55 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "general.h" +#include "ring.h" +#include "externs.h" +#include "defines.h" +#include "types.h" +#include +#ifdef AUTHENTICATION +#include +#endif +#ifdef ENCRYPTION +#include +#endif + +#include +#include + + +#if defined(IPPROTO_IP) && defined(IP_TOS) +int tos = -1; +#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ + +char *hostname; +static char _hostname[MAXHOSTNAMELEN]; + +typedef struct { + const char *name; /* command name */ + const char *help; /* help string (NULL for no help) */ + int (*handler) /* routine which executes command */ + (int, char *[]); + int needconnect; /* Do we need to be connected to execute? */ +} Command; + +static char line[256]; +static char saveline[256]; +static int margc; +static char *margv[20]; + +static void makeargv(void); +static int special(char *); +static const char *control(cc_t); +static int sendcmd(int, char **); +static int send_esc(char *); +static int send_docmd(char *); +static int send_dontcmd(char *); +static int send_willcmd(char *); +static int send_wontcmd(char *); +static int send_help(char *); +static int lclchars(int); +static int togdebug(int); +static int togcrlf(int); +static int togbinary(int); +static int togrbinary(int); +static int togxbinary(int); +static int togglehelp(int); +static void settogglehelp(int); +static int toggle(int, char *[]); +static struct setlist *getset(char *); +static int setcmd(int, char *[]); +static int unsetcmd(int, char *[]); +static int dokludgemode(int); +static int dolinemode(int); +static int docharmode(int); +static int dolmmode(int, int ); +static int modecmd(int, char *[]); +static int display(int, char *[]); +static int setescape(int, char *[]); +static int togcrmod(int, char *[]); +static int bye(int, char *[]); +static void slc_help(int); +static struct slclist *getslc(char *); +static int slccmd(int, char *[]); +static struct env_lst *env_help(const unsigned char *, unsigned char *); +static struct envlist *getenvcmd(char *); +#ifdef AUTHENTICATION +static int auth_help(char *); +#endif +#ifdef TN3270 +static void filestuff(int); +#endif +static int status(int, char *[]); +static const char *sockaddr_ntop (struct sockaddr *); +typedef int (*intrtn_t)(int, char **); +static int call(intrtn_t, ...); +static Command *getcmd(char *); +static int help(int, char *[]); + +static void +makeargv(void) +{ + char *cp, *cp2, c; + char **argp = margv; + static char bang[] = "!"; + + margc = 0; + cp = line; + if (*cp == '!') { /* Special case shell escape */ + strlcpy(saveline, line, sizeof(saveline)); /* save for shell command */ + *argp++ = bang; /* No room in string to get this */ + margc++; + cp++; + } + while ((c = *cp) != '\0') { + int inquote = 0; + while (isspace((unsigned char)c)) + c = *++cp; + if (c == '\0') + break; + *argp++ = cp; + margc += 1; + for (cp2 = cp; c != '\0'; c = *++cp) { + if (inquote) { + if (c == inquote) { + inquote = 0; + continue; + } + } else { + if (c == '\\') { + if ((c = *++cp) == '\0') + break; + } else if (c == '"') { + inquote = '"'; + continue; + } else if (c == '\'') { + inquote = '\''; + continue; + } else if (isspace((unsigned char)c)) + break; + } + *cp2++ = c; + } + *cp2 = '\0'; + if (c == '\0') + break; + cp++; + } + *argp++ = 0; +} + +/* + * Make a character string into a number. + * + * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). + */ + +static int +special(char *s) +{ + char c; + char b; + + switch (*s) { + case '^': + b = *++s; + if (b == '?') { + c = b | 0x40; /* DEL */ + } else { + c = b & 0x1f; + } + break; + default: + c = *s; + break; + } + return c; +} + +/* + * Construct a control character sequence + * for a special character. + */ +static const char * +control(cc_t c) +{ + static char buf[5]; + /* + * The only way I could get the Sun 3.5 compiler + * to shut up about + * if ((unsigned int)c >= 0x80) + * was to assign "c" to an unsigned int variable... + * Arggg.... + */ + unsigned int uic = (unsigned int)c; + + if (uic == 0x7f) + return ("^?"); + if (c == (cc_t)_POSIX_VDISABLE) { + return "off"; + } + if (uic >= 0x80) { + buf[0] = '\\'; + buf[1] = ((c>>6)&07) + '0'; + buf[2] = ((c>>3)&07) + '0'; + buf[3] = (c&07) + '0'; + buf[4] = 0; + } else if (uic >= 0x20) { + buf[0] = c; + buf[1] = 0; + } else { + buf[0] = '^'; + buf[1] = '@'+c; + buf[2] = 0; + } + return (buf); +} + + + +/* + * The following are data structures and routines for + * the "send" command. + * + */ + +struct sendlist { + const char *name; /* How user refers to it (case independent) */ + const char *help; /* Help information (0 ==> no help) */ + int needconnect; /* Need to be connected */ + int narg; /* Number of arguments */ + int (*handler) /* Routine to perform (for special ops) */ + (char *); + int nbyte; /* Number of bytes to send this command */ + int what; /* Character to be sent (<0 ==> special) */ +}; + + +static struct sendlist Sendlist[] = { + { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, + { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, + { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, + { "break", 0, 1, 0, 0, 2, BREAK }, + { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, + { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, + { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, + { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, + { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, + { "intp", 0, 1, 0, 0, 2, IP }, + { "interrupt", 0, 1, 0, 0, 2, IP }, + { "intr", 0, 1, 0, 0, 2, IP }, + { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, + { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, + { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, + { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, + { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, + { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, + { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, + { "?", "Display send options", 0, 0, send_help, 0, 0 }, + { "help", 0, 0, 0, send_help, 0, 0 }, + { "do", 0, 0, 1, send_docmd, 3, 0 }, + { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, + { "will", 0, 0, 1, send_willcmd, 3, 0 }, + { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, + { .name = 0 } +}; + +#define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ + sizeof(struct sendlist))) + +static int +sendcmd(int argc, char **argv) +{ + int count; /* how many bytes we are going to need to send */ + int i; + struct sendlist *s; /* pointer to current command */ + int success = 0; + int needconnect = 0; + + if (argc < 2) { + printf("need at least one argument for 'send' command\n"); + printf("'send ?' for help\n"); + return 0; + } + /* + * First, validate all the send arguments. + * In addition, we see how much space we are going to need, and + * whether or not we will be doing a "SYNCH" operation (which + * flushes the network queue). + */ + count = 0; + for (i = 1; i < argc; i++) { + s = GETSEND(argv[i]); + if (s == 0) { + printf("Unknown send argument '%s'\n'send ?' for help.\n", + argv[i]); + return 0; + } else if (Ambiguous(s)) { + printf("Ambiguous send argument '%s'\n'send ?' for help.\n", + argv[i]); + return 0; + } + if (i + s->narg >= argc) { + fprintf(stderr, + "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n", + s->narg, s->narg == 1 ? "" : "s", s->name, s->name); + return 0; + } + count += s->nbyte; + if (s->handler == send_help) { + send_help(NULL); + return 0; + } + + i += s->narg; + needconnect += s->needconnect; + } + if (!connected && needconnect) { + printf("?Need to be connected first.\n"); + printf("'send ?' for help\n"); + return 0; + } + /* Now, do we have enough room? */ + if (NETROOM() < count) { + printf("There is not enough room in the buffer TO the network\n"); + printf("to process your request. Nothing will be done.\n"); + printf("('send synch' will throw away most data in the network\n"); + printf("buffer, if this might help.)\n"); + return 0; + } + /* OK, they are all OK, now go through again and actually send */ + count = 0; + for (i = 1; i < argc; i++) { + if ((s = GETSEND(argv[i])) == 0) { + fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); + (void) quit(0, NULL); + /*NOTREACHED*/ + } + if (s->handler) { + count++; + success += (*s->handler)(argv[i+1]); + i += s->narg; + } else { + NET2ADD(IAC, s->what); + printoption("SENT", IAC, s->what); + } + } + return (count == success); +} + +static int +send_esc(char *s) +{ + NETADD(escape); + return 1; +} + +static int +send_docmd(char *name) +{ + return(send_tncmd(send_do, "do", name)); +} + +static int +send_dontcmd(char *name) +{ + return(send_tncmd(send_dont, "dont", name)); +} +static int +send_willcmd(char *name) +{ + return(send_tncmd(send_will, "will", name)); +} +static int +send_wontcmd(char *name) +{ + return(send_tncmd(send_wont, "wont", name)); +} + +int +send_tncmd(void (*func)(int, int), const char *cmd, char *name) +{ + const char **cpp; + int val = 0; + + if (isprefix(name, "?")) { + int col, len; + + printf("usage: send %s \n", cmd); + printf("\"value\" must be from 0 to 255\n"); + printf("Valid options are:\n\t"); + + col = 8; + for (cpp = telopts; *cpp; cpp++) { + len = strlen(*cpp) + 3; + if (col + len > 65) { + printf("\n\t"); + col = 8; + } + printf(" \"%s\"", *cpp); + col += len; + } + printf("\n"); + return 0; + } + cpp = (void *)genget(name, __UNCONST(telopts), sizeof(char *)); + if (Ambiguous(cpp)) { + fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n", + name, cmd); + return 0; + } + if (cpp) { + val = cpp - telopts; + } else { + char *cp = name; + + while (*cp >= '0' && *cp <= '9') { + val *= 10; + val += *cp - '0'; + cp++; + } + if (*cp != 0) { + fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n", + name, cmd); + return 0; + } else if (val < 0 || val > 255) { + fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n", + name, cmd); + return 0; + } + } + if (!connected) { + printf("?Need to be connected first.\n"); + return 0; + } + (*func)(val, 1); + return 1; +} + +static int +send_help(char *n) +{ + struct sendlist *s; /* pointer to current command */ + for (s = Sendlist; s->name; s++) { + if (s->help) + printf("%-15s %s\n", s->name, s->help); + } + return(0); +} + +/* + * The following are the routines and data structures referred + * to by the arguments to the "toggle" command. + */ + +static int +lclchars(int n) +{ + donelclchars = 1; + return 1; +} + +static int +togdebug(int n) +{ + if (net > 0 && + (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, telnet_debug)) < 0) { + perror("setsockopt (SO_DEBUG)"); + } + return 1; +} + +static int +togcrlf(int n) +{ + if (crlf) { + printf("Will send carriage returns as telnet .\n"); + } else { + printf("Will send carriage returns as telnet .\n"); + } + return 1; +} + +int binmode; + +static int +togbinary(int val) +{ + donebinarytoggle = 1; + + if (val >= 0) { + binmode = val; + } else { + if (my_want_state_is_will(TELOPT_BINARY) && + my_want_state_is_do(TELOPT_BINARY)) { + binmode = 1; + } else if (my_want_state_is_wont(TELOPT_BINARY) && + my_want_state_is_dont(TELOPT_BINARY)) { + binmode = 0; + } + val = binmode ? 0 : 1; + } + + if (val == 1) { + if (my_want_state_is_will(TELOPT_BINARY) && + my_want_state_is_do(TELOPT_BINARY)) { + printf("Already operating in binary mode with remote host.\n"); + } else { + printf("Negotiating binary mode with remote host.\n"); + tel_enter_binary(3); + } + } else { + if (my_want_state_is_wont(TELOPT_BINARY) && + my_want_state_is_dont(TELOPT_BINARY)) { + printf("Already in network ascii mode with remote host.\n"); + } else { + printf("Negotiating network ascii mode with remote host.\n"); + tel_leave_binary(3); + } + } + return 1; +} + +static int +togrbinary(int val) +{ + donebinarytoggle = 1; + + if (val == -1) + val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; + + if (val == 1) { + if (my_want_state_is_do(TELOPT_BINARY)) { + printf("Already receiving in binary mode.\n"); + } else { + printf("Negotiating binary mode on input.\n"); + tel_enter_binary(1); + } + } else { + if (my_want_state_is_dont(TELOPT_BINARY)) { + printf("Already receiving in network ascii mode.\n"); + } else { + printf("Negotiating network ascii mode on input.\n"); + tel_leave_binary(1); + } + } + return 1; +} + +static int +togxbinary(int val) +{ + donebinarytoggle = 1; + + if (val == -1) + val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; + + if (val == 1) { + if (my_want_state_is_will(TELOPT_BINARY)) { + printf("Already transmitting in binary mode.\n"); + } else { + printf("Negotiating binary mode on output.\n"); + tel_enter_binary(2); + } + } else { + if (my_want_state_is_wont(TELOPT_BINARY)) { + printf("Already transmitting in network ascii mode.\n"); + } else { + printf("Negotiating network ascii mode on output.\n"); + tel_leave_binary(2); + } + } + return 1; +} + +#ifdef ENCRYPTION +extern int EncryptAutoEnc(int); +extern int EncryptAutoDec(int); +extern int EncryptDebug(int); +extern int EncryptVerbose(int); +#endif /* ENCRYPTION */ + +struct togglelist { + const char *name; /* name of toggle */ + const char *help; /* help message */ + int (*handler) /* routine to do actual setting */ + (int); + int *variable; + const char *actionexplanation; +}; + +static struct togglelist Togglelist[] = { + { "autoflush", + "flushing of output when sending interrupt characters", + 0, + &autoflush, + "flush output when sending interrupt characters" }, + { "autosynch", + "automatic sending of interrupt characters in urgent mode", + 0, + &autosynch, + "send interrupt characters in urgent mode" }, +#ifdef AUTHENTICATION + { "autologin", + "automatic sending of login and/or authentication info", + 0, + &autologin, + "send login name and/or authentication information" }, + { "authdebug", + "Toggle authentication debugging", + auth_togdebug, + 0, + "print authentication debugging information" }, +#endif +#ifdef ENCRYPTION + { "autoencrypt", + "automatic encryption of data stream", + EncryptAutoEnc, + 0, + "automatically encrypt output" }, + { "autodecrypt", + "automatic decryption of data stream", + EncryptAutoDec, + 0, + "automatically decrypt input" }, + { "verbose_encrypt", + "Toggle verbose encryption output", + EncryptVerbose, + 0, + "print verbose encryption output" }, + { "encdebug", + "Toggle encryption debugging", + EncryptDebug, + 0, + "print encryption debugging information" }, +#endif /* ENCRYPTION */ + { "skiprc", + "don't read ~/.telnetrc file", + 0, + &skiprc, + "skip reading of ~/.telnetrc file" }, + { "binary", + "sending and receiving of binary data", + togbinary, + 0, + 0 }, + { "inbinary", + "receiving of binary data", + togrbinary, + 0, + 0 }, + { "outbinary", + "sending of binary data", + togxbinary, + 0, + 0 }, + { "crlf", + "sending carriage returns as telnet ", + togcrlf, + &crlf, + 0 }, + { "crmod", + "mapping of received carriage returns", + 0, + &crmod, + "map carriage return on output" }, + { "localchars", + "local recognition of certain control characters", + lclchars, + &localchars, + "recognize certain control characters" }, + { " ", "", 0, NULL, NULL }, /* empty line */ +#ifdef TN3270 + { "apitrace", + "(debugging) toggle tracing of API transactions", + 0, + &apitrace, + "trace API transactions" }, + { "cursesdata", + "(debugging) toggle printing of hexadecimal curses data", + 0, + &cursesdata, + "print hexadecimal representation of curses data" }, +#endif /* defined(TN3270) */ + { "debug", + "debugging", + togdebug, + &telnet_debug, + "turn on socket level debugging" }, + { "netdata", + "printing of hexadecimal network data (debugging)", + 0, + &netdata, + "print hexadecimal representation of network traffic" }, + { "prettydump", + "output of \"netdata\" to user readable format (debugging)", + 0, + &prettydump, + "print user readable output for \"netdata\"" }, + { "options", + "viewing of options processing (debugging)", + 0, + &showoptions, + "show option processing" }, + { "termdata", + "(debugging) toggle printing of hexadecimal terminal data", + 0, + &termdata, + "print hexadecimal representation of terminal traffic" }, + { "?", + 0, + togglehelp, NULL, NULL }, + { "help", + 0, + togglehelp, NULL, NULL }, + { .name = 0 } +}; + +static int +togglehelp(int n) +{ + struct togglelist *c; + + for (c = Togglelist; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s toggle %s\n", c->name, c->help); + else + printf("\n"); + } + } + printf("\n"); + printf("%-15s %s\n", "?", "display help information"); + return 0; +} + +static void +settogglehelp(int set) +{ + struct togglelist *c; + + for (c = Togglelist; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s %s %s\n", c->name, set ? "enable" : "disable", + c->help); + else + printf("\n"); + } + } +} + +#define GETTOGGLE(name) (struct togglelist *) \ + genget(name, (char **) Togglelist, sizeof(struct togglelist)) + +static int +toggle(int argc, char *argv[]) +{ + int retval = 1; + char *name; + struct togglelist *c; + + if (argc < 2) { + fprintf(stderr, + "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); + return 0; + } + argc--; + argv++; + while (argc--) { + name = *argv++; + c = GETTOGGLE(name); + if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", + name); + return 0; + } else if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", + name); + return 0; + } else { + if (c->variable) { + *c->variable = !*c->variable; /* invert it */ + if (c->actionexplanation) { + printf("%s %s.\n", *c->variable? "Will" : "Won't", + c->actionexplanation); + } + } + if (c->handler) { + retval &= (*c->handler)(-1); + } + } + } + return retval; +} + +/* + * The following perform the "set" command. + */ + +struct termios new_tc = { .c_iflag = 0 }; + +struct setlist { + const char *name; /* name */ + const char *help; /* help information */ + void (*handler)(char *); + cc_t *charp; /* where it is located at */ +}; + +static struct setlist Setlist[] = { +#ifdef KLUDGELINEMODE + { "echo", "character to toggle local echoing on/off", 0, &echoc }, +#endif + { "escape", "character to escape back to telnet command mode", 0, &escape }, + { "rlogin", "rlogin escape character", 0, &rlogin }, + { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, + { " ", "", NULL, NULL }, + { " ", "The following need 'localchars' to be toggled true", 0, 0 }, + { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp }, + { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp }, + { "quit", "character to cause an Abort process", 0, termQuitCharp }, + { "eof", "character to cause an EOF ", 0, termEofCharp }, + { " ", "", NULL, NULL }, + { " ", "The following are for local editing in linemode", 0, 0 }, + { "erase", "character to use to erase a character", 0, termEraseCharp }, + { "kill", "character to use to erase a line", 0, termKillCharp }, + { "lnext", "character to use for literal next", 0, termLiteralNextCharp }, + { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, + { "reprint", "character to use for line reprint", 0, termRprntCharp }, + { "worderase", "character to use to erase a word", 0, termWerasCharp }, + { "start", "character to use for XON", 0, termStartCharp }, + { "stop", "character to use for XOFF", 0, termStopCharp }, + { "forw1", "alternate end of line character", 0, termForw1Charp }, + { "forw2", "alternate end of line character", 0, termForw2Charp }, + { "ayt", "alternate AYT character", 0, termAytCharp }, + { .name = 0 } +}; + +static struct setlist * +getset(char *name) +{ + return (struct setlist *) + genget(name, (char **) Setlist, sizeof(struct setlist)); +} + +void +set_escape_char(char *s) +{ + if (rlogin != _POSIX_VDISABLE) { + rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; + printf("Telnet rlogin escape character is '%s'.\n", + control(rlogin)); + } else { + escape = (s && *s) ? special(s) : _POSIX_VDISABLE; + printf("Telnet escape character is '%s'.\n", control(escape)); + } +} + +static int +setcmd(int argc, char *argv[]) +{ + int value; + struct setlist *ct; + struct togglelist *c; + + if (argc < 2 || argc > 3) { + printf("Format is 'set Name Value'\n'set ?' for help.\n"); + return 0; + } + if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { + for (ct = Setlist; ct->name; ct++) + printf("%-15s %s\n", ct->name, ct->help); + printf("\n"); + settogglehelp(1); + printf("%-15s %s\n", "?", "display help information"); + return 0; + } + + ct = getset(argv[1]); + if (ct == 0) { + c = GETTOGGLE(argv[1]); + if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", + argv[1]); + return 0; + } else if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", + argv[1]); + return 0; + } + if (c->variable) { + if ((argc == 2) || (strcmp("on", argv[2]) == 0)) + *c->variable = 1; + else if (strcmp("off", argv[2]) == 0) + *c->variable = 0; + else { + printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n"); + return 0; + } + if (c->actionexplanation) { + printf("%s %s.\n", *c->variable? "Will" : "Won't", + c->actionexplanation); + } + } + if (c->handler) + (*c->handler)(1); + } else if (argc != 3) { + printf("Format is 'set Name Value'\n'set ?' for help.\n"); + return 0; + } else if (Ambiguous(ct)) { + fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", + argv[1]); + return 0; + } else if (ct->handler) { + (*ct->handler)(argv[2]); + printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp); + } else { + if (strcmp("off", argv[2])) { + value = special(argv[2]); + } else { + value = _POSIX_VDISABLE; + } + *(ct->charp) = (cc_t)value; + printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); + } + slc_check(); + return 1; +} + +static int +unsetcmd(int argc, char *argv[]) +{ + struct setlist *ct; + struct togglelist *c; + char *name; + + if (argc < 2) { + fprintf(stderr, + "Need an argument to 'unset' command. 'unset ?' for help.\n"); + return 0; + } + if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { + for (ct = Setlist; ct->name; ct++) + printf("%-15s %s\n", ct->name, ct->help); + printf("\n"); + settogglehelp(0); + printf("%-15s %s\n", "?", "display help information"); + return 0; + } + + argc--; + argv++; + while (argc--) { + name = *argv++; + ct = getset(name); + if (ct == 0) { + c = GETTOGGLE(name); + if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n", + name); + return 0; + } else if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", + name); + return 0; + } + if (c->variable) { + *c->variable = 0; + if (c->actionexplanation) { + printf("%s %s.\n", *c->variable? "Will" : "Won't", + c->actionexplanation); + } + } + if (c->handler) + (*c->handler)(0); + } else if (Ambiguous(ct)) { + fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", + name); + return 0; + } else if (ct->handler) { + (*ct->handler)(0); + printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp); + } else { + *(ct->charp) = _POSIX_VDISABLE; + printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); + } + } + return 1; +} + +/* + * The following are the data structures and routines for the + * 'mode' command. + */ +#ifdef KLUDGELINEMODE +extern int kludgelinemode; + +static int +dokludgemode(int n) +{ + kludgelinemode = 1; + send_wont(TELOPT_LINEMODE, 1); + send_dont(TELOPT_SGA, 1); + send_dont(TELOPT_ECHO, 1); + return 1; +} +#endif + +static int +dolinemode(int n) +{ +#ifdef KLUDGELINEMODE + if (kludgelinemode) + send_dont(TELOPT_SGA, 1); +#endif + send_will(TELOPT_LINEMODE, 1); + send_dont(TELOPT_ECHO, 1); + return 1; +} + +static int +docharmode(int n) +{ +#ifdef KLUDGELINEMODE + if (kludgelinemode) + send_do(TELOPT_SGA, 1); + else +#endif + send_wont(TELOPT_LINEMODE, 1); + send_do(TELOPT_ECHO, 1); + return 1; +} + +static int +dolmmode(int bit, int on) +{ + unsigned char c; + extern int linemode; + + if (my_want_state_is_wont(TELOPT_LINEMODE)) { + printf("?Need to have LINEMODE option enabled first.\n"); + printf("'mode ?' for help.\n"); + return 0; + } + + if (on) + c = (linemode | bit); + else + c = (linemode & ~bit); + lm_mode(&c, 1, 1); + return 1; +} + +int +set_mode(int bit) +{ + return dolmmode(bit, 1); +} + +int +clear_mode(int bit) +{ + return dolmmode(bit, 0); +} + +struct modelist { + const char *name; /* command name */ + const char *help; /* help string */ + int (*handler) /* routine which executes command */ + (int); + int needconnect; /* Do we need to be connected to execute? */ + int arg1; +}; + +static struct modelist ModeList[] = { + { "character", "Disable LINEMODE option", docharmode, 1, 0 }, +#ifdef KLUDGELINEMODE + { "", "(or disable obsolete line-by-line mode)", 0, 0, 0 }, +#endif + { "line", "Enable LINEMODE option", dolinemode, 1, 0 }, +#ifdef KLUDGELINEMODE + { "", "(or enable obsolete line-by-line mode)", 0, 0, 0 }, +#endif + { "", "", 0, 0, 0 }, + { "", "These require the LINEMODE option to be enabled", 0, 0, 0 }, + { "isig", "Enable signal trapping", set_mode, 1, MODE_TRAPSIG }, + { "+isig", 0, set_mode, 1, MODE_TRAPSIG }, + { "-isig", "Disable signal trapping", clear_mode, 1, MODE_TRAPSIG }, + { "edit", "Enable character editing", set_mode, 1, MODE_EDIT }, + { "+edit", 0, set_mode, 1, MODE_EDIT }, + { "-edit", "Disable character editing", clear_mode, 1, MODE_EDIT }, + { "softtabs", "Enable tab expansion", set_mode, 1, MODE_SOFT_TAB }, + { "+softtabs", 0, set_mode, 1, MODE_SOFT_TAB }, + { "-softtabs", "Disable character editing", clear_mode, 1, MODE_SOFT_TAB }, + { "litecho", "Enable literal character echo", set_mode, 1, MODE_LIT_ECHO }, + { "+litecho", 0, set_mode, 1, MODE_LIT_ECHO }, + { "-litecho", "Disable literal character echo", clear_mode, 1, MODE_LIT_ECHO }, + { "help", 0, modehelp, 0, 0 }, +#ifdef KLUDGELINEMODE + { "kludgeline", 0, dokludgemode, 1, 0 }, +#endif + { "", "", 0, 0, 0 }, + { "?", "Print help information", modehelp, 0, 0 }, + { .name = 0 }, +}; + + +int +modehelp(int n) +{ + struct modelist *mt; + + printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); + for (mt = ModeList; mt->name; mt++) { + if (mt->help) { + if (*mt->help) + printf("%-15s %s\n", mt->name, mt->help); + else + printf("\n"); + } + } + return 0; +} + +#define GETMODECMD(name) (struct modelist *) \ + genget(name, (char **) ModeList, sizeof(struct modelist)) + +static int +modecmd(int argc, char *argv[]) +{ + struct modelist *mt; + + if (argc != 2) { + printf("'mode' command requires an argument\n"); + printf("'mode ?' for help.\n"); + } else if ((mt = GETMODECMD(argv[1])) == 0) { + fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); + } else if (Ambiguous(mt)) { + fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); + } else if (mt->needconnect && !connected) { + printf("?Need to be connected first.\n"); + printf("'mode ?' for help.\n"); + } else if (mt->handler) { + return (*mt->handler)(mt->arg1); + } + return 0; +} + +/* + * The following data structures and routines implement the + * "display" command. + */ + +static int +display(int argc, char *argv[]) +{ + struct togglelist *tl; + struct setlist *sl; + +#define dotog(tl) if (tl->variable && tl->actionexplanation) { \ + if (*tl->variable) { \ + printf("will"); \ + } else { \ + printf("won't"); \ + } \ + printf(" %s.\n", tl->actionexplanation); \ + } + +#define doset(sl) if (sl->name && *sl->name != ' ') { \ + if (sl->handler == 0) \ + printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \ + else \ + printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \ + } + + if (argc == 1) { + for (tl = Togglelist; tl->name; tl++) { + dotog(tl); + } + printf("\n"); + for (sl = Setlist; sl->name; sl++) { + doset(sl); + } + } else { + int i; + + for (i = 1; i < argc; i++) { + sl = getset(argv[i]); + tl = GETTOGGLE(argv[i]); + if (Ambiguous(sl) || Ambiguous(tl)) { + printf("?Ambiguous argument '%s'.\n", argv[i]); + return 0; + } else if (!sl && !tl) { + printf("?Unknown argument '%s'.\n", argv[i]); + return 0; + } else { + if (tl) { + dotog(tl); + } + if (sl) { + doset(sl); + } + } + } + } +/*@*/optionstatus(); +#ifdef ENCRYPTION + EncryptStatus(); +#endif /* ENCRYPTION */ + return 1; +#undef doset +#undef dotog +} + +/* + * The following are the data structures, and many of the routines, + * relating to command processing. + */ + +/* + * Set the escape character. + */ +static int +setescape(int argc, char *argv[]) +{ + char *arg; + char buf[50]; + + printf( + "Deprecated usage - please use 'set escape%s%s' in the future.\n", + (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); + if (argc > 2) + arg = argv[1]; + else { + printf("new escape character: "); + (void) fgets(buf, sizeof(buf), stdin); + arg = buf; + } + if (arg[0] != '\0') + escape = arg[0]; + if (!In3270) { + printf("Escape character is '%s'.\n", control(escape)); + } + (void) fflush(stdout); + return 1; +} + +/*VARARGS*/ +static int +togcrmod(int argc, char *argv[]) +{ + crmod = !crmod; + printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); + printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); + (void) fflush(stdout); + return 1; +} + +/*VARARGS*/ +int +suspend(int argc, char *argv[]) +{ + setcommandmode(); + { + long oldrows, oldcols, newrows, newcols, err; + + err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; + (void) kill(0, SIGTSTP); + /* + * If we didn't get the window size before the SUSPEND, but we + * can get them now (?), then send the NAWS to make sure that + * we are set up for the right window size. + */ + if (TerminalWindowSize(&newrows, &newcols) && connected && + (err || ((oldrows != newrows) || (oldcols != newcols)))) { + sendnaws(); + } + } + /* reget parameters in case they were changed */ + TerminalSaveState(); + setconnmode(0); + return 1; +} + +#ifndef TN3270 +/*ARGSUSED*/ +int +shell(int argc, char *argv[]) +{ + long oldrows, oldcols, newrows, newcols; + long volatile err; /* Avoid vfork clobbering */ + + setcommandmode(); + + err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; + switch(vfork()) { + case -1: + perror("Fork failed"); + break; + + case 0: + { + /* + * Fire up the shell in the child. + */ + const char *shellp, *shellname; + + shellp = getenv("SHELL"); + if (shellp == NULL) + shellp = "/bin/sh"; + if ((shellname = strrchr(shellp, '/')) == 0) + shellname = shellp; + else + shellname++; + if (argc > 1) + execl(shellp, shellname, "-c", &saveline[1], NULL); + else + execl(shellp, shellname, NULL); + perror("execl"); + _exit(1); + } + default: + (void)wait((int *)0); /* Wait for the shell to complete */ + + if (TerminalWindowSize(&newrows, &newcols) && connected && + (err || ((oldrows != newrows) || (oldcols != newcols)))) { + sendnaws(); + } + break; + } + return 1; +} +#endif /* !defined(TN3270) */ + +/*VARARGS*/ +static int +bye(int argc, char *argv[]) +{ + extern int resettermname; + + if (connected) { + (void) shutdown(net, 2); + printf("Connection closed.\n"); + (void) NetClose(net); + connected = 0; + resettermname = 1; +#if defined(AUTHENTICATION) || defined(ENCRYPTION) + auth_encrypt_connect(connected); +#endif /* defined(AUTHENTICATION) */ + /* reset options */ + tninit(); +#ifdef TN3270 + SetIn3270(); /* Get out of 3270 mode */ +#endif /* defined(TN3270) */ + } + if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { + longjmp(toplevel, 1); + /* NOTREACHED */ + } + return 1; /* Keep lint, etc., happy */ +} + +/*VARARGS*/ +int +quit(int argc, char *argv[]) +{ + (void) call(bye, "bye", "fromquit", 0); + Exit(0); + /*NOTREACHED*/ +} + +/*VARARGS*/ +int +logout(int argc, char *argv[]) +{ + send_do(TELOPT_LOGOUT, 1); + (void) netflush(); + return 1; +} + + +/* + * The SLC command. + */ + +struct slclist { + const char *name; + const char *help; + void (*handler)(int); + int arg; +}; + +struct slclist SlcList[] = { + { "export", "Use local special character definitions", + slc_mode_export, 0 }, + { "import", "Use remote special character definitions", + slc_mode_import, 1 }, + { "check", "Verify remote special character definitions", + slc_mode_import, 0 }, + { "help", 0, slc_help, 0 }, + { "?", "Print help information", slc_help, 0 }, + { .name = 0 }, +}; + +static void +slc_help(int n) +{ + struct slclist *c; + + for (c = SlcList; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s %s\n", c->name, c->help); + else + printf("\n"); + } + } +} + +static struct slclist * +getslc(char *name) +{ + return (struct slclist *) + genget(name, (char **) SlcList, sizeof(struct slclist)); +} + +static int +slccmd(int argc, char *argv[]) +{ + struct slclist *c; + + if (argc != 2) { + fprintf(stderr, + "Need an argument to 'slc' command. 'slc ?' for help.\n"); + return 0; + } + c = getslc(argv[1]); + if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n", + argv[1]); + return 0; + } + if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n", + argv[1]); + return 0; + } + (*c->handler)(c->arg); + slcstate(); + return 1; +} + +/* + * The ENVIRON command. + */ + +struct envlist { + const char *name; + const char *help; + struct env_lst *(*handler)(const unsigned char *, unsigned char *); + int narg; +}; + +struct envlist EnvList[] = { + { "define", "Define an environment variable", + env_define, 2 }, + { "undefine", "Undefine an environment variable", + env_undefine, 1 }, + { "export", "Mark an environment variable for automatic export", + env_export, 1 }, + { "unexport", "Don't mark an environment variable for automatic export", + env_unexport, 1 }, + { "send", "Send an environment variable", env_send, 1 }, + { "list", "List the current environment variables", + env_list, 0 }, +#if defined(OLD_ENVIRON) && defined(ENV_HACK) + { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", + env_varval, 1 }, +#endif + { "help", 0, env_help, 0 }, + { "?", "Print help information", env_help, 0 }, + { .name = 0 }, +}; + +static struct env_lst * +env_help(const unsigned char *us1, unsigned char *us2) +{ + struct envlist *c; + + for (c = EnvList; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s %s\n", c->name, c->help); + else + printf("\n"); + } + } + return NULL; +} + +static struct envlist * +getenvcmd(char *name) +{ + return (struct envlist *) + genget(name, (char **) EnvList, sizeof(struct envlist)); +} + +int +env_cmd(int argc, char *argv[]) +{ + struct envlist *c; + + if (argc < 2) { + fprintf(stderr, + "Need an argument to 'environ' command. 'environ ?' for help.\n"); + return 0; + } + c = getenvcmd(argv[1]); + if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n", + argv[1]); + return 0; + } + if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", + argv[1]); + return 0; + } + if (c->narg + 2 != argc) { + fprintf(stderr, + "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n", + c->narg < argc + 2 ? "only " : "", + c->narg, c->narg == 1 ? "" : "s", c->name); + return 0; + } + (*c->handler)(argv[2], argv[3]); + return 1; +} + +struct env_lst { + struct env_lst *next; /* pointer to next structure */ + struct env_lst *prev; /* pointer to previous structure */ + unsigned char *var; /* pointer to variable name */ + unsigned char *value; /* pointer to variable value */ + int export; /* 1 -> export with default list of variables */ + int welldefined; /* A well defined variable */ +}; + +struct env_lst envlisthead; + +struct env_lst * +env_find(const unsigned char *var) +{ + struct env_lst *ep; + + for (ep = envlisthead.next; ep; ep = ep->next) { + if (strcmp((const char *)ep->var, (const char *)var) == 0) + return(ep); + } + return(NULL); +} + +void +env_init(void) +{ + extern char **environ; + char **epp, *cp; + struct env_lst *ep; + + for (epp = environ; *epp; epp++) { + if ((cp = strchr(*epp, '=')) != NULL) { + *cp = '\0'; + ep = env_define((unsigned char *)*epp, + (unsigned char *)cp+1); + ep->export = 0; + *cp = '='; + } + } + /* + * Special case for DISPLAY variable. If it is ":0.0" or + * "unix:0.0", we have to get rid of "unix" and insert our + * hostname. + */ + if ((ep = env_find("DISPLAY")) + && ((*ep->value == ':') + || (strncmp((char *)ep->value, "unix:", 5) == 0))) { + char hbuf[MAXHOSTNAMELEN + 1]; + char *cp2 = strchr((char *)ep->value, ':'); + + gethostname(hbuf, sizeof hbuf); + hbuf[sizeof(hbuf) - 1] = '\0'; + cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1); + sprintf((char *)cp, "%s%s", hbuf, cp2); + free(ep->value); + ep->value = (unsigned char *)cp; + } + /* + * If USER is not defined, but LOGNAME is, then add + * USER with the value from LOGNAME. By default, we + * don't export the USER variable. + */ + if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) { + env_define((const unsigned char *)"USER", ep->value); + env_unexport((const unsigned char *)"USER", NULL); + } + env_export((const unsigned char *)"DISPLAY", NULL); + env_export((const unsigned char *)"PRINTER", NULL); +} + +struct env_lst * +env_define(const unsigned char *var, unsigned char *value) +{ + struct env_lst *ep; + + if ((ep = env_find(var)) != NULL) { + if (ep->var) + free(ep->var); + if (ep->value) + free(ep->value); + } else { + ep = (struct env_lst *)malloc(sizeof(struct env_lst)); + ep->next = envlisthead.next; + envlisthead.next = ep; + ep->prev = &envlisthead; + if (ep->next) + ep->next->prev = ep; + } + ep->welldefined = opt_welldefined(var); + ep->export = 1; + ep->var = (unsigned char *)strdup((const char *)var); + ep->value = (unsigned char *)strdup((const char *)value); + return(ep); +} + +struct env_lst * +env_undefine(const unsigned char *var, unsigned char *d) +{ + struct env_lst *ep; + + if ((ep = env_find(var)) != NULL) { + ep->prev->next = ep->next; + if (ep->next) + ep->next->prev = ep->prev; + if (ep->var) + free(ep->var); + if (ep->value) + free(ep->value); + free(ep); + } + return NULL; +} + +struct env_lst * +env_export(const unsigned char *var, unsigned char *d) +{ + struct env_lst *ep; + + if ((ep = env_find(var)) != NULL) + ep->export = 1; + return NULL; +} + +struct env_lst * +env_unexport(const unsigned char *var, unsigned char *d) +{ + struct env_lst *ep; + + if ((ep = env_find(var)) != NULL) + ep->export = 0; + return NULL; +} + +struct env_lst * +env_send(const unsigned char *var, unsigned char *d) +{ + struct env_lst *ep; + + if (my_state_is_wont(TELOPT_NEW_ENVIRON) +#ifdef OLD_ENVIRON + && my_state_is_wont(TELOPT_OLD_ENVIRON) +#endif + ) { + fprintf(stderr, + "Cannot send '%s': Telnet ENVIRON option not enabled\n", + var); + return NULL; + } + ep = env_find(var); + if (ep == 0) { + fprintf(stderr, "Cannot send '%s': variable not defined\n", + var); + return NULL; + } + env_opt_start_info(); + env_opt_add(ep->var); + env_opt_end(0); + return NULL; +} + +struct env_lst * +env_list(const unsigned char *d1, unsigned char *d2) +{ + struct env_lst *ep; + + for (ep = envlisthead.next; ep; ep = ep->next) { + printf("%c %-20s %s\n", ep->export ? '*' : ' ', + ep->var, ep->value); + } + return NULL; +} + +unsigned char * +env_default(int init, int welldefined) +{ + static struct env_lst *nep = NULL; + + if (init) { + nep = &envlisthead; + return NULL; + } + if (nep) { + while ((nep = nep->next) != NULL) { + if (nep->export && (nep->welldefined == welldefined)) + return(nep->var); + } + } + return(NULL); +} + +unsigned char * +env_getvalue(const unsigned char *var) +{ + struct env_lst *ep; + + if ((ep = env_find(var)) != NULL) + return(ep->value); + return(NULL); +} + +#if defined(OLD_ENVIRON) && defined(ENV_HACK) +void +env_varval(const unsigned char *what) +{ + extern int old_env_var, old_env_value, env_auto; + int len = strlen((char *)what); + + if (len == 0) + goto unknown; + + if (strncasecmp((char *)what, "status", len) == 0) { + if (env_auto) + printf("%s%s", "VAR and VALUE are/will be ", + "determined automatically\n"); + if (old_env_var == OLD_ENV_VAR) + printf("VAR and VALUE set to correct definitions\n"); + else + printf("VAR and VALUE definitions are reversed\n"); + } else if (strncasecmp((char *)what, "auto", len) == 0) { + env_auto = 1; + old_env_var = OLD_ENV_VALUE; + old_env_value = OLD_ENV_VAR; + } else if (strncasecmp((char *)what, "right", len) == 0) { + env_auto = 0; + old_env_var = OLD_ENV_VAR; + old_env_value = OLD_ENV_VALUE; + } else if (strncasecmp((char *)what, "wrong", len) == 0) { + env_auto = 0; + old_env_var = OLD_ENV_VALUE; + old_env_value = OLD_ENV_VAR; + } else { +unknown: + printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n"); + } +} +#endif + +#ifdef AUTHENTICATION +/* + * The AUTHENTICATE command. + */ + +struct authlist { + const char *name; + const char *help; + int (*handler)(char *); + int narg; +}; + +struct authlist AuthList[] = { + { "status", "Display current status of authentication information", + auth_status, 0 }, + { "disable", "Disable an authentication type ('auth disable ?' for more)", + auth_disable, 1 }, + { "enable", "Enable an authentication type ('auth enable ?' for more)", + auth_enable, 1 }, + { "help", 0, auth_help, 0 }, + { "?", "Print help information", auth_help, 0 }, + { .name = 0 }, +}; + +static int +auth_help(char *s) +{ + struct authlist *c; + + for (c = AuthList; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s %s\n", c->name, c->help); + else + printf("\n"); + } + } + return 0; +} + +int +auth_cmd(int argc, char *argv[]) +{ + struct authlist *c; + + if (argc < 2) { + fprintf(stderr, + "Need an argument to 'auth' command. 'auth ?' for help.\n"); + return 0; + } + + c = (struct authlist *) + genget(argv[1], (char **) AuthList, sizeof(struct authlist)); + if (c == 0) { + fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n", + argv[1]); + return 0; + } + if (Ambiguous(c)) { + fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n", + argv[1]); + return 0; + } + if (c->narg + 2 != argc) { + fprintf(stderr, + "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n", + c->narg < argc + 2 ? "only " : "", + c->narg, c->narg == 1 ? "" : "s", c->name); + return 0; + } + return((*c->handler)(argv[2])); +} +#endif + +#ifdef ENCRYPTION +/* + * The ENCRYPT command. + */ + +struct encryptlist { + const char *name; + const char *help; + int (*handler)(char *, char *); + int needconnect; + int minarg; + int maxarg; +}; + +static int + EncryptHelp(char *, char *); +typedef int (*encrypthandler)(char *, char *); + +struct encryptlist EncryptList[] = { + { "enable", "Enable encryption. ('encrypt enable ?' for more)", + EncryptEnable, 1, 1, 2 }, + { "disable", "Disable encryption. ('encrypt enable ?' for more)", + EncryptDisable, 0, 1, 2 }, + { "type", "Set encryption type. ('encrypt type ?' for more)", + EncryptType, 0, 1, 1 }, + { "start", "Start encryption. ('encrypt start ?' for more)", + (encrypthandler) EncryptStart, 1, 0, 1 }, + { "stop", "Stop encryption. ('encrypt stop ?' for more)", + (encrypthandler) EncryptStop, 1, 0, 1 }, + { "input", "Start encrypting the input stream", + (encrypthandler) EncryptStartInput, 1, 0, 0 }, + { "-input", "Stop encrypting the input stream", + (encrypthandler) EncryptStopInput, 1, 0, 0 }, + { "output", "Start encrypting the output stream", + (encrypthandler) EncryptStartOutput, 1, 0, 0 }, + { "-output", "Stop encrypting the output stream", + (encrypthandler) EncryptStopOutput, 1, 0, 0 }, + + { "status", "Display current status of authentication information", + (encrypthandler) EncryptStatus, 0, 0, 0 }, + { "help", 0, EncryptHelp, 0, 0, 0 }, + { "?", "Print help information", EncryptHelp, 0, 0, 0 }, + { .name = 0 }, +}; + +static int +EncryptHelp(char *s1, char *s2) +{ + struct encryptlist *c; + + for (c = EncryptList; c->name; c++) { + if (c->help) { + if (*c->help) + printf("%-15s %s\n", c->name, c->help); + else + printf("\n"); + } + } + return (0); +} + +int +encrypt_cmd(int argc, char *argv[]) +{ + struct encryptlist *c; + + if (argc < 2) { + fprintf(stderr, + "Need an argument to 'encrypt' command. " + "'encrypt ?' for help.\n"); + return (0); + } + + c = (struct encryptlist *) + genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist)); + if (c == NULL) { + fprintf(stderr, + "'%s': unknown argument ('encrypt ?' for help).\n", + argv[1]); + return (0); + } + if (Ambiguous(c)) { + fprintf(stderr, + "'%s': ambiguous argument ('encrypt ?' for help).\n", + argv[1]); + return (0); + } + argc -= 2; + if (argc < c->minarg || argc > c->maxarg) { + if (c->minarg == c->maxarg) { + fprintf(stderr, "Need %s%d argument%s ", + c->minarg < argc ? "only " : "", c->minarg, + c->minarg == 1 ? "" : "s"); + } else { + fprintf(stderr, "Need %s%d-%d arguments ", + c->maxarg < argc ? "only " : "", c->minarg, + c->maxarg); + } + fprintf(stderr, + "to 'encrypt %s' command. 'encrypt ?' for help.\n", + c->name); + return (0); + } + if (c->needconnect && !connected) { + if (!(argc && (isprefix(argv[2], "help") || + isprefix(argv[2], "?")))) { + printf("?Need to be connected first.\n"); + return (0); + } + } + return ((*c->handler)(argv[2], argv[3])); +} +#endif /* ENCRYPTION */ + +#ifdef TN3270 +static void +filestuff(int fd) +{ + int res; + + setconnmode(0); + res = fcntl(fd, F_GETOWN, 0); + setcommandmode(); + + if (res == -1) { + perror("fcntl"); + return; + } + printf("\tOwner is %d.\n", res); + + setconnmode(0); + res = fcntl(fd, F_GETFL, 0); + setcommandmode(); + + if (res == -1) { + perror("fcntl"); + return; + } +#ifdef notdef + printf("\tFlags are 0x%x: %s\n", res, decodeflags(res)); +#endif +} +#endif /* defined(TN3270) */ + +/* + * Print status about the connection. + */ +/*ARGSUSED*/ +static int +status(int argc, char *argv[]) +{ + if (connected) { + printf("Connected to %s.\n", hostname); + if ((argc < 2) || strcmp(argv[1], "notmuch")) { + int mode = getconnmode(); + + if (my_want_state_is_will(TELOPT_LINEMODE)) { + printf("Operating with LINEMODE option\n"); + printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No"); + printf("%s catching of signals\n", + (mode&MODE_TRAPSIG) ? "Local" : "No"); + slcstate(); +#ifdef KLUDGELINEMODE + } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) { + printf("Operating in obsolete linemode\n"); +#endif + } else { + printf("Operating in single character mode\n"); + if (localchars) + printf("Catching signals locally\n"); + } + printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote"); + if (my_want_state_is_will(TELOPT_LFLOW)) + printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No"); +#ifdef ENCRYPTION + encrypt_display(); +#endif /* ENCRYPTION */ + } + } else { + printf("No connection.\n"); + } +# ifndef TN3270 + printf("Escape character is '%s'.\n", control(escape)); + (void) fflush(stdout); +# else /* !defined(TN3270) */ + if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { + printf("Escape character is '%s'.\n", control(escape)); + } + if ((argc >= 2) && !strcmp(argv[1], "everything")) { + printf("SIGIO received %d time%s.\n", + sigiocount, (sigiocount == 1)? "":"s"); + if (In3270) { + printf("Process ID %d, process group %d.\n", + getpid(), getpgrp()); + printf("Terminal input:\n"); + filestuff(tin); + printf("Terminal output:\n"); + filestuff(tout); + printf("Network socket:\n"); + filestuff(net); + } + } + if (In3270 && transcom) { + printf("Transparent mode command is '%s'.\n", transcom); + } + (void) fflush(stdout); + if (In3270) { + return 0; + } +# endif /* defined(TN3270) */ + return 1; +} + +/* + * Function that gets called when SIGINFO is received. + */ +int +ayt_status(void) +{ + return call(status, "status", "notmuch", 0); +} + +static const char * +sockaddr_ntop(struct sockaddr *sa) +{ + static char addrbuf[NI_MAXHOST]; + const int niflags = NI_NUMERICHOST; + + if (getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), + NULL, 0, niflags) == 0) + return addrbuf; + else + return NULL; +} + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +static int setpolicy (int, struct addrinfo *, char *); + +static int +setpolicy(int netw, struct addrinfo *res, char *policy) +{ + char *buf; + int level; + int optname; + + if (policy == NULL) + return 0; + + buf = ipsec_set_policy(policy, strlen(policy)); + if (buf == NULL) { + printf("%s\n", ipsec_strerror()); + return -1; + } + level = res->ai_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + optname = res->ai_family == AF_INET ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY; + if (setsockopt(netw, level, optname, buf, ipsec_get_policylen(buf)) < 0){ + perror("setsockopt"); + return -1; + } + + free(buf); + return 0; +} +#endif + +int +tn(int argc, char *argv[]) +{ + struct addrinfo hints, *res, *res0; + const char *cause = "telnet: unknown"; + int error; +#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + char *srp = 0; + long srlen; + int proto, opt; +#endif + char *cmd, *hostp = 0; + const char *portp = 0; + const char *user = 0; + + if (connected) { + printf("?Already connected to %s\n", hostname); + return 0; + } + if (argc < 2) { + (void) strlcpy(line, "open ", sizeof(line)); + printf("(to) "); + (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin); + makeargv(); + argc = margc; + argv = margv; + } + cmd = *argv; + --argc; ++argv; + while (argc) { + if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?")) + goto usage; + if (strcmp(*argv, "-l") == 0) { + --argc; ++argv; + if (argc == 0) + goto usage; + user = *argv++; + --argc; + continue; + } + if (strcmp(*argv, "-a") == 0) { + --argc; ++argv; + autologin = 1; + continue; + } + if (hostp == 0) { + hostp = *argv++; + --argc; + continue; + } + if (portp == 0) { + portp = *argv++; + --argc; + continue; + } + usage: + printf("usage: %s [-l user] [-a] host-name [port]\n", cmd); + return 0; + } + if (hostp == 0) + goto usage; + + (void) strlcpy(_hostname, hostp, sizeof(_hostname)); + if (hostp[0] == '@' || hostp[0] == '!') { + char *p; + hostname = NULL; + for (p = hostp + 1; *p; p++) { + if (*p == ',' || *p == '@') + hostname = p; + } + if (hostname == NULL) { + fprintf(stderr, "%s: bad source route specification\n", hostp); + return 0; + } + *hostname++ = '\0'; + } else + hostname = hostp; + + if (!portp) { + telnetport = 1; + portp = "telnet"; + } else if (portp[0] == '-') { + /* use telnet negotiation if port number/name preceded by minus sign */ + telnetport = 1; + portp++; + } else + telnetport = 0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_NUMERICHOST; /* avoid forward lookup */ + error = getaddrinfo(hostname, portp, &hints, &res0); + if (!error) { + /* numeric */ + if (doaddrlookup && + getnameinfo(res0->ai_addr, res0->ai_addrlen, + _hostname, sizeof(_hostname), NULL, 0, NI_NAMEREQD) == 0) + ; /* okay */ + else + strlcpy(_hostname, hostname, sizeof(_hostname)); + } else { + /* FQDN - try again with forward DNS lookup */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(hostname, portp, &hints, &res0); + if (error == EAI_SERVICE) { + fprintf(stderr, "tcp/%s: unknown service\n", portp); + return 0; + } else if (error) { + fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error)); + return 0; + } + if (res0->ai_canonname) + (void)strlcpy(_hostname, res0->ai_canonname, sizeof(_hostname)); + else + (void)strlcpy(_hostname, hostname, sizeof(_hostname)); + } + hostname = _hostname; + + net = -1; + for (res = res0; res; res = res->ai_next) { + printf("Trying %s...\n", sockaddr_ntop(res->ai_addr)); + net = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (net < 0) { + cause = "telnet: socket"; + continue; + } + + if (telnet_debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { + perror("setsockopt (SO_DEBUG)"); + } + if (hostp[0] == '@' || hostp[0] == '!') { + if ((srlen = sourceroute(res, hostp, &srp, &proto, &opt)) < 0) { + (void) NetClose(net); + net = -1; + continue; + } + if (srp && setsockopt(net, proto, opt, srp, srlen) < 0) + perror("setsockopt (source route)"); + } + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + if (setpolicy(net, res, ipsec_policy_in) < 0) { + (void) NetClose(net); + net = -1; + continue; + } + if (setpolicy(net, res, ipsec_policy_out) < 0) { + (void) NetClose(net); + net = -1; + continue; + } +#endif + + if (connect(net, res->ai_addr, res->ai_addrlen) < 0) { + if (res->ai_next) { + int oerrno = errno; + + fprintf(stderr, "telnet: connect to address %s: ", + sockaddr_ntop(res->ai_addr)); + errno = oerrno; + perror((char *)0); + } + cause = "telnet: Unable to connect to remote host"; + (void) NetClose(net); + net = -1; + continue; + } + + connected++; +#if defined(AUTHENTICATION) || defined(ENCRYPTION) + auth_encrypt_connect(connected); +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ + break; + } + freeaddrinfo(res0); + if (net < 0 || connected == 0) { + perror(cause); + return 0; + } + + cmdrc(hostp, hostname); + if (autologin && user == NULL) { + struct passwd *pw; + + user = getenv("USER"); + if (user == NULL || + ((pw = getpwnam(user)) && pw->pw_uid != getuid())) { + if ((pw = getpwuid(getuid())) != NULL) + user = pw->pw_name; + else + user = NULL; + } + } + if (user) { + env_define((const unsigned char *)"USER", __UNCONST(user)); + env_export((const unsigned char *)"USER", NULL); + } + (void) call(status, "status", "notmuch", 0); + telnet(user); + (void) NetClose(net); + ExitString("Connection closed by foreign host.\n",1); + /*NOTREACHED*/ +} + +#define HELPINDENT ((int)sizeof ("connect")) + +static char + openhelp[] = "connect to a site", + closehelp[] = "close current connection", + logouthelp[] = "forcibly logout remote user and close the connection", + quithelp[] = "exit telnet", + statushelp[] = "print status information", + helphelp[] = "print help information", + sendhelp[] = "transmit special characters ('send ?' for more)", + sethelp[] = "set operating parameters ('set ?' for more)", + unsethelp[] = "unset operating parameters ('unset ?' for more)", + togglestring[] ="toggle operating parameters ('toggle ?' for more)", + slchelp[] = "change state of special characters ('slc ?' for more)", + displayhelp[] = "display operating parameters", +#ifdef TN3270 + transcomhelp[] = "specify Unix command for transparent mode pipe", +#endif /* defined(TN3270) */ +#ifdef AUTHENTICATION + authhelp[] = "turn on (off) authentication ('auth ?' for more)", +#endif +#ifdef ENCRYPTION + encrypthelp[] = "turn on (off) encryption ('encrypt ?' for more)", +#endif /* ENCRYPTION */ + zhelp[] = "suspend telnet", + shellhelp[] = "invoke a subshell", + envhelp[] = "change environment variables ('environ ?' for more)", + modestring[] = "try to enter line or character mode ('mode ?' for more)"; + +static Command cmdtab[] = { + { "close", closehelp, bye, 1 }, + { "logout", logouthelp, logout, 1 }, + { "display", displayhelp, display, 0 }, + { "mode", modestring, modecmd, 0 }, + { "open", openhelp, tn, 0 }, + { "quit", quithelp, quit, 0 }, + { "send", sendhelp, sendcmd, 0 }, + { "set", sethelp, setcmd, 0 }, + { "unset", unsethelp, unsetcmd, 0 }, + { "status", statushelp, status, 0 }, + { "toggle", togglestring, toggle, 0 }, + { "slc", slchelp, slccmd, 0 }, +#ifdef TN3270 + { "transcom", transcomhelp, settranscom, 0 }, +#endif /* defined(TN3270) */ +#ifdef AUTHENTICATION + { "auth", authhelp, auth_cmd, 0 }, +#endif +#ifdef ENCRYPTION + { "encrypt", encrypthelp, encrypt_cmd, 0 }, +#endif + { "z", zhelp, suspend, 0 }, +#ifdef TN3270 + { "!", shellhelp, shell, 1 }, +#else + { "!", shellhelp, shell, 0 }, +#endif + { "environ", envhelp, env_cmd, 0 }, + { "?", helphelp, help, 0 }, + { NULL, NULL, NULL, 0 } +}; + +static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; +static char escapehelp[] = "deprecated command -- use 'set escape' instead"; + +static Command cmdtab2[] = { + { "help", 0, help, 0 }, + { "escape", escapehelp, setescape, 0 }, + { "crmod", crmodhelp, togcrmod, 0 }, + { NULL, NULL, NULL, 0 } +}; + + +/* + * Call routine with argc, argv set from args (terminated by 0). + */ + +/*VARARGS1*/ +static int +call(intrtn_t routine, ...) +{ + va_list ap; + char *args[100]; + int argno = 0; + + va_start(ap, routine); + while ((args[argno++] = va_arg(ap, char *)) != 0) { + ; + } + va_end(ap); + return (*routine)(argno-1, args); +} + + +static Command * +getcmd(char *name) +{ + Command *cm; + + if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))) != NULL) + return cm; + return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); +} + +void +command(int top, const char *tbuf, int cnt) +{ + Command *c; + + setcommandmode(); + if (!top) { + putchar('\n'); + } else { + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + } + for (;;) { + if (rlogin == _POSIX_VDISABLE) + printf("%s> ", prompt); + if (tbuf) { + char *cp; + cp = line; + while (cnt > 0 && (*cp++ = *tbuf++) != '\n') + cnt--; + tbuf = 0; + if (cp == line || *--cp != '\n' || cp == line) + goto getline; + *cp = '\0'; + if (rlogin == _POSIX_VDISABLE) + printf("%s\n", line); + } else { + getline: + if (rlogin != _POSIX_VDISABLE) + printf("%s> ", prompt); +#ifdef TN3270 + fflush(stdout); +#endif + if (fgets(line, sizeof(line), stdin) == NULL) { + if (feof(stdin) || ferror(stdin)) { + (void) quit(0, NULL); + /*NOTREACHED*/ + } + break; + } + } + if (line[0] == 0) + break; + makeargv(); + if (margv[0] == 0) { + break; + } + c = getcmd(margv[0]); + if (Ambiguous(c)) { + printf("?Ambiguous command\n"); + continue; + } + if (c == 0) { + printf("?Invalid command\n"); + continue; + } + if (c->needconnect && !connected) { + printf("?Need to be connected first.\n"); + continue; + } + if ((*c->handler)(margc, margv)) { + break; + } + } + if (!top) { + if (!connected) { + longjmp(toplevel, 1); + /*NOTREACHED*/ + } +#ifdef TN3270 + if (shell_active == 0) { + setconnmode(0); + } +#else /* defined(TN3270) */ + setconnmode(0); +#endif /* defined(TN3270) */ + } +} + +/* + * Help command. + */ +static int +help(int argc, char *argv[]) +{ + Command *c; + + if (argc == 1) { + printf("Commands may be abbreviated. Commands are:\n\n"); + for (c = cmdtab; c->name; c++) + if (c->help) { + printf("%-*s\t%s\n", HELPINDENT, c->name, + c->help); + } + return 0; + } + while (--argc > 0) { + char *arg; + arg = *++argv; + c = getcmd(arg); + if (Ambiguous(c)) + printf("?Ambiguous help command %s\n", arg); + else if (c == (Command *)0) + printf("?Invalid help command %s\n", arg); + else + printf("%s\n", c->help); + } + return 0; +} + +static char *rcname = 0; +static char rcbuf[128]; + +void +cmdrc(const char *m1, const char *m2) +{ + Command *c; + FILE *rcfile; + int gotmachine = 0; + int l1 = strlen(m1); + int l2 = strlen(m2); + char m1save[MAXHOSTNAMELEN + 1]; + + if (skiprc) + return; + + strlcpy(m1save, m1, sizeof(m1save)); + m1 = m1save; + + if (rcname == 0) { + rcname = getenv("HOME"); + if (rcname) + strlcpy(rcbuf, rcname, sizeof(rcbuf)); + else + rcbuf[0] = '\0'; + strlcat(rcbuf, "/.telnetrc", sizeof(rcbuf)); + rcname = rcbuf; + } + + if ((rcfile = fopen(rcname, "r")) == 0) { + return; + } + + for (;;) { + if (fgets(line, sizeof(line), rcfile) == NULL) + break; + if (line[0] == 0) + break; + if (line[0] == '#') + continue; + if (gotmachine) { + if (!isspace((unsigned char)line[0])) + gotmachine = 0; + } + if (gotmachine == 0) { + if (isspace((unsigned char)line[0])) + continue; + if (strncasecmp(line, m1, l1) == 0) + strncpy(line, &line[l1], sizeof(line) - l1); + else if (strncasecmp(line, m2, l2) == 0) + strncpy(line, &line[l2], sizeof(line) - l2); + else if (strncasecmp(line, "DEFAULT", 7) == 0) + strncpy(line, &line[7], sizeof(line) - 7); + else + continue; + if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n') + continue; + gotmachine = 1; + } + makeargv(); + if (margv[0] == 0) + continue; + c = getcmd(margv[0]); + if (Ambiguous(c)) { + printf("?Ambiguous command: %s\n", margv[0]); + continue; + } + if (c == 0) { + printf("?Invalid command: %s\n", margv[0]); + continue; + } + /* + * This should never happen... + */ + if (c->needconnect && !connected) { + printf("?Need to be connected first for %s.\n", margv[0]); + continue; + } + (*c->handler)(margc, margv); + } + fclose(rcfile); +} + +/* + * Source route is handed in as + * [!]@hop1@hop2...@dst + * + * If the leading ! is present, it is a strict source route, otherwise it is + * assmed to be a loose source route. Note that leading ! is effective + * only for IPv4 case. + * + * We fill in the source route option as + * hop1,hop2,hop3...dest + * and return a pointer to hop1, which will + * be the address to connect() to. + * + * Arguments: + * ai: The address (by struct addrinfo) for the final destination. + * + * arg: Pointer to route list to decipher + * + * cpp: Pointer to a pointer, so that sourceroute() can return + * the address of result buffer (statically alloc'ed). + * + * protop/optp: + * Pointer to an integer. The pointed variable + * lenp: pointer to an integer that contains the + * length of *cpp if *cpp != NULL. + * + * Return values: + * + * Returns the length of the option pointed to by *cpp. If the + * return value is -1, there was a syntax error in the + * option, either arg contained unknown characters or too many hosts, + * or hostname cannot be resolved. + * + * The caller needs to pass return value (len), *cpp, *protop and *optp + * to setsockopt(2). + * + * *cpp: Points to the result buffer. The region is statically + * allocated by the function. + * + * *protop: + * protocol # to be passed to setsockopt(2). + * + * *optp: option # to be passed to setsockopt(2). + * + */ +int +sourceroute(struct addrinfo *ai, char *arg, char **cpp, int *protop, int *optp) +{ + char *cp, *cp2, *lsrp, *lsrep; + struct addrinfo hints, *res; + int len, error; + struct sockaddr_in *sin; + char c; + static char lsr[44]; +#ifdef INET6 + struct cmsghdr *cmsg; + struct sockaddr_in6 *sin6; + static char rhbuf[1024]; +#endif + + /* + * Verify the arguments. + */ + if (cpp == NULL) + return -1; + + cp = arg; + + *cpp = NULL; + + /* init these just in case.... */ + lsrp = NULL; + lsrep = NULL; +#ifdef INET6 + cmsg = NULL; +#endif + + switch (ai->ai_family) { + case AF_INET: + lsrp = lsr; + lsrep = lsrp + sizeof(lsr); + + /* + * Next, decide whether we have a loose source + * route or a strict source route, and fill in + * the begining of the option. + */ + if (*cp == '!') { + cp++; + *lsrp++ = IPOPT_SSRR; + } else + *lsrp++ = IPOPT_LSRR; + if (*cp != '@') + return -1; + lsrp++; /* skip over length, we'll fill it in later */ + *lsrp++ = 4; + cp++; + *protop = IPPROTO_IP; + *optp = IP_OPTIONS; + break; +#ifdef INET6 + case AF_INET6: +#ifdef IPV6_PKTOPTIONS + /* RFC2292 */ + cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0); + if (*cp != '@') + return -1; + cp++; + *protop = IPPROTO_IPV6; + *optp = IPV6_PKTOPTIONS; + break; +#else + /* no RFC2292 */ + return -1; +#endif +#endif + default: + return -1; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ai->ai_family; + hints.ai_socktype = SOCK_STREAM; + + for (c = 0;;) { + if (c == ':') + cp2 = 0; + else for (cp2 = cp; (c = *cp2) != '\0'; cp2++) { + if (c == ',') { + *cp2++ = '\0'; + if (*cp2 == '@') + cp2++; + } else if (c == '@') { + *cp2++ = '\0'; + } +#if 0 /*colon conflicts with IPv6 address*/ + else if (c == ':') { + *cp2++ = '\0'; + } +#endif + else + continue; + break; + } + if (!c) + cp2 = 0; + + error = getaddrinfo(cp, NULL, &hints, &res); + if (error) { + fprintf(stderr, "%s: %s\n", cp, gai_strerror(error)); + return -1; + } + if (ai->ai_family != res->ai_family) { + freeaddrinfo(res); + return -1; + } + if (ai->ai_family == AF_INET) { + /* + * Check to make sure there is space for address + */ + if (lsrp + 4 > lsrep) { + freeaddrinfo(res); + return -1; + } + sin = (struct sockaddr_in *)res->ai_addr; + memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr)); + lsrp += sizeof(struct in_addr); + } +#ifdef INET6 + else if (ai->ai_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)res->ai_addr; + inet6_rthdr_add(cmsg, &sin6->sin6_addr, + IPV6_RTHDR_LOOSE); + } +#endif + else { + freeaddrinfo(res); + return -1; + } + freeaddrinfo(res); + if (cp2) + cp = cp2; + else + break; + } + switch (ai->ai_family) { + case AF_INET: + /* record the last hop */ + if (lsrp + 4 > lsrep) + return -1; + sin = (struct sockaddr_in *)ai->ai_addr; + memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr)); + lsrp += sizeof(struct in_addr); + lsr[IPOPT_OLEN] = lsrp - lsr; + if (lsr[IPOPT_OLEN] <= 7 || lsr[IPOPT_OLEN] > 40) + return -1; + *lsrp++ = IPOPT_NOP; /*32bit word align*/ + len = lsrp - lsr; + *cpp = lsr; + break; +#ifdef INET6 + case AF_INET6: + inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE); + len = cmsg->cmsg_len; + *cpp = rhbuf; + break; +#endif + default: + return -1; + } + return len; +} diff --git a/usr.bin/telnet/defines.h b/usr.bin/telnet/defines.h new file mode 100644 index 000000000..93f6aa0bd --- /dev/null +++ b/usr.bin/telnet/defines.h @@ -0,0 +1,59 @@ +/* $NetBSD: defines.h,v 1.8 2003/08/07 11:16:09 agc Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)defines.h 8.1 (Berkeley) 6/6/93 + */ + +#define settimer(x) clocks.x = clocks.system++ + +#ifndef TN3270 + +#define SetIn3270() + +#endif /* !defined(TN3270) */ + +#define NETADD(c) { *netoring.supply = (c); ring_supplied(&netoring, 1); } +#define NET2ADD(c1,c2) { NETADD((c1)); NETADD((c2)); } +#define NETBYTES() (ring_full_count(&netoring)) +#define NETROOM() (ring_empty_count(&netoring)) + +#define TTYADD(c) if (!(SYNCHing||flushout)) { \ + *ttyoring.supply = c; \ + ring_supplied(&ttyoring, 1); \ + } +#define TTYBYTES() (ring_full_count(&ttyoring)) +#define TTYROOM() (ring_empty_count(&ttyoring)) + +/* Various modes */ +#define MODE_LOCAL_CHARS(m) ((m)&(MODE_EDIT|MODE_TRAPSIG)) +#define MODE_LOCAL_ECHO(m) ((m)&MODE_ECHO) +#define MODE_COMMAND_LINE(m) ((m)==-1) + +#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h new file mode 100644 index 000000000..6647bfc78 --- /dev/null +++ b/usr.bin/telnet/externs.h @@ -0,0 +1,413 @@ +/* $NetBSD: externs.h,v 1.37 2012/01/10 23:39:11 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)externs.h 8.3 (Berkeley) 5/30/95 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#if defined(IPSEC) +#include +#if defined(IPSEC_POLICY_IPSEC) +extern char *ipsec_policy_in; +extern char *ipsec_policy_out; +#endif +#endif + +#ifndef _POSIX_VDISABLE +# ifdef sun +# include /* pick up VDISABLE definition, mayby */ +# endif +# ifdef VDISABLE +# define _POSIX_VDISABLE VDISABLE +# else +# define _POSIX_VDISABLE ((cc_t)'\377') +# endif +#endif + +#define SUBBUFSIZE 256 + +#include + +extern int + autologin, /* Autologin enabled */ + skiprc, /* Don't process the ~/.telnetrc file */ + eight, /* use eight bit mode (binary in and/or out */ + family, /* address family of peer */ + flushout, /* flush output */ + connected, /* Are we connected to the other side? */ + globalmode, /* Mode tty should be in */ + In3270, /* Are we in 3270 mode? */ + telnetport, /* Are we connected to the telnet port? */ + localflow, /* Flow control handled locally */ + restartany, /* If flow control, restart output on any character */ + localchars, /* we recognize interrupt/quit */ + donelclchars, /* the user has set "localchars" */ + showoptions, + net, /* Network file descriptor */ + tin, /* Terminal input file descriptor */ + tout, /* Terminal output file descriptor */ + crlf, /* Should '\r' be mapped to (or )? */ + autoflush, /* flush output when interrupting? */ + autosynch, /* send interrupt characters with SYNCH? */ + SYNCHing, /* Is the stream in telnet SYNCH mode? */ + donebinarytoggle, /* the user has put us in binary */ + dontlecho, /* do we suppress local echoing right now? */ + crmod, + netdata, /* Print out network data flow */ + prettydump, /* Print "netdata" output in user readable format */ +#ifdef TN3270 + cursesdata, /* Print out curses data flow */ + apitrace, /* Trace API transactions */ +#endif /* defined(TN3270) */ + termdata, /* Print out terminal data flow */ + telnet_debug, /* Debug level */ + doaddrlookup, /* do a reverse address lookup? */ + clienteof; /* Client received EOF */ + +extern cc_t escape; /* Escape to command mode */ +extern cc_t rlogin; /* Rlogin mode escape character */ +#ifdef KLUDGELINEMODE +extern cc_t echoc; /* Toggle local echoing */ +#endif + +extern char + *prompt; /* Prompt for command. */ + +extern char + doopt[], + dont[], + will[], + wont[], + options[], /* All the little options */ + *hostname; /* Who are we connected to? */ + +#ifdef ENCRYPTION +extern void (*encrypt_output)(unsigned char *, int); +extern int (*decrypt_input)(int); +#endif /* ENCRYPTION */ + +/* + * We keep track of each side of the option negotiation. + */ + +#define MY_STATE_WILL 0x01 +#define MY_WANT_STATE_WILL 0x02 +#define MY_STATE_DO 0x04 +#define MY_WANT_STATE_DO 0x08 + +/* + * Macros to check the current state of things + */ + +#define my_state_is_do(opt) (options[opt]&MY_STATE_DO) +#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) +#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) +#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) + +#define my_state_is_dont(opt) (!my_state_is_do(opt)) +#define my_state_is_wont(opt) (!my_state_is_will(opt)) +#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) +#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) + +#define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;} +#define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;} +#define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;} +#define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;} + +#define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;} +#define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;} +#define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;} +#define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;} + +/* + * Make everything symmetrical + */ + +#define HIS_STATE_WILL MY_STATE_DO +#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO +#define HIS_STATE_DO MY_STATE_WILL +#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL + +#define his_state_is_do my_state_is_will +#define his_state_is_will my_state_is_do +#define his_want_state_is_do my_want_state_is_will +#define his_want_state_is_will my_want_state_is_do + +#define his_state_is_dont my_state_is_wont +#define his_state_is_wont my_state_is_dont +#define his_want_state_is_dont my_want_state_is_wont +#define his_want_state_is_wont my_want_state_is_dont + +#define set_his_state_do set_my_state_will +#define set_his_state_will set_my_state_do +#define set_his_want_state_do set_my_want_state_will +#define set_his_want_state_will set_my_want_state_do + +#define set_his_state_dont set_my_state_wont +#define set_his_state_wont set_my_state_dont +#define set_his_want_state_dont set_my_want_state_wont +#define set_his_want_state_wont set_my_want_state_dont + + +extern FILE + *NetTrace; /* Where debugging output goes */ +extern char + NetTraceFile[]; /* Name of file where debugging output goes */ + +extern jmp_buf + toplevel; /* For error conditions. */ + + +/* authenc.c */ +int telnet_net_write(unsigned char *, int); +void net_encrypt(void); +int telnet_spin(void); +char *telnet_getenv(char *); +char *telnet_gets(char *, char *, int, int); + +/* commands.c */ +int send_tncmd(void (*)(int, int), const char *, char *); +void _setlist_init(void); +void set_escape_char(char *); +int set_mode(int); +int clear_mode(int); +int modehelp(int); +int suspend(int, char *[]); +int shell(int, char *[]); +int quit(int, char *[]); +int logout(int, char *[]); +int env_cmd(int, char *[]); +struct env_lst *env_find(const unsigned char *); +void env_init(void); +struct env_lst *env_define(const unsigned char *, unsigned char *); +struct env_lst *env_undefine(const unsigned char *, unsigned char *); +struct env_lst *env_export(const unsigned char *, unsigned char *); +struct env_lst *env_unexport(const unsigned char *, unsigned char *); +struct env_lst *env_send(const unsigned char *, unsigned char *); +struct env_lst *env_list(const unsigned char *, unsigned char *); +unsigned char *env_default(int, int ); +unsigned char *env_getvalue(const unsigned char *); +void env_varval(const unsigned char *); +int auth_cmd(int, char *[]); +int ayt_status(void); +int encrypt_cmd(int, char *[]); +int tn(int, char *[]); +void command(int, const char *, int); +void cmdrc(const char *, const char *); +struct addrinfo; +int sourceroute(struct addrinfo *, char *, char **, int *, int*); + +/* main.c */ +void tninit(void); +void usage(void) __dead; + +/* network.c */ +void init_network(void); +int stilloob(void); +void setneturg(void); +int netflush(void); + +/* sys_bsd.c */ +void init_sys(void); +int TerminalWrite(char *, int); +int TerminalRead(unsigned char *, int); +int TerminalAutoFlush(void); +int TerminalSpecialChars(int); +void TerminalFlushOutput(void); +void TerminalSaveState(void); +cc_t *tcval(int); +void TerminalDefaultChars(void); +void TerminalRestoreState(void); +void TerminalNewMode(int); +void TerminalSpeeds(long *, long *); +int TerminalWindowSize(long *, long *); +int NetClose(int); +void NetNonblockingIO(int, int); +void NetSigIO(int, int); +void NetSetPgrp(int); +void sys_telnet_init(void); +int process_rings(int , int , int , int , int , int); + +/* telnet.c */ +void init_telnet(void); +void send_do(int, int ); +void send_dont(int, int ); +void send_will(int, int ); +void send_wont(int, int ); +void willoption(int); +void wontoption(int); +char **mklist(char *, char *); +int is_unique(char *, char **, char **); +int setup_term(char *, int, int *); +char *gettermname(void); +void lm_will(unsigned char *, int); +void lm_wont(unsigned char *, int); +void lm_do(unsigned char *, int); +void lm_dont(unsigned char *, int); +void lm_mode(unsigned char *, int, int ); +void slc_init(void); +void slcstate(void); +void slc_mode_export(int); +void slc_mode_import(int); +void slc_import(int); +void slc_export(void); +void slc(unsigned char *, int); +void slc_check(void); +void slc_start_reply(void); +void slc_add_reply(unsigned int, unsigned int, cc_t); +void slc_end_reply(void); +int slc_update(void); +void env_opt(unsigned char *, int); +void env_opt_start(void); +void env_opt_start_info(void); +void env_opt_add(unsigned char *); +int opt_welldefined(const char *); +void env_opt_end(int); +int telrcv(void); +int rlogin_susp(void); +int Scheduler(int); +void telnet(const char *); +void xmitAO(void); +void xmitEL(void); +void xmitEC(void); +int dosynch(char *); +int get_status(char *); +void intp(void); +void sendbrk(void); +void sendabort(void); +void sendsusp(void); +void sendeof(void); +void sendayt(void); +void sendnaws(void); +void tel_enter_binary(int); +void tel_leave_binary(int); + +/* terminal.c */ +void init_terminal(void); +int ttyflush(int); +int getconnmode(void); +void setconnmode(int); +void setcommandmode(void); + +/* utilities.c */ +void upcase(char *); +int SetSockOpt(int, int, int, int); +void SetNetTrace(char *); +void Dump(int, unsigned char *, int); +void printoption(const char *, int, int ); +void optionstatus(void); +void printsub(int, unsigned char *, int); +void EmptyTerminal(void); +void SetForExit(void); +void Exit(int) __attribute__((__noreturn__)); +void ExitString(const char *, int) __attribute__((__noreturn__)); + + +extern struct termios new_tc; + +# define termEofChar new_tc.c_cc[VEOF] +# define termEraseChar new_tc.c_cc[VERASE] +# define termIntChar new_tc.c_cc[VINTR] +# define termKillChar new_tc.c_cc[VKILL] +# define termQuitChar new_tc.c_cc[VQUIT] + +# define termSuspChar new_tc.c_cc[VSUSP] +# define termFlushChar new_tc.c_cc[VDISCARD] +# define termWerasChar new_tc.c_cc[VWERASE] +# define termRprntChar new_tc.c_cc[VREPRINT] +# define termLiteralNextChar new_tc.c_cc[VLNEXT] +# define termStartChar new_tc.c_cc[VSTART] +# define termStopChar new_tc.c_cc[VSTOP] +# define termForw1Char new_tc.c_cc[VEOL] +# define termForw2Char new_tc.c_cc[VEOL] +# define termAytChar new_tc.c_cc[VSTATUS] + +# define termEofCharp &termEofChar +# define termEraseCharp &termEraseChar +# define termIntCharp &termIntChar +# define termKillCharp &termKillChar +# define termQuitCharp &termQuitChar +# define termSuspCharp &termSuspChar +# define termFlushCharp &termFlushChar +# define termWerasCharp &termWerasChar +# define termRprntCharp &termRprntChar +# define termLiteralNextCharp &termLiteralNextChar +# define termStartCharp &termStartChar +# define termStopCharp &termStopChar +# define termForw1Charp &termForw1Char +# define termForw2Charp &termForw2Char +# define termAytCharp &termAytChar + + +/* Tn3270 section */ +#if defined(TN3270) + +extern int + HaveInput, /* Whether an asynchronous I/O indication came in */ + noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */ + noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */ + sigiocount, /* Count of SIGIO receptions */ + shell_active; /* Subshell is active */ + +extern char + *Ibackp, /* Oldest byte of 3270 data */ + Ibuf[], /* 3270 buffer */ + *Ifrontp, /* Where next 3270 byte goes */ + tline[200], + *transcom; /* Transparent command */ + +/* tn3270.c */ +void init_3270(void); +int DataToNetwork(char *, int, int); +void inputAvailable(int); +void outputPurge(void); +int DataToTerminal(char *, int); +int Push3270(void); +void Finish3270(void); +void StringToTerminal(char *); +int _putchar(int); +void SetIn3270(void); +int tn3270_ttype(void); +int settranscom(int, char *[]); +int shell_continue(void); +int DataFromTerminal(char *, int); +int DataFromNetwork(char *, int, int); +void ConnectScreen(void); +int DoTerminalOutput(void); + +#endif /* defined(TN3270) */ diff --git a/usr.bin/telnet/general.h b/usr.bin/telnet/general.h new file mode 100644 index 000000000..27cf45865 --- /dev/null +++ b/usr.bin/telnet/general.h @@ -0,0 +1,43 @@ +/* $NetBSD: general.h,v 1.6 2003/08/07 11:16:09 agc Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)general.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Some general definitions. + */ + + +#define numberof(x) (sizeof x/sizeof x[0]) +#define highestof(x) (numberof(x)-1) + +#define ClearElement(x) memset((char *)&x, 0, sizeof x) +#define ClearArray(x) memset((char *)x, 0, sizeof x) diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c new file mode 100644 index 000000000..ac4551c54 --- /dev/null +++ b/usr.bin/telnet/main.c @@ -0,0 +1,361 @@ +/* $NetBSD: main.c,v 1.29 2012/03/20 20:34:59 matt Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1990, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: main.c,v 1.29 2012/03/20 20:34:59 matt Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include + +#include "ring.h" +#include "externs.h" +#include "defines.h" +#ifdef AUTHENTICATION +#include +#endif +#ifdef ENCRYPTION +#include +#endif + +/* These values need to be the same as defined in libtelnet/kerberos5.c */ +/* Either define them in both places, or put in some common header file. */ +#define OPTS_FORWARD_CREDS 0x00000002 +#define OPTS_FORWARDABLE_CREDS 0x00000001 + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +char *ipsec_policy_in = NULL; +char *ipsec_policy_out = NULL; +#endif + +int family = AF_UNSPEC; + +int main(int, char *[]); + +/* + * Initialize variables. + */ +void +tninit(void) +{ + init_terminal(); + + init_network(); + + init_telnet(); + + init_sys(); + +#ifdef TN3270 + init_3270(); +#endif +} + + void +usage(void) +{ + fprintf(stderr, "usage: %s %s%s%s%s\n", + prompt, +#ifdef AUTHENTICATION + "[-4] [-6] [-8] [-E] [-K] [-L] [-N] [-S tos] [-X atype] [-a] [-c]", + "\n\t[-d] [-e char] [-k realm] [-l user] [-f/-F] [-n tracefile] ", +#else + "[-4] [-6] [-8] [-E] [-L] [-N] [-S tos] [-a] [-c] [-d] [-e char]", + "\n\t[-l user] [-n tracefile] ", +#endif +#ifdef TN3270 +# ifdef AUTHENTICATION + "[-noasynch] [-noasynctty]\n\t[-noasyncnet] [-r] [-t transcom] ", +# else + "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t[-t transcom]", +# endif +#else + "[-r] ", +#endif +#ifdef ENCRYPTION + "[-x] " +#endif +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + "\n\t[-P policy] [host-name [port]]" +#else + "\n\t[host-name [port]]" +#endif + ); + exit(1); +} + +/* + * main. Parse arguments, invoke the protocol or command parser. + */ + + +int +main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + int ch; + char *user; +#ifdef FORWARD + extern int forward_flags; +#endif /* FORWARD */ + + tninit(); /* Clear out things */ + + TerminalSaveState(); + + if ((prompt = strrchr(argv[0], '/')) != NULL) + ++prompt; + else + prompt = argv[0]; + + user = NULL; + + rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; + autologin = -1; + +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) +#define IPSECOPT "P:" +#else +#define IPSECOPT +#endif + while ((ch = getopt(argc, argv, "468EKLNS:X:acde:fFk:l:n:rt:x" + IPSECOPT)) != -1) { +#undef IPSECOPT + switch(ch) { + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; + case '8': + eight = 3; /* binary output and input */ + break; + case 'E': + rlogin = escape = _POSIX_VDISABLE; + break; + case 'K': +#ifdef AUTHENTICATION + autologin = 0; +#endif + break; + case 'L': + eight |= 2; /* binary output only */ + break; + case 'N': + doaddrlookup = 0; + break; + case 'S': + { + fprintf(stderr, + "%s: Warning: -S ignored, no parsetos() support.\n", + prompt); + } + break; + case 'X': +#ifdef AUTHENTICATION + auth_disable_name(optarg); +#endif + break; + case 'a': + autologin = 1; + break; + case 'c': + skiprc = 1; + break; + case 'd': + telnet_debug = 1; + break; + case 'e': + set_escape_char(optarg); + break; + case 'f': +#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) + if (forward_flags & OPTS_FORWARD_CREDS) { + fprintf(stderr, + "%s: Only one of -f and -F allowed.\n", + prompt); + usage(); + } + forward_flags |= OPTS_FORWARD_CREDS; +#else + fprintf(stderr, + "%s: Warning: -f ignored, no Kerberos V5 support.\n", + prompt); +#endif + break; + case 'F': +#if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) + if (forward_flags & OPTS_FORWARD_CREDS) { + fprintf(stderr, + "%s: Only one of -f and -F allowed.\n", + prompt); + usage(); + } + forward_flags |= OPTS_FORWARD_CREDS; + forward_flags |= OPTS_FORWARDABLE_CREDS; +#else + fprintf(stderr, + "%s: Warning: -F ignored, no Kerberos V5 support.\n", + prompt); +#endif + break; + case 'k': + fprintf(stderr, + "%s: Warning: -k ignored, no Kerberos V4 support.\n", + prompt); + break; + case 'l': + if(autologin == 0) { + autologin = -1; + } + user = optarg; + break; + case 'n': +#ifdef TN3270 + /* distinguish between "-n oasynch" and "-noasynch" */ + if (argv[optind - 1][0] == '-' && argv[optind - 1][1] + == 'n' && argv[optind - 1][2] == 'o') { + if (!strcmp(optarg, "oasynch")) { + noasynchtty = 1; + noasynchnet = 1; + } else if (!strcmp(optarg, "oasynchtty")) + noasynchtty = 1; + else if (!strcmp(optarg, "oasynchnet")) + noasynchnet = 1; + } else +#endif /* defined(TN3270) */ + SetNetTrace(optarg); + break; + case 'r': + rlogin = '~'; + break; + case 't': +#ifdef TN3270 + (void)strlcpy(tline, optarg, sizeof(tline)); + transcom = tline; +#else + fprintf(stderr, + "%s: Warning: -t ignored, no TN3270 support.\n", + prompt); +#endif + break; + case 'x': +#ifdef ENCRYPTION + encrypt_auto(1); + decrypt_auto(1); +#else /* ENCRYPTION */ + fprintf(stderr, + "%s: Warning: -x ignored, no ENCRYPT support.\n", + prompt); +#endif /* ENCRYPTION */ + break; +#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC) + case 'P': + if (!strncmp("in", optarg, 2)) + ipsec_policy_in = strdup(optarg); + else if (!strncmp("out", optarg, 3)) + ipsec_policy_out = strdup(optarg); + else + usage(); + break; +#endif + case '?': + default: + usage(); + /* NOTREACHED */ + } + } + + if (autologin == -1) { /* esc@magic.fi; force */ +#if defined(AUTHENTICATION) + autologin = 1; +#endif +#if defined(ENCRYPTION) + encrypt_auto(1); + decrypt_auto(1); +#endif + } + + if (autologin == -1) + autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1; + + argc -= optind; + argv += optind; + + if (argc) { + static char ml[] = "-l"; + char *args[7]; + char ** volatile argp; /* avoid longjmp clobbering */ + + argp = args; + if (argc > 2) + usage(); + *argp++ = prompt; + if (user) { + *argp++ = ml; + *argp++ = user; + } + *argp++ = argv[0]; /* host */ + if (argc > 1) + *argp++ = argv[1]; /* port */ + *argp = 0; + + if (setjmp(toplevel) != 0) + Exit(0); + if (tn(argp - args, args) == 1) + return (0); + else + return (1); + } + (void)setjmp(toplevel); + for (;;) { +#ifdef TN3270 + if (shell_active) + shell_continue(); + else +#endif + command(1, 0, 0); + } +} diff --git a/usr.bin/telnet/network.c b/usr.bin/telnet/network.c new file mode 100644 index 000000000..d8fe3e0bd --- /dev/null +++ b/usr.bin/telnet/network.c @@ -0,0 +1,179 @@ +/* $NetBSD: network.c,v 1.17 2004/03/20 23:26:05 heas Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)network.c 8.2 (Berkeley) 12/15/93"; +#else +__RCSID("$NetBSD: network.c,v 1.17 2004/03/20 23:26:05 heas Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "ring.h" +#include "defines.h" +#include "externs.h" + +Ring netoring, netiring; +unsigned char netobuf[2*BUFSIZ], netibuf[BUFSIZ]; + +/* + * Initialize internal network data structures. + */ + +void +init_network(void) +{ + if (ring_init(&netoring, netobuf, sizeof netobuf) != 1) { + exit(1); + } + if (ring_init(&netiring, netibuf, sizeof netibuf) != 1) { + exit(1); + } + NetTrace = stdout; +} + + +/* + * Check to see if any out-of-band data exists on a socket (for + * Telnet "synch" processing). + */ + +int +stilloob(void) +{ + struct pollfd set[1]; + int value; + + set[0].fd = net; + set[0].events = POLLPRI; + do { + value = poll(set, 1, 0); + } while ((value == -1) && (errno == EINTR)); + + if (value < 0) { + perror("poll"); + (void) quit(0, NULL); + /* NOTREACHED */ + } + if (set[0].revents & POLLPRI) { + return 1; + } else { + return 0; + } +} + + +/* + * setneturg() + * + * Sets "neturg" to the current location. + */ + +void +setneturg(void) +{ + ring_mark(&netoring); +} + + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + * + * The return value indicates whether we did any + * useful work. + */ + + +int +netflush(void) +{ + int n, n1; + +#ifdef ENCRYPTION + if (encrypt_output) + ring_encrypt(&netoring, encrypt_output); +#endif /* ENCRYPTION */ + if ((n1 = n = ring_full_consecutive(&netoring)) > 0) { + if (!ring_at_mark(&netoring)) { + n = send(net, (char *)netoring.consume, n, 0); /* normal write */ + } else { + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + n = send(net, (char *)netoring.consume, 1, MSG_OOB);/* URGENT data */ + } + } + if (n < 0) { + if (errno != ENOBUFS && errno != EWOULDBLOCK) { + setcommandmode(); + perror(hostname); + (void)NetClose(net); + ring_clear_mark(&netoring); + ExitString("Connection closed by foreign host.\n", 1); + /*NOTREACHED*/ + } + n = 0; + } + if (netdata && n) { + Dump('>', netoring.consume, n); + } + if (n) { + ring_consumed(&netoring, n); + /* + * If we sent all, and more to send, then recurse to pick + * up the other half. + */ + if ((n1 == n) && ring_full_consecutive(&netoring)) { + (void) netflush(); + } + return 1; + } else { + return 0; + } +} diff --git a/usr.bin/telnet/ring.c b/usr.bin/telnet/ring.c new file mode 100644 index 000000000..71d5b51dc --- /dev/null +++ b/usr.bin/telnet/ring.c @@ -0,0 +1,339 @@ +/* $NetBSD: ring.c,v 1.13 2003/08/07 11:16:10 agc Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ring.c 8.2 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: ring.c,v 1.13 2003/08/07 11:16:10 agc Exp $"); +#endif +#endif /* not lint */ + +/* + * This defines a structure for a ring buffer. + * + * The circular buffer has two parts: + *((( + * full: [consume, supply) + * empty: [supply, consume) + *]]] + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ring.h" +#include "general.h" + +/* Internal macros */ + +#if !defined(MIN) +#define MIN(a,b) (((a)<(b))? (a):(b)) +#endif /* !defined(MIN) */ + +#define ring_subtract(d,a,b) (((a)-(b) >= 0)? \ + (a)-(b): (((a)-(b))+(d)->size)) + +#define ring_increment(d,a,c) (((a)+(c) < (d)->top)? \ + (a)+(c) : (((a)+(c))-(d)->size)) + +#define ring_decrement(d,a,c) (((a)-(c) >= (d)->bottom)? \ + (a)-(c) : (((a)-(c))-(d)->size)) + + +/* + * The following is a clock, used to determine full, empty, etc. + * + * There is some trickiness here. Since the ring buffers are initialized + * to ZERO on allocation, we need to make sure, when interpreting the + * clock, that when the times are EQUAL, then the buffer is FULL. + */ +static u_long ring_clock = 0; + + +#define ring_empty(d) (((d)->consume == (d)->supply) && \ + ((d)->consumetime >= (d)->supplytime)) +#define ring_full(d) (((d)->supply == (d)->consume) && \ + ((d)->supplytime > (d)->consumetime)) + + + + + +/* Buffer state transition routines */ + +int +ring_init(Ring *ring, unsigned char *buffer, int count) +{ + memset((char *)ring, 0, sizeof *ring); + + ring->size = count; + + ring->supply = ring->consume = ring->bottom = buffer; + + ring->top = ring->bottom+ring->size; + +#ifdef ENCRYPTION + ring->clearto = 0; +#endif /* ENCRYPTION */ + + return 1; +} + +/* Mark routines */ + +/* + * Mark the most recently supplied byte. + */ + +void +ring_mark(Ring *ring) +{ + ring->mark = ring_decrement(ring, ring->supply, 1); +} + +/* + * Is the ring pointing to the mark? + */ + +int +ring_at_mark(Ring *ring) +{ + if (ring->mark == ring->consume) { + return 1; + } else { + return 0; + } +} + +/* + * Clear any mark set on the ring. + */ + +void +ring_clear_mark(Ring *ring) +{ + ring->mark = 0; +} + +/* + * Add characters from current segment to ring buffer. + */ +void +ring_supplied(Ring *ring, int count) +{ + ring->supply = ring_increment(ring, ring->supply, count); + ring->supplytime = ++ring_clock; +} + +/* + * We have just consumed "c" bytes. + */ +void +ring_consumed(Ring *ring, int count) +{ + if (count == 0) /* don't update anything */ + return; + + if (ring->mark && + (ring_subtract(ring, ring->mark, ring->consume) < count)) { + ring->mark = 0; + } +#ifdef ENCRYPTION + if (ring->consume < ring->clearto && + ring->clearto <= ring->consume + count) + ring->clearto = 0; + else if (ring->consume + count > ring->top && + ring->bottom <= ring->clearto && + ring->bottom + ((ring->consume + count) - ring->top)) + ring->clearto = 0; +#endif /* ENCRYPTION */ + ring->consume = ring_increment(ring, ring->consume, count); + ring->consumetime = ++ring_clock; + /* + * Try to encourage "ring_empty_consecutive()" to be large. + */ + if (ring_empty(ring)) { + ring->consume = ring->supply = ring->bottom; + } +} + + + +/* Buffer state query routines */ + + +/* Number of bytes that may be supplied */ +int +ring_empty_count(Ring *ring) +{ + if (ring_empty(ring)) { /* if empty */ + return ring->size; + } else { + return ring_subtract(ring, ring->consume, ring->supply); + } +} + +/* number of CONSECUTIVE bytes that may be supplied */ +int +ring_empty_consecutive(Ring *ring) +{ + if ((ring->consume < ring->supply) || ring_empty(ring)) { + /* + * if consume is "below" supply, or empty, then + * return distance to the top + */ + return ring_subtract(ring, ring->top, ring->supply); + } else { + /* + * else, return what we may. + */ + return ring_subtract(ring, ring->consume, ring->supply); + } +} + +/* Return the number of bytes that are available for consuming + * (but don't give more than enough to get to cross over set mark) + */ + +int +ring_full_count(Ring *ring) +{ + if ((ring->mark == 0) || (ring->mark == ring->consume)) { + if (ring_full(ring)) { + return ring->size; /* nothing consumed, but full */ + } else { + return ring_subtract(ring, ring->supply, ring->consume); + } + } else { + return ring_subtract(ring, ring->mark, ring->consume); + } +} + +/* + * Return the number of CONSECUTIVE bytes available for consuming. + * However, don't return more than enough to cross over set mark. + */ +int +ring_full_consecutive(Ring *ring) +{ + if ((ring->mark == 0) || (ring->mark == ring->consume)) { + if ((ring->supply < ring->consume) || ring_full(ring)) { + return ring_subtract(ring, ring->top, ring->consume); + } else { + return ring_subtract(ring, ring->supply, ring->consume); + } + } else { + if (ring->mark < ring->consume) { + return ring_subtract(ring, ring->top, ring->consume); + } else { /* Else, distance to mark */ + return ring_subtract(ring, ring->mark, ring->consume); + } + } +} + +/* + * Move data into the "supply" portion of of the ring buffer. + */ +void +ring_supply_data(Ring *ring, unsigned char *buffer, int count) +{ + int i; + + while (count) { + i = MIN(count, ring_empty_consecutive(ring)); + memmove(ring->supply, buffer, i); + ring_supplied(ring, i); + count -= i; + buffer += i; + } +} + +#ifdef notdef + +/* + * Move data from the "consume" portion of the ring buffer + */ +void +ring_consume_data(Ring *ring, unsigned char *buffer, int count) +{ + int i; + + while (count) { + i = MIN(count, ring_full_consecutive(ring)); + memmove(buffer, ring->consume, i); + ring_consumed(ring, i); + count -= i; + buffer += i; + } +} +#endif + +#ifdef ENCRYPTION +void +ring_encrypt(Ring *ring, void (*encryptor)(unsigned char *, int)) +{ + unsigned char *s, *c; + + if (ring_empty(ring) || ring->clearto == ring->supply) + return; + + if (!(c = ring->clearto)) + c = ring->consume; + + s = ring->supply; + + if (s <= c) { + (*encryptor)(c, ring->top - c); + (*encryptor)(ring->bottom, s - ring->bottom); + } else + (*encryptor)(c, s - c); + + ring->clearto = ring->supply; +} + +void +ring_clearto(Ring *ring) +{ + + if (!ring_empty(ring)) + ring->clearto = ring->supply; + else + ring->clearto = 0; +} +#endif /* ENCRYPTION */ diff --git a/usr.bin/telnet/ring.h b/usr.bin/telnet/ring.h new file mode 100644 index 000000000..86f03cc52 --- /dev/null +++ b/usr.bin/telnet/ring.h @@ -0,0 +1,104 @@ +/* $NetBSD: ring.h,v 1.10 2003/08/07 11:16:10 agc Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)ring.h 8.1 (Berkeley) 6/6/93 + */ + +#include + +/* + * This defines a structure for a ring buffer. + * + * The circular buffer has two parts: + *((( + * full: [consume, supply) + * empty: [supply, consume) + *]]] + * + */ +typedef struct { + unsigned char *consume, /* where data comes out of */ + *supply, /* where data comes in to */ + *bottom, /* lowest address in buffer */ + *top, /* highest address+1 in buffer */ + *mark; /* marker (user defined) */ +#ifdef ENCRYPTION + unsigned char *clearto; /* Data to this point is clear text */ + unsigned char *encryyptedto; /* Data is encrypted to here */ +#endif /* ENCRYPTION */ + int size; /* size in bytes of buffer */ + u_long consumetime, /* help us keep straight full, empty, etc. */ + supplytime; +} Ring; + +/* Ring buffer structures which are shared */ + +extern Ring + netoring, + netiring, + ttyoring, + ttyiring; + +/* Here are some functions and macros to deal with the ring buffer */ + +/* Initialization routine */ +extern int + ring_init(Ring *ring, unsigned char *buffer, int count); + +/* Data movement routines */ +extern void + ring_supply_data(Ring *ring, unsigned char *buffer, int count); +#ifdef notdef +extern void + ring_consume_data(Ring *ring, unsigned char *buffer, int count); +#endif + +/* Buffer state transition routines */ +extern void + ring_supplied(Ring *ring, int count), + ring_consumed(Ring *ring, int count); + +/* Buffer state query routines */ +extern int + ring_empty_count(Ring *ring), + ring_empty_consecutive(Ring *ring), + ring_full_count(Ring *ring), + ring_full_consecutive(Ring *ring), + ring_at_mark(Ring *ring); + +#ifdef ENCRYPTION +extern void + ring_encrypt(Ring *ring, void (*func)(unsigned char *, int)), + ring_clearto(Ring *ring); +#endif /* ENCRYPTION */ + +extern void + ring_clear_mark(Ring *ring), + ring_mark(Ring *ring); diff --git a/usr.bin/telnet/sys_bsd.c b/usr.bin/telnet/sys_bsd.c new file mode 100644 index 000000000..b237de56c --- /dev/null +++ b/usr.bin/telnet/sys_bsd.c @@ -0,0 +1,739 @@ +/* $NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +from: static char sccsid[] = "@(#)sys_bsd.c 8.4 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: sys_bsd.c,v 1.33 2012/01/09 16:08:55 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * The following routines try to encapsulate what is system dependent + * (at least between 4.x and dos) which is used in telnet.c. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ring.h" +#include "defines.h" +#include "externs.h" +#include "types.h" + +#define SIG_FUNC_RET void + +SIG_FUNC_RET susp(int); +SIG_FUNC_RET ayt(int); + +SIG_FUNC_RET intr(int); +SIG_FUNC_RET intr2(int); +SIG_FUNC_RET sendwin(int); + + +int + tout, /* Output file descriptor */ + tin, /* Input file descriptor */ + net; + +struct termios old_tc = { .c_iflag = 0 }; +extern struct termios new_tc; + +# ifndef TCSANOW +# ifdef TCSETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, (char *)t) +# define cfgetospeed(ptr) ((ptr)->c_cflag&CBAUD) +# ifdef CIBAUD +# define cfgetispeed(ptr) (((ptr)->c_cflag&CIBAUD) >> IBSHIFT) +# else +# define cfgetispeed(ptr) cfgetospeed(ptr) +# endif +# endif /* TCSANOW */ + + +void +init_sys(void) +{ + tout = fileno(stdout); + tin = fileno(stdin); + + errno = 0; +} + + +int +TerminalWrite(char *buf, int n) +{ + return write(tout, buf, n); +} + +int +TerminalRead(unsigned char *buf, int n) +{ + return read(tin, buf, n); +} + +/* + * + */ + +int +TerminalAutoFlush(void) +{ + return 1; +} + +#ifdef KLUDGELINEMODE +extern int kludgelinemode; +#endif +/* + * TerminalSpecialChars() + * + * Look at an input character to see if it is a special character + * and decide what to do. + * + * Output: + * + * 0 Don't add this character. + * 1 Do add this character + */ + +int +TerminalSpecialChars(int c) +{ + if (c == termIntChar) { + intp(); + return 0; + } else if (c == termQuitChar) { +#ifdef KLUDGELINEMODE + if (kludgelinemode) + sendbrk(); + else +#endif + sendabort(); + return 0; + } else if (c == termEofChar) { + if (my_want_state_is_will(TELOPT_LINEMODE)) { + sendeof(); + return 0; + } + return 1; + } else if (c == termSuspChar) { + sendsusp(); + return(0); + } else if (c == termFlushChar) { + xmitAO(); /* Transmit Abort Output */ + return 0; + } else if (!MODE_LOCAL_CHARS(globalmode)) { + if (c == termKillChar) { + xmitEL(); + return 0; + } else if (c == termEraseChar) { + xmitEC(); /* Transmit Erase Character */ + return 0; + } + } + return 1; +} + + +/* + * Flush output to the terminal + */ + +void +TerminalFlushOutput(void) +{ + int com = 0; + (void) ioctl(fileno(stdout), TIOCFLUSH, &com); +} + +void +TerminalSaveState(void) +{ + tcgetattr(0, &old_tc); + + new_tc = old_tc; +} + +cc_t * +tcval(int func) +{ + switch(func) { + case SLC_IP: return(&termIntChar); + case SLC_ABORT: return(&termQuitChar); + case SLC_EOF: return(&termEofChar); + case SLC_EC: return(&termEraseChar); + case SLC_EL: return(&termKillChar); + case SLC_XON: return(&termStartChar); + case SLC_XOFF: return(&termStopChar); + case SLC_FORW1: return(&termForw1Char); + case SLC_FORW2: return(&termForw2Char); + case SLC_AO: return(&termFlushChar); + case SLC_SUSP: return(&termSuspChar); + case SLC_EW: return(&termWerasChar); + case SLC_RP: return(&termRprntChar); + case SLC_LNEXT: return(&termLiteralNextChar); + case SLC_AYT: return(&termAytChar); + + case SLC_SYNCH: + case SLC_BRK: + case SLC_EOR: + default: + return((cc_t *)0); + } +} + +void +TerminalDefaultChars(void) +{ + memmove(new_tc.c_cc, old_tc.c_cc, sizeof(old_tc.c_cc)); +} + +#ifdef notdef +void +TerminalRestoreState(void) +{ +} +#endif + +/* + * TerminalNewMode - set up terminal to a specific mode. + * MODE_ECHO: do local terminal echo + * MODE_FLOW: do local flow control + * MODE_TRAPSIG: do local mapping to TELNET IAC sequences + * MODE_EDIT: do local line editing + * + * Command mode: + * MODE_ECHO|MODE_EDIT|MODE_FLOW|MODE_TRAPSIG + * local echo + * local editing + * local xon/xoff + * local signal mapping + * + * Linemode: + * local/no editing + * Both Linemode and Single Character mode: + * local/remote echo + * local/no xon/xoff + * local/no signal mapping + */ + + +void +TerminalNewMode(int f) +{ + static int prevmode = 0; + struct termios tmp_tc; + int onoff; + int old; + cc_t esc; + + globalmode = f&~MODE_FORCE; + if (prevmode == f) + return; + + /* + * Write any outstanding data before switching modes + * ttyflush() returns 0 only when there is no more data + * left to write out, it returns -1 if it couldn't do + * anything at all, otherwise it returns 1 + the number + * of characters left to write. +#ifndef USE_TERMIO + * We would really like to ask the kernel to wait for the output + * to drain, like we can do with the TCSADRAIN, but we don't have + * that option. The only ioctl that waits for the output to + * drain, TIOCSETP, also flushes the input queue, which is NOT + * what we want (TIOCSETP is like TCSADFLUSH). +#endif + */ + old = ttyflush(SYNCHing|flushout); + if (old < 0 || old > 1) { + tcgetattr(tin, &tmp_tc); + do { + /* + * Wait for data to drain, then flush again. + */ + tcsetattr(tin, TCSADRAIN, &tmp_tc); + old = ttyflush(SYNCHing|flushout); + } while (old < 0 || old > 1); + } + + old = prevmode; + prevmode = f&~MODE_FORCE; + tmp_tc = new_tc; + + if (f&MODE_ECHO) { + tmp_tc.c_lflag |= ECHO; + tmp_tc.c_oflag |= ONLCR; + if (crlf) + tmp_tc.c_iflag |= ICRNL; + } else { + tmp_tc.c_lflag &= ~ECHO; + tmp_tc.c_oflag &= ~ONLCR; +# ifdef notdef + if (crlf) + tmp_tc.c_iflag &= ~ICRNL; +# endif + } + + if ((f&MODE_FLOW) == 0) { + tmp_tc.c_iflag &= ~(IXOFF|IXON); /* Leave the IXANY bit alone */ + } else { + if (restartany < 0) { + tmp_tc.c_iflag |= IXOFF|IXON; /* Leave the IXANY bit alone */ + } else if (restartany > 0) { + tmp_tc.c_iflag |= IXOFF|IXON|IXANY; + } else { + tmp_tc.c_iflag |= IXOFF|IXON; + tmp_tc.c_iflag &= ~IXANY; + } + } + + if ((f&MODE_TRAPSIG) == 0) { + tmp_tc.c_lflag &= ~ISIG; + localchars = 0; + } else { + tmp_tc.c_lflag |= ISIG; + localchars = 1; + } + + if (f&MODE_EDIT) { + tmp_tc.c_lflag |= ICANON; + } else { + tmp_tc.c_lflag &= ~ICANON; + tmp_tc.c_iflag &= ~ICRNL; + tmp_tc.c_cc[VMIN] = 1; + tmp_tc.c_cc[VTIME] = 0; + } + + if ((f&(MODE_EDIT|MODE_TRAPSIG)) == 0) { + tmp_tc.c_lflag &= ~IEXTEN; + } + + if (f&MODE_SOFT_TAB) { +# ifdef OXTABS + tmp_tc.c_oflag |= OXTABS; +# endif +# ifdef TABDLY + tmp_tc.c_oflag &= ~TABDLY; + tmp_tc.c_oflag |= TAB3; +# endif + } else { +# ifdef OXTABS + tmp_tc.c_oflag &= ~OXTABS; +# endif +# ifdef TABDLY + tmp_tc.c_oflag &= ~TABDLY; +# endif + } + + if (f&MODE_LIT_ECHO) { +# ifdef ECHOCTL + tmp_tc.c_lflag &= ~ECHOCTL; +# endif + } else { +# ifdef ECHOCTL + tmp_tc.c_lflag |= ECHOCTL; +# endif + } + + if (f == -1) { + onoff = 0; + } else { + if (f & MODE_INBIN) + tmp_tc.c_iflag &= ~ISTRIP; + else + tmp_tc.c_iflag |= ISTRIP; + if (f & MODE_OUTBIN) { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= CS8; + tmp_tc.c_oflag &= ~OPOST; + } else { + tmp_tc.c_cflag &= ~(CSIZE|PARENB); + tmp_tc.c_cflag |= old_tc.c_cflag & (CSIZE|PARENB); + tmp_tc.c_oflag |= OPOST; + } + onoff = 1; + } + + if (f != -1) { + (void) signal(SIGTSTP, susp); + (void) signal(SIGINFO, ayt); +#if defined(USE_TERMIO) && defined(NOKERNINFO) + tmp_tc.c_lflag |= NOKERNINFO; +#endif + /* + * We don't want to process ^Y here. It's just another + * character that we'll pass on to the back end. It has + * to process it because it will be processed when the + * user attempts to read it, not when we send it. + */ +# ifdef VDSUSP + tmp_tc.c_cc[VDSUSP] = (cc_t)(_POSIX_VDISABLE); +# endif + /* + * If the VEOL character is already set, then use VEOL2, + * otherwise use VEOL. + */ + esc = (rlogin != _POSIX_VDISABLE) ? rlogin : escape; + if ((tmp_tc.c_cc[VEOL] != esc) +# ifdef VEOL2 + && (tmp_tc.c_cc[VEOL2] != esc) +# endif + ) { + if (tmp_tc.c_cc[VEOL] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL] = esc; +# ifdef VEOL2 + else if (tmp_tc.c_cc[VEOL2] == (cc_t)(_POSIX_VDISABLE)) + tmp_tc.c_cc[VEOL2] = esc; +# endif + } + } else { + (void) signal(SIGINFO, (void (*)(int)) ayt_status); + (void) signal(SIGTSTP, SIG_DFL); + (void) sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); + tmp_tc = old_tc; + } + if (tcsetattr(tin, TCSADRAIN, &tmp_tc) < 0) + tcsetattr(tin, TCSANOW, &tmp_tc); + + ioctl(tin, FIONBIO, (char *)&onoff); + ioctl(tout, FIONBIO, (char *)&onoff); +#if defined(TN3270) + if (noasynchtty == 0) { + ioctl(tin, FIOASYNC, (char *)&onoff); + } +#endif /* defined(TN3270) */ + +} + +void +TerminalSpeeds(long *ispeed, long *ospeed) +{ + long in, out; + + out = cfgetospeed(&old_tc); + in = cfgetispeed(&old_tc); + if (in == 0) + in = out; + + *ispeed = in; + *ospeed = out; +} + +int +TerminalWindowSize(long *rows, long *cols) +{ + struct winsize ws; + + if (ioctl(fileno(stdin), TIOCGWINSZ, (char *)&ws) >= 0) { + *rows = ws.ws_row; + *cols = ws.ws_col; + return 1; + } + return 0; +} + +int +NetClose(int fd) +{ + return close(fd); +} + + +void +NetNonblockingIO(int fd, int onoff) +{ + ioctl(fd, FIONBIO, (char *)&onoff); +} + +#ifdef TN3270 +void +NetSigIO(int fd, int onoff) +{ + ioctl(fd, FIOASYNC, (char *)&onoff); /* hear about input */ +} + +void +NetSetPgrp(int fd) +{ + int myPid; + + myPid = getpid(); + fcntl(fd, F_SETOWN, myPid); +} +#endif /*defined(TN3270)*/ + +/* + * Various signal handling routines. + */ + +/* ARGSUSED */ +SIG_FUNC_RET +intr(int sig) +{ + if (localchars) { + intp(); + return; + } + setcommandmode(); + longjmp(toplevel, -1); +} + +/* ARGSUSED */ +SIG_FUNC_RET +intr2(int sig) +{ + if (localchars) { +#ifdef KLUDGELINEMODE + if (kludgelinemode) + sendbrk(); + else +#endif + sendabort(); + return; + } +} + +/* ARGSUSED */ +SIG_FUNC_RET +susp(int sig) +{ + if ((rlogin != _POSIX_VDISABLE) && rlogin_susp()) + return; + if (localchars) + sendsusp(); +} + +/* ARGSUSED */ +SIG_FUNC_RET +sendwin(int sig) +{ + if (connected) { + sendnaws(); + } +} + +/* ARGSUSED */ +SIG_FUNC_RET +ayt(int sig) +{ + if (connected) + sendayt(); + else + ayt_status(); +} + + +void +sys_telnet_init(void) +{ + (void) signal(SIGINT, intr); + (void) signal(SIGQUIT, intr2); + (void) signal(SIGPIPE, SIG_IGN); + (void) signal(SIGWINCH, sendwin); + (void) signal(SIGTSTP, susp); + (void) signal(SIGINFO, ayt); + + setconnmode(0); + + NetNonblockingIO(net, 1); + +#ifdef TN3270 + if (noasynchnet == 0) { /* DBX can't handle! */ + NetSigIO(net, 1); + NetSetPgrp(net); + } +#endif /* defined(TN3270) */ + + if (SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1) == -1) { + perror("SetSockOpt"); + } +} + +/* + * Process rings - + * + * This routine tries to fill up/empty our various rings. + * + * The parameter specifies whether this is a poll operation, + * or a block-until-something-happens operation. + * + * The return value is 1 if something happened, 0 if not, < 0 if an + * error occurred. + */ + +int +process_rings(int netin, int netout, int netex, int ttyin, int ttyout, + int dopoll) /* If 0, then block until something to do */ +{ + struct pollfd set[3]; + int c; + /* One wants to be a bit careful about setting returnValue + * to one, since a one implies we did some useful work, + * and therefore probably won't be called to block next + * time (TN3270 mode only). + */ + int returnValue = 0; + + set[0].fd = net; + set[0].events = (netout ? POLLOUT : 0) | (netin ? POLLIN : 0) | + (netex ? POLLPRI : 0); + set[1].fd = tout; + set[1].events = ttyout ? POLLOUT : 0; + set[2].fd = tin; + set[2].events = ttyin ? POLLIN : 0; + + if ((c = poll(set, 3, dopoll ? 0 : INFTIM)) < 0) { + if (c == -1) { + /* + * we can get EINTR if we are in line mode, + * and the user does an escape (TSTP), or + * some other signal generator. + */ + if (errno == EINTR) { + return 0; + } +#ifdef TN3270 + /* + * we can get EBADF if we were in transparent + * mode, and the transcom process died. + */ + if (errno == EBADF) + return 0; +#endif /* defined(TN3270) */ + /* I don't like this, does it ever happen? */ + printf("sleep(5) from telnet, after poll\r\n"); + sleep(5); + } + return 0; + } + + /* + * Any urgent data? + */ + if (set[0].revents & POLLPRI) { + SYNCHing = 1; + (void) ttyflush(1); /* flush already enqueued data */ + } + + /* + * Something to read from the network... + */ + if (set[0].revents & POLLIN) { + int canread; + + canread = ring_empty_consecutive(&netiring); + c = recv(net, (char *)netiring.supply, canread, 0); + if (c < 0 && errno == EWOULDBLOCK) { + c = 0; + } else if (c <= 0) { + return -1; + } + if (netdata) { + Dump('<', netiring.supply, c); + } + if (c) + ring_supplied(&netiring, c); + returnValue = 1; + } + + /* + * Something to read from the tty... + */ + if (set[2].revents & POLLIN) { + c = TerminalRead(ttyiring.supply, ring_empty_consecutive(&ttyiring)); + if (c < 0 && errno == EIO) + c = 0; + if (c < 0 && errno == EWOULDBLOCK) { + c = 0; + } else { + if (c < 0) { + return -1; + } + if (c == 0) { + /* must be an EOF... */ + if (MODE_LOCAL_CHARS(globalmode) && isatty(tin)) { + *ttyiring.supply = termEofChar; + c = 1; + } else { + clienteof = 1; + shutdown(net, 1); + return 0; + } + } + if (termdata) { + Dump('<', ttyiring.supply, c); + } + ring_supplied(&ttyiring, c); + } + returnValue = 1; /* did something useful */ + } + + if (set[0].revents & POLLOUT) { + returnValue |= netflush(); + } + + if (set[1].revents & (POLLHUP|POLLNVAL)) + return(-1); + + if (set[1].revents & POLLOUT) { + returnValue |= (ttyflush(SYNCHing|flushout) > 0); + } + + return returnValue; +} diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1 new file mode 100644 index 000000000..fa946c3ec --- /dev/null +++ b/usr.bin/telnet/telnet.1 @@ -0,0 +1,1414 @@ +.\" $NetBSD: telnet.1,v 1.33 2012/04/08 22:00:39 wiz Exp $ +.\" +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)telnet.1 8.4 (Berkeley) 2/3/94 +.\" +.Dd October 28, 2003 +.Dt TELNET 1 +.Os +.Sh NAME +.Nm telnet +.Nd user interface to the +.Tn TELNET +protocol +.Sh SYNOPSIS +.Nm +.Op Fl 468acdEFfKLNrx +.Op Fl e Ar escapechar +.Op Fl k Ar realm +.Op Fl l Ar user +.Op Fl n Ar tracefile +.Op Fl P Ar policy +.Op Fl S Ar tos +.Op Fl X Ar authtype +.Oo +.Ar host +.Op Ar port +.Oc +.Sh DESCRIPTION +The +.Nm +command +is used to communicate with another host using the +.Tn TELNET +protocol. +If +.Nm +is invoked without the +.Ar host +argument, it enters command mode, +indicated by its prompt +.Pq Nm telnet\&\*[Gt] . +In this mode, it accepts and executes the commands listed below. +If it is invoked with arguments, it performs an +.Ic open +command with those arguments. +.Pp +Options: +.Bl -tag -width indent +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl 8 +Specifies an 8-bit data path. +This causes an attempt to +negotiate the +.Dv TELNET BINARY +option on both input and output. +.It Fl E +Stops any character from being recognized as an escape character. +.It Fl F +If Kerberos V5 authentication is being used, the +.Fl F +option allows the local credentials to be forwarded +to the remote system, including any credentials that +have already been forwarded into the local environment. +.It Fl K +Specifies no automatic login to the remote system. +.It Fl L +Specifies an 8-bit data path on output. +This causes the +BINARY option to be negotiated on output. +.It Fl N +Numeric host address. +No attempt will be made to look up +symbolic names for host addresses. +.It Fl S Ar tos +Sets the IP type-of-service (TOS) option for the telnet +connection to the value +.Ar tos , +which can be a numeric TOS value +or, on systems that support it, a symbolic +TOS name found in the /etc/iptos file. +.It Fl X Ar atype +Disables the +.Ar atype +type of authentication. +.It Fl a +Attempt automatic login. +Currently, this sends the user name via the +.Ev USER +variable +of the +.Ev ENVIRON +option if supported by the remote system. +The name used is that of the current user as returned by +.Xr getlogin 2 +if it agrees with the current user ID, +otherwise it is the name associated with the user ID. +.It Fl c +Disables the reading of the user's +.Pa \&.telnetrc +file. +(See the +.Ic toggle skiprc +command on this man page.) +.It Fl d +Sets the initial value of the +.Ic debug +toggle to +.Dv TRUE . +.It Fl e Ar escape char +Sets the initial +.Nm +escape character to +.Ar escape char . +If +.Ar escape char +is omitted, then +there will be no escape character. +.It Fl f +If Kerberos V5 authentication is being used, the +.Fl f +option allows the local credentials to be forwarded to the remote system. +.It Fl k Ar realm +If Kerberos authentication is being used, the +.Fl k +option requests that telnet obtain tickets for the remote host in +realm +.Ar realm +instead of the remote host's realm, as determined by +.Xr krb_realmofhost 3 . +.It Fl l Ar user +When connecting to the remote system, if the remote system +understands the +.Ev ENVIRON +option, then +.Ar user +will be sent to the remote system as the value for the variable USER. +This option implies the +.Fl a +option. +This option may also be used with the +.Ic open +command. +.It Fl n Ar tracefile +Opens +.Ar tracefile +for recording trace information. +See the +.Ic set tracefile +command below. +.It Fl P Ar policy +Use IPsec policy specification string +.Ar policy , +for the connections. +See +.Xr ipsec_set_policy 3 +for details. +.It Fl r +Specifies a user interface similar to +.Xr rlogin 1 . +In this +mode, the escape character is set to the tilde (~) character, +unless modified by the +.Fl e +option. +.It Fl x +Turns on encryption of the data stream if possible. +This +option is not available outside of the United States and +Canada. +.It Ar host +Indicates the official name, an alias, or the Internet address +of a remote host. +.It Ar port +Indicates a port number (address of an application). +If a number is +not specified, the default +.Nm +port is used. +.El +.Pp +When in rlogin mode, a line of the form ~. +disconnects from the +remote host; ~ is the telnet escape character. +Similarly, the line ~^Z suspends the telnet session. +The line ~^] escapes to the normal telnet escape prompt. +.Pp +Once a connection has been opened, +.Nm +will attempt to enable the +.Dv TELNET LINEMODE +option. +If this fails, then +.Nm +will revert to one of two input modes: +either \*(Lqcharacter at a time\*(Rq +or \*(Lqold line by line\*(Rq +depending on what the remote system supports. +.Pp +When +.Dv LINEMODE +is enabled, character processing is done on the +local system, under the control of the remote system. +When input +editing or character echoing is to be disabled, the remote system +will relay that information. +The remote system will also relay +changes to any special characters that happen on the remote +system, so that they can take effect on the local system. +.Pp +In \*(Lqcharacter at a time\*(Rq mode, most +text typed is immediately sent to the remote host for processing. +.Pp +In \*(Lqold line by line\*(Rq mode, all text is echoed locally, +and (normally) only completed lines are sent to the remote host. +The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used +to turn off and on the local echo +(this would mostly be used to enter passwords +without the password being echoed). +.Pp +If the +.Dv LINEMODE +option is enabled, or if the +.Ic localchars +toggle is +.Dv TRUE +(the default for \*(Lqold line by line\*(Lq; see below), +the user's +.Ic quit , +.Ic intr , +and +.Ic flush +characters are trapped locally, and sent as +.Tn TELNET +protocol sequences to the remote side. +If +.Dv LINEMODE +has ever been enabled, then the user's +.Ic susp +and +.Ic eof +are also sent as +.Tn TELNET +protocol sequences, +and +.Ic quit +is sent as a +.Dv TELNET ABORT +instead of +.Dv BREAK . +There are options (see +.Ic toggle +.Ic autoflush +and +.Ic toggle +.Ic autosynch +below) +which cause this action to flush subsequent output to the terminal +(until the remote host acknowledges the +.Tn TELNET +sequence) and flush previous terminal input +(in the case of +.Ic quit +and +.Ic intr ) . +.Pp +While connected to a remote host, +.Nm +command mode may be entered by typing the +.Nm +\*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq). +When in command mode, the normal terminal editing conventions are available. +.Pp +The following +.Nm +commands are available. +Only enough of each command to uniquely identify it need be typed +(this is also true for arguments to the +.Ic mode , +.Ic set , +.Ic toggle , +.Ic unset , +.Ic slc , +.Ic environ , +and +.Ic display +commands). +.Pp +.Bl -tag -width "mode type" +.It Ic auth Ar argument ... +The auth command manipulates the information sent through the +.Dv TELNET AUTHENTICATE +option. +Valid arguments for the +auth command are as follows: +.Bl -tag -width "disable type" +.It Ic disable Ar type +Disables the specified type of authentication. +To obtain a list of available types, use the +.Ic auth disable \&? +command. +.It Ic enable Ar type +Enables the specified type of authentication. +To obtain a list of available types, use the +.Ic auth enable \&? +command. +.It Ic status +Lists the current status of the various types of +authentication. +.El +.It Ic close +Close a +.Tn TELNET +session and return to command mode. +.It Ic display Ar argument ... +Displays all, or some, of the +.Ic set +and +.Ic toggle +values (see below). +.It Ic encrypt Ar argument ... +The encrypt command manipulates the information sent through the +.Dv TELNET ENCRYPT +option. +.Pp +Note: Because of export controls, the +.Dv TELNET ENCRYPT +option is not supported outside of the United States and Canada. +.Pp +Valid arguments for the encrypt command are: +.Bl -tag -width Ar +.It Ic disable Ar type Ic [input|output] +Disables the specified type of encryption. +If you omit the input and output, both input and output +are disabled. +To obtain a list of available types, use the +.Ic encrypt disable \&? +command. +.It Ic enable Ar type Ic [input|output] +Enables the specified type of encryption. +If you omit input and output, both input and output are +enabled. +To obtain a list of available types, use the +.Ic encrypt enable \&? +command. +.It Ic input +This is the same as the +.Ic encrypt start input +command. +.It Ic -input +This is the same as the +.Ic encrypt stop input +command. +.It Ic output +This is the same as the +.Ic encrypt start output +command. +.It Ic -output +This is the same as the +.Ic encrypt stop output +command. +.It Ic start Ic [input|output] +Attempts to start encryption. +If you omit +.Ic input +and +.Ic output , +both input and output are enabled. +To obtain a list of available types, use the +.Ic encrypt enable \&? +command. +.It Ic status +Lists the current status of encryption. +.It Ic stop Ic [input|output] +Stops encryption. +If you omit input and output, +encryption is on both input and output. +.It Ic type Ar type +Sets the default type of encryption to be used +with later +.Ic encrypt start +or +.Ic encrypt stop +commands. +.El +.It Ic environ Ar arguments ... +The +.Ic environ +command is used to manipulate the +variables that may be sent through the +.Dv TELNET ENVIRON +option. +The initial set of variables is taken from the users +environment, with only the +.Ev DISPLAY +and +.Ev PRINTER +variables being exported by default. +The +.Ev USER +variable is also exported if the +.Fl a +or +.Fl l +options are used. +.Pp +Valid arguments for the +.Ic environ +command are: +.Bl -tag -width Fl +.It Ic define Ar variable value +Define the variable +.Ar variable +to have a value of +.Ar value . +Any variables defined by this command are automatically exported. +The +.Ar value +may be enclosed in single or double quotes so +that tabs and spaces may be included. +.It Ic undefine Ar variable +Remove +.Ar variable +from the list of environment variables. +.It Ic export Ar variable +Mark the variable +.Ar variable +to be exported to the remote side. +.It Ic unexport Ar variable +Mark the variable +.Ar variable +to not be exported unless +explicitly asked for by the remote side. +.It Ic list +List the current set of environment variables. +Those marked with a +.Cm * +will be sent automatically, +other variables will only be sent if explicitly requested. +.It Ic \&? +Prints out help information for the +.Ic environ +command. +.El +.It Ic logout +Sends the +.Dv TELNET LOGOUT +option to the remote side. +This command is similar to a +.Ic close +command; however, if the remote side does not support the +.Dv LOGOUT +option, nothing happens. +If, however, the remote side does support the +.Dv LOGOUT +option, this command should cause the remote side to close the +.Tn TELNET +connection. +If the remote side also supports the concept of +suspending a user's session for later reattachment, +the logout argument indicates that you +should terminate the session immediately. +.It Ic mode Ar type +.Ar Type +is one of several options, depending on the state of the +.Tn TELNET +session. +The remote host is asked for permission to go into the requested mode. +If the remote host is capable of entering that mode, the requested +mode will be entered. +.Bl -tag -width Ar +.It Ic character +Disable the +.Dv TELNET LINEMODE +option, or, if the remote side does not understand the +.Dv LINEMODE +option, then enter \*(Lqcharacter at a time\*(Lq mode. +.It Ic line +Enable the +.Dv TELNET LINEMODE +option, or, if the remote side does not understand the +.Dv LINEMODE +option, then attempt to enter \*(Lqold-line-by-line\*(Lq mode. +.It Ic isig Pq Ic \-isig +Attempt to enable (disable) the +.Dv TRAPSIG +mode of the +.Dv LINEMODE +option. +This requires that the +.Dv LINEMODE +option be enabled. +.It Ic edit Pq Ic \-edit +Attempt to enable (disable) the +.Dv EDIT +mode of the +.Dv LINEMODE +option. +This requires that the +.Dv LINEMODE +option be enabled. +.It Ic softtabs Pq Ic \-softtabs +Attempt to enable (disable) the +.Dv SOFT_TAB +mode of the +.Dv LINEMODE +option. +This requires that the +.Dv LINEMODE +option be enabled. +.It Ic litecho Pq Ic \-litecho +Attempt to enable (disable) the +.Dv LIT_ECHO +mode of the +.Dv LINEMODE +option. +This requires that the +.Dv LINEMODE +option be enabled. +.It Ic \&? +Prints out help information for the +.Ic mode +command. +.El +.It Ic open Ar host Oo Fl l Ar user Oc Oo Fl a Oc Oo Oo \&- Oc Ns Ar port Oc +Open a connection to the named host. +If no port number +is specified, +.Nm +will attempt to contact a +.Tn TELNET +server at the default port. +The host specification may be either a host name (see +.Xr hosts 5 ) +or an Internet address specified in the \*(Lqdot notation\*(Rq (see +.Xr inet 3 ) . +The +.Fl l +option may be used to specify the user name +to be passed to the remote system via the +.Ev ENVIRON +option. +If a port is specified +.Nm +omits any automatic initialisation of +.Tn TELNET +options. +When the port number is preceded by a minus sign, +the initial option negotiation is done. +.Pp +After establishing a connection, the file +.Pa \&.telnetrc +in the +user's home directory is read. +Lines beginning with a # are +comment lines. +Blank lines are ignored. +Lines that begin +without white space are the start of a machine entry. +The first thing on such a line is a string identifying the machine +that is being connected to. +It may be the hostname or numeric address specified as the argument +.Ar host , +the canonical name of that string as determined by +.Xr getaddrinfo 3 , +or the string +.Dq DEFAULT +indicating all hosts. +The rest of the line, and successive +lines that begin with white space are assumed to be +.Nm +commands and are processed as if they had been typed +in manually to the +.Nm +command prompt. +.It Ic quit +Close any open +.Tn TELNET +session and exit +.Nm . +An end of file (in command mode) will also close a session and exit. +.It Ic send Ar arguments +Sends one or more special character sequences to the remote host. +The following are the arguments which may be specified +(more than one argument may be specified at a time): +.Pp +.Bl -tag -width escape +.It Ic abort +Sends the +.Dv TELNET ABORT +(Abort +processes) +sequence. +.It Ic ao +Sends the +.Dv TELNET AO +(Abort Output) sequence, which should cause the remote system to flush +all output +.Em from +the remote system +.Em to +the user's terminal. +.It Ic ayt +Sends the +.Dv TELNET AYT +(Are You There) +sequence, to which the remote system may or may not choose to respond. +.It Ic brk +Sends the +.Dv TELNET BRK +(Break) sequence, which may have significance to the remote +system. +.It Ic ec +Sends the +.Dv TELNET EC +(Erase Character) +sequence, which should cause the remote system to erase the last character +entered. +.It Ic el +Sends the +.Dv TELNET EL +(Erase Line) +sequence, which should cause the remote system to erase the line currently +being entered. +.It Ic eof +Sends the +.Dv TELNET EOF +(End Of File) +sequence. +.It Ic eor +Sends the +.Dv TELNET EOR +(End of Record) +sequence. +.It Ic escape +Sends the current +.Nm +escape character (initially \*(Lq^\*(Rq). +.It Ic ga +Sends the +.Dv TELNET GA +(Go Ahead) +sequence, which likely has no significance to the remote system. +.It Ic getstatus +If the remote side supports the +.Dv TELNET STATUS +command, +.Ic getstatus +will send the subnegotiation to request that the server send +its current option status. +.It Ic ip +Sends the +.Dv TELNET IP +(Interrupt Process) sequence, which should cause the remote +system to abort the currently running process. +.It Ic nop +Sends the +.Dv TELNET NOP +(No OPeration) +sequence. +.It Ic susp +Sends the +.Dv TELNET SUSP +(SUSPend process) +sequence. +.It Ic synch +Sends the +.Dv TELNET SYNCH +sequence. +This sequence causes the remote system to discard all previously typed +(but not yet read) input. +This sequence is sent as +.Tn TCP +urgent +data (and may not work if the remote system is a +.Bx 4.2 +system -- if +it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal). +.It Ic do Ar cmd +.It Ic dont Ar cmd +.It Ic will Ar cmd +.It Ic wont Ar cmd +Sends the +.Dv TELNET DO +.Ar cmd +sequence. +.Ar Cmd +can be either a decimal number between 0 and 255, +or a symbolic name for a specific +.Dv TELNET +command. +.Ar Cmd +can also be either +.Ic help +or +.Ic \&? +to print out help information, including +a list of known symbolic names. +.It Ic \&? +Prints out help information for the +.Ic send +command. +.El +.It Ic set Ar argument value +.It Ic unset Ar argument value +The +.Ic set +command will set any one of a number of +.Nm +variables to a specific value or to +.Dv TRUE . +The special value +.Ic off +turns off the function associated with +the variable, this is equivalent to using the +.Ic unset +command. +The +.Ic unset +command will disable or set to +.Dv FALSE +any of the specified functions. +The values of variables may be interrogated with the +.Ic display +command. +The variables which may be set or unset, but not toggled, are +listed here. +In addition, any of the variables for the +.Ic toggle +command may be explicitly set or unset using +the +.Ic set +and +.Ic unset +commands. +.Bl -tag -width escape +.It Ic ayt +If +.Tn TELNET +is in localchars mode, or +.Dv LINEMODE +is enabled, and the status character is typed, a +.Dv TELNET AYT +sequence (see +.Ic send ayt +above) is sent to the +remote host. +The initial value for the "Are You There" +character is the terminal's status character. +.It Ic echo +This is the value (initially \*(Lq^E\*(Rq) which, when in +\*(Lqline by line\*(Rq mode, toggles between doing local echoing +of entered characters (for normal processing), and suppressing +echoing of entered characters (for entering, say, a password). +.It Ic eof +If +.Nm +is operating in +.Dv LINEMODE +or \*(Lqold line by line\*(Rq mode, entering this character +as the first character on a line will cause this character to be +sent to the remote system. +The initial value of the eof character is taken to be the terminal's +.Ic eof +character. +.It Ic erase +If +.Nm +is in +.Ic localchars +mode (see +.Ic toggle +.Ic localchars +below), +.Sy and +if +.Nm +is operating in \*(Lqcharacter at a time\*(Rq mode, then when this +character is typed, a +.Dv TELNET EC +sequence (see +.Ic send +.Ic ec +above) +is sent to the remote system. +The initial value for the erase character is taken to be +the terminal's +.Ic erase +character. +.It Ic escape +This is the +.Nm +escape character (initially \*(Lq^[\*(Rq) which causes entry +into +.Nm +command mode (when connected to a remote system). +.It Ic flushoutput +If +.Nm +is in +.Ic localchars +mode (see +.Ic toggle +.Ic localchars +below) +and the +.Ic flushoutput +character is typed, a +.Dv TELNET AO +sequence (see +.Ic send +.Ic ao +above) +is sent to the remote host. +The initial value for the flush character is taken to be +the terminal's +.Ic flush +character. +.It Ic forw1 +.It Ic forw2 +If +.Tn TELNET +is operating in +.Dv LINEMODE , +these are the +characters that, when typed, cause partial lines to be +forwarded to the remote system. +The initial value for +the forwarding characters are taken from the terminal's +eol and eol2 characters. +.It Ic interrupt +If +.Nm +is in +.Ic localchars +mode (see +.Ic toggle +.Ic localchars +below) +and the +.Ic interrupt +character is typed, a +.Dv TELNET IP +sequence (see +.Ic send +.Ic ip +above) +is sent to the remote host. +The initial value for the interrupt character is taken to be +the terminal's +.Ic intr +character. +.It Ic kill +If +.Nm +is in +.Ic localchars +mode (see +.Ic toggle +.Ic localchars +below), +.Ic and +if +.Nm +is operating in \*(Lqcharacter at a time\*(Rq mode, then when this +character is typed, a +.Dv TELNET EL +sequence (see +.Ic send +.Ic el +above) +is sent to the remote system. +The initial value for the kill character is taken to be +the terminal's +.Ic kill +character. +.It Ic lnext +If +.Nm +is operating in +.Dv LINEMODE +or \*(Lqold line by line\*(Lq mode, then this character is taken to +be the terminal's +.Ic lnext +character. +The initial value for the lnext character is taken to be +the terminal's +.Ic lnext +character. +.It Ic quit +If +.Nm +is in +.Ic localchars +mode (see +.Ic toggle +.Ic localchars +below) +and the +.Ic quit +character is typed, a +.Dv TELNET BRK +sequence (see +.Ic send +.Ic brk +above) +is sent to the remote host. +The initial value for the quit character is taken to be +the terminal's +.Ic quit +character. +.It Ic reprint +If +.Nm +is operating in +.Dv LINEMODE +or \*(Lqold line by line\*(Lq mode, then this character is taken to +be the terminal's +.Ic reprint +character. +The initial value for the reprint character is taken to be +the terminal's +.Ic reprint +character. +.It Ic rlogin +This is the rlogin escape character. +If set, the normal +.Tn TELNET +escape character is ignored unless it is +preceded by this character at the beginning of a line. +This character, at the beginning of a line followed by +a "." closes the connection; when followed by a ^Z it +suspends the +.Nm +command. +The initial state is to +disable the rlogin escape character. +.It Ic start +If the +.Dv TELNET TOGGLE-FLOW-CONTROL +option has been enabled, +then this character is taken to +be the terminal's +.Ic start +character. +The initial value for the start character is taken to be +the terminal's +.Ic start +character. +.It Ic stop +If the +.Dv TELNET TOGGLE-FLOW-CONTROL +option has been enabled, +then this character is taken to +be the terminal's +.Ic stop +character. +The initial value for the stop character is taken to be +the terminal's +.Ic stop +character. +.It Ic susp +If +.Nm +is in +.Ic localchars +mode, or +.Dv LINEMODE +is enabled, and the +.Ic suspend +character is typed, a +.Dv TELNET SUSP +sequence (see +.Ic send +.Ic susp +above) +is sent to the remote host. +The initial value for the suspend character is taken to be +the terminal's +.Ic suspend +character. +.It Ic tracefile +This is the file to which the output, caused by +.Ic netdata +or +.Ic option +tracing being +.Dv TRUE , +will be written. +If it is set to +.Dq Fl , +then tracing information will be written to standard output (the default). +.It Ic worderase +If +.Nm +is operating in +.Dv LINEMODE +or \*(Lqold line by line\*(Lq mode, then this character is taken to +be the terminal's +.Ic worderase +character. +The initial value for the worderase character is taken to be +the terminal's +.Ic worderase +character. +.It Ic \&? +Displays the legal +.Ic set +.Pq Ic unset +commands. +.El +.It Ic slc Ar state +The +.Ic slc +command (Set Local Characters) is used to set +or change the state of the special +characters when the +.Dv TELNET LINEMODE +option has +been enabled. +Special characters are characters that get +mapped to +.Tn TELNET +commands sequences (like +.Ic ip +or +.Ic quit ) +or line editing characters (like +.Ic erase +and +.Ic kill ) . +By default, the local special characters are exported. +.Bl -tag -width Fl +.It Ic check +Verify the current settings for the current special characters. +The remote side is requested to send all the current special +character settings, and if there are any discrepancies with +the local side, the local side will switch to the remote value. +.It Ic export +Switch to the local defaults for the special characters. +The +local default characters are those of the local terminal at +the time when +.Nm +was started. +.It Ic import +Switch to the remote defaults for the special characters. +The remote default characters are those of the remote system +at the time when the +.Tn TELNET +connection was established. +.It Ic \&? +Prints out help information for the +.Ic slc +command. +.El +.It Ic status +Show the current status of +.Nm . +This includes the peer one is connected to, as well +as the current mode. +.It Ic toggle Ar arguments ... +Toggle (between +.Dv TRUE +and +.Dv FALSE ) +various flags that control how +.Nm +responds to events. +These flags may be set explicitly to +.Dv TRUE +or +.Dv FALSE +using the +.Ic set +and +.Ic unset +commands listed above. +More than one argument may be specified. +The state of these flags may be interrogated with the +.Ic display +command. +Valid arguments are: +.Bl -tag -width Ar +.It Ic authdebug +Turns on debugging information for the authentication code. +.It Ic autoflush +If +.Ic autoflush +and +.Ic localchars +are both +.Dv TRUE , +then when the +.Ic ao , +or +.Ic quit +characters are recognized (and transformed into +.Tn TELNET +sequences; see +.Ic set +above for details), +.Nm +refuses to display any data on the user's terminal +until the remote system acknowledges (via a +.Dv TELNET TIMING MARK +option) +that it has processed those +.Tn TELNET +sequences. +The initial value for this toggle is +.Dv TRUE +if the terminal user had not +done an "stty noflsh", otherwise +.Dv FALSE +(see +.Xr stty 1 ) . +.It Ic autodecrypt +When the +.Dv TELNET ENCRYPT +option is negotiated, by +default the actual encryption (decryption) of the data +stream does not start automatically. +The autoencrypt +(autodecrypt) command states that encryption of the +output (input) stream should be enabled as soon as +possible. +.Pp +Note: Because of export controls, the +.Dv TELNET ENCRYPT +option is not supported outside the United States and Canada. +.It Ic autologin +If the remote side supports the +.Dv TELNET AUTHENTICATION +option +.Tn TELNET +attempts to use it to perform automatic authentication. +If the +.Dv AUTHENTICATION +option is not supported, the user's login +name are propagated through the +.Dv TELNET ENVIRON +option. +This command is the same as specifying the +.Fl a +option on the +.Ic open +command. +.It Ic autosynch +If +.Ic autosynch +and +.Ic localchars +are both +.Dv TRUE , +then when either the +.Ic intr +or +.Ic quit +characters is typed (see +.Ic set +above for descriptions of the +.Ic intr +and +.Ic quit +characters), the resulting +.Tn TELNET +sequence sent is followed by the +.Dv TELNET SYNCH +sequence. +This procedure +.Ic should +cause the remote system to begin throwing away all previously +typed input until both of the +.Tn TELNET +sequences have been read and acted upon. +The initial value of this toggle is +.Dv FALSE . +.It Ic binary +Enable or disable the +.Dv TELNET BINARY +option on both input and output. +.It Ic inbinary +Enable or disable the +.Dv TELNET BINARY +option on input. +.It Ic outbinary +Enable or disable the +.Dv TELNET BINARY +option on output. +.It Ic crlf +If this is +.Dv TRUE , +then carriage returns will be sent as +.Li \*[Lt]CR\*[Gt]\*[Lt]LF\*[Gt] . +If this is +.Dv FALSE , +then carriage returns will be send as +.Li \*[Lt]CR\*[Gt]\*[Lt]NUL\*[Gt] . +The initial value for this toggle is +.Dv FALSE . +.It Ic crmod +Toggle carriage return mode. +When this mode is enabled, most carriage return characters received from +the remote host will be mapped into a carriage return followed by +a line feed. +This mode does not affect those characters typed by the user, only +those received from the remote host. +This mode is not very useful unless the remote host +only sends carriage return, but never line feed. +The initial value for this toggle is +.Dv FALSE . +.It Ic debug +Toggles socket level debugging (useful only to the +.Ic super user ) . +The initial value for this toggle is +.Dv FALSE . +.It Ic encdebug +Turns on debugging information for the encryption code. +.It Ic localchars +If this is +.Dv TRUE , +then the +.Ic flush , +.Ic interrupt , +.Ic quit , +.Ic erase , +and +.Ic kill +characters (see +.Ic set +above) are recognized locally, and transformed into (hopefully) appropriate +.Tn TELNET +control sequences +(respectively +.Ic ao , +.Ic ip , +.Ic brk , +.Ic ec , +and +.Ic el ; +see +.Ic send +above). +The initial value for this toggle is +.Dv TRUE +in \*(Lqold line by line\*(Rq mode, +and +.Dv FALSE +in \*(Lqcharacter at a time\*(Rq mode. +When the +.Dv LINEMODE +option is enabled, the value of +.Ic localchars +is ignored, and assumed to always be +.Dv TRUE . +If +.Dv LINEMODE +has ever been enabled, then +.Ic quit +is sent as +.Ic abort , +and +.Ic eof and +.Ic suspend +are sent as +.Ic eof and +.Ic susp +(see +.Ic send +above). +.It Ic netdata +Toggles the display of all network data (in hexadecimal format). +The initial value for this toggle is +.Dv FALSE . +.It Ic options +Toggles the display of some internal +.Nm +protocol processing (having to do with +.Tn TELNET +options). +The initial value for this toggle is +.Dv FALSE . +.It Ic prettydump +When the +.Ic netdata +toggle is enabled, if +.Ic prettydump +is enabled the output from the +.Ic netdata +command will be formatted in a more user readable format. +Spaces are put between each character in the output, and the +beginning of any +.Tn TELNET +escape sequence is preceded by a '*' to aid in locating them. +.It Ic skiprc +When the skiprc toggle is +.Dv TRUE , +.Tn TELNET +skips the reading of the +.Pa \&.telnetrc +file in the users home +directory when connections are opened. +The initial +value for this toggle is +.Dv FALSE . +.It Ic termdata +Toggles the display of all terminal data (in hexadecimal format). +The initial value for this toggle is +.Dv FALSE . +.It Ic verbose_encrypt +When the +.Ic verbose_encrypt +toggle is +.Dv TRUE , +.Nm +prints out a message each time encryption is enabled or +disabled. +The initial value for this toggle is +.Dv FALSE . +Note: Because of export controls, data encryption +is not supported outside of the United States and Canada. +.It Ic \&? +Displays the legal +.Ic toggle +commands. +.El +.It Ic z +Suspend +.Nm . +This command only works when the user is using the +.Xr csh 1 . +.It Ic \&! Op Ar command +Execute a single command in a subshell on the local +system. +If +.Ar command +is omitted, then an interactive +subshell is invoked. +.It Ic \&? Op Ar command +Get help. +With no arguments, +.Nm +prints a help summary. +If a command is specified, +.Nm +will print the help information for just that command. +.El +.Sh ENVIRONMENT +.Nm +uses at least the +.Ev HOME , +.Ev SHELL , +.Ev DISPLAY , +and +.Ev TERM +environment variables. +Other environment variables may be propagated +to the other side via the +.Dv TELNET ENVIRON +option. +.Sh FILES +.Bl -tag -width ~/.telnetrc -compact +.It Pa ~/.telnetrc +user customized telnet startup values +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +IPsec support was added by WIDE/KAME project, in 1999. +.Sh NOTES +On some remote systems, echo has to be turned off manually when in +\*(Lqold line by line\*(Rq mode. +.Pp +In \*(Lqold line by line\*(Rq mode or +.Dv LINEMODE +the terminal's +.Ic eof +character is only recognized (and sent to the remote system) +when it is the first character on a line. diff --git a/usr.bin/telnet/telnet.c b/usr.bin/telnet/telnet.c new file mode 100644 index 000000000..bfc363e79 --- /dev/null +++ b/usr.bin/telnet/telnet.c @@ -0,0 +1,2648 @@ +/* $NetBSD: telnet.c,v 1.36 2012/01/10 13:49:32 christos Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)telnet.c 8.4 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: telnet.c,v 1.36 2012/01/10 13:49:32 christos Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +/* By the way, we need to include curses.h before telnet.h since, + * among other things, telnet.h #defines 'DO', which is a variable + * declared in curses.h. + */ + +#include + +#include + +#include "ring.h" +#include "defines.h" +#include "externs.h" +#include "types.h" +#include "general.h" + +#include +#ifdef AUTHENTICATION +#include +#endif +#ifdef ENCRYPTION +#include +#endif + +#define strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x)) + +static unsigned char subbuffer[SUBBUFSIZE], + *subpointer, *subend; /* buffer for sub-options */ +#define SB_CLEAR() subpointer = subbuffer; +#define SB_TERM() { subend = subpointer; SB_CLEAR(); } +#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ + *subpointer++ = (c); \ + } + +#define SB_GET() ((*subpointer++)&0xff) +#define SB_PEEK() ((*subpointer)&0xff) +#define SB_EOF() (subpointer >= subend) +#define SB_LEN() (subend - subpointer) + +char options[256]; /* The combined options */ +char do_dont_resp[256]; +char will_wont_resp[256]; + +int + eight = 0, + autologin = 0, /* Autologin anyone? */ + skiprc = 0, + connected, + showoptions, + In3270, /* Are we in 3270 mode? */ + ISend, /* trying to send network data in */ + telnet_debug = 0, + crmod, + netdata, /* Print out network data flow */ + crlf, /* Should '\r' be mapped to (or )? */ +#ifdef TN3270 + noasynchtty = 0,/* User specified "-noasynch" on command line */ + noasynchnet = 0,/* User specified "-noasynch" on command line */ + askedSGA = 0, /* We have talked about suppress go ahead */ +#endif /* defined(TN3270) */ + telnetport, + SYNCHing, /* we are in TELNET SYNCH mode */ + flushout, /* flush output */ + autoflush = 0, /* flush output when interrupting? */ + autosynch, /* send interrupt characters with SYNCH? */ + localflow, /* we handle flow control locally */ + restartany, /* if flow control enabled, restart on any character */ + localchars, /* we recognize interrupt/quit */ + donelclchars, /* the user has set "localchars" */ + donebinarytoggle, /* the user has put us in binary */ + dontlecho, /* do we suppress local echoing right now? */ + globalmode, + doaddrlookup = 1, /* do a reverse address lookup? */ + clienteof = 0; + +char *prompt = 0; + +cc_t escape; +cc_t rlogin; +#ifdef KLUDGELINEMODE +cc_t echoc; +#endif + +/* + * Telnet receiver states for fsm + */ +#define TS_DATA 0 +#define TS_IAC 1 +#define TS_WILL 2 +#define TS_WONT 3 +#define TS_DO 4 +#define TS_DONT 5 +#define TS_CR 6 +#define TS_SB 7 /* sub-option collection */ +#define TS_SE 8 /* looking for sub-option end */ + +static int telrcv_state; +#ifdef OLD_ENVIRON +unsigned char telopt_environ = TELOPT_NEW_ENVIRON; +#else +# define telopt_environ TELOPT_NEW_ENVIRON +#endif + +jmp_buf toplevel = { 0 }; + +int flushline; +int linemode; + +#ifdef KLUDGELINEMODE +int kludgelinemode = 1; +#endif + +static void dooption(int); +static void dontoption(int); +static void suboption(void); +static int telsnd(void); +static void netclear(void); +static void doflush(void); + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +Clocks clocks; + +#ifdef notdef +Modelist modelist[] = { + { "telnet command mode", COMMAND_LINE }, + { "character-at-a-time mode", 0 }, + { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, + { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, + { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, + { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, + { "3270 mode", 0 }, +}; +#endif + + +/* + * Initialize telnet environment. + */ + +void +init_telnet(void) +{ + env_init(); + + SB_CLEAR(); + ClearArray(options); + + connected = In3270 = ISend = localflow = donebinarytoggle = 0; +#if defined(AUTHENTICATION) || defined(ENCRYPTION) + auth_encrypt_connect(connected); +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ + restartany = -1; + + SYNCHing = 0; + + /* Don't change NetTrace */ + + escape = CONTROL(']'); + rlogin = _POSIX_VDISABLE; +#ifdef KLUDGELINEMODE + echoc = CONTROL('E'); +#endif + + flushline = 1; + telrcv_state = TS_DATA; +} + + +#ifdef notdef +#include + +/*VARARGS*/ +static void +printring(Ring *ring, char *format, ...) + va_dcl +{ + va_list ap; + char buffer[100]; /* where things go */ + char *ptr; + char *string; + int i; + + va_start(ap, format); + + ptr = buffer; + + while ((i = *format++) != 0) { + if (i == '%') { + i = *format++; + switch (i) { + case 'c': + *ptr++ = va_arg(ap, int); + break; + case 's': + string = va_arg(ap, char *); + ring_supply_data(ring, buffer, ptr-buffer); + ring_supply_data(ring, string, strlen(string)); + ptr = buffer; + break; + case 0: + ExitString("printring: trailing %%.\n", 1); + /*NOTREACHED*/ + default: + ExitString("printring: unknown format character.\n", 1); + /*NOTREACHED*/ + } + } else { + *ptr++ = i; + } + } + va_end(ap); + ring_supply_data(ring, buffer, ptr-buffer); +} +#endif + +/* + * These routines are in charge of sending option negotiations + * to the other side. + * + * The basic idea is that we send the negotiation if either side + * is in disagreement as to what the current state should be. + */ + +void +send_do(int c, int init) +{ + if (init) { + if (((do_dont_resp[c] == 0) && my_state_is_do(c)) || + my_want_state_is_do(c)) + return; + set_my_want_state_do(c); + do_dont_resp[c]++; + } + NET2ADD(IAC, DO); + NETADD(c); + printoption("SENT", DO, c); +} + +void +send_dont(int c, int init) +{ + if (init) { + if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) || + my_want_state_is_dont(c)) + return; + set_my_want_state_dont(c); + do_dont_resp[c]++; + } + NET2ADD(IAC, DONT); + NETADD(c); + printoption("SENT", DONT, c); +} + +void +send_will(int c, int init) +{ + if (init) { + if (((will_wont_resp[c] == 0) && my_state_is_will(c)) || + my_want_state_is_will(c)) + return; + set_my_want_state_will(c); + will_wont_resp[c]++; + } + NET2ADD(IAC, WILL); + NETADD(c); + printoption("SENT", WILL, c); +} + +void +send_wont(int c, int init) +{ + if (init) { + if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) || + my_want_state_is_wont(c)) + return; + set_my_want_state_wont(c); + will_wont_resp[c]++; + } + NET2ADD(IAC, WONT); + NETADD(c); + printoption("SENT", WONT, c); +} + + +void +willoption(int option) +{ + int new_state_ok = 0; + + if (do_dont_resp[option]) { + --do_dont_resp[option]; + if (do_dont_resp[option] && my_state_is_do(option)) + --do_dont_resp[option]; + } + + if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) { + + switch (option) { + + case TELOPT_ECHO: +# if defined(TN3270) + /* + * The following is a pain in the rear-end. + * Various IBM servers (some versions of Wiscnet, + * possibly Fibronics/Spartacus, and who knows who + * else) will NOT allow us to send "DO SGA" too early + * in the setup proceedings. On the other hand, + * 4.2 servers (telnetd) won't set SGA correctly. + * So, we are stuck. Empirically (but, based on + * a VERY small sample), the IBM servers don't send + * out anything about ECHO, so we postpone our sending + * "DO SGA" until we see "WILL ECHO" (which 4.2 servers + * DO send). + */ + { + if (askedSGA == 0) { + askedSGA = 1; + if (my_want_state_is_dont(TELOPT_SGA)) + send_do(TELOPT_SGA, 1); + } + } + /* Fall through */ + case TELOPT_EOR: +#endif /* defined(TN3270) */ + case TELOPT_BINARY: + case TELOPT_SGA: + settimer(modenegotiated); + /* FALL THROUGH */ + case TELOPT_STATUS: +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: +#endif /* ENCRYPTION */ +#endif + new_state_ok = 1; + break; + + case TELOPT_TM: + if (flushout) + flushout = 0; + /* + * Special case for TM. If we get back a WILL, + * pretend we got back a WONT. + */ + set_my_want_state_dont(option); + set_my_state_dont(option); + return; /* Never reply to TM will's/wont's */ + + case TELOPT_LINEMODE: + default: + break; + } + + if (new_state_ok) { + set_my_want_state_do(option); + send_do(option, 0); + setconnmode(0); /* possibly set new tty mode */ + } else { + do_dont_resp[option]++; + send_dont(option, 0); + } + } + set_my_state_do(option); +#ifdef ENCRYPTION + if (option == TELOPT_ENCRYPT) + encrypt_send_support(); +#endif /* ENCRYPTION */ +} + +void +wontoption(int option) +{ + if (do_dont_resp[option]) { + --do_dont_resp[option]; + if (do_dont_resp[option] && my_state_is_dont(option)) + --do_dont_resp[option]; + } + + if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) { + + switch (option) { + +#ifdef KLUDGELINEMODE + case TELOPT_SGA: + if (!kludgelinemode) + break; + /* FALL THROUGH */ +#endif + case TELOPT_ECHO: + settimer(modenegotiated); + break; + + case TELOPT_TM: + if (flushout) + flushout = 0; + set_my_want_state_dont(option); + set_my_state_dont(option); + return; /* Never reply to TM will's/wont's */ + + default: + break; + } + set_my_want_state_dont(option); + if (my_state_is_do(option)) + send_dont(option, 0); + setconnmode(0); /* Set new tty mode */ + } else if (option == TELOPT_TM) { + /* + * Special case for TM. + */ + if (flushout) + flushout = 0; + set_my_want_state_dont(option); + } + set_my_state_dont(option); +} + +static void +dooption(int option) +{ + int new_state_ok = 0; + + if (will_wont_resp[option]) { + --will_wont_resp[option]; + if (will_wont_resp[option] && my_state_is_will(option)) + --will_wont_resp[option]; + } + + if (will_wont_resp[option] == 0) { + if (my_want_state_is_wont(option)) { + + switch (option) { + + case TELOPT_TM: + /* + * Special case for TM. We send a WILL, but pretend + * we sent WONT. + */ + send_will(option, 0); + set_my_want_state_wont(TELOPT_TM); + set_my_state_wont(TELOPT_TM); + return; + +# if defined(TN3270) + case TELOPT_EOR: /* end of record */ +# endif /* defined(TN3270) */ + case TELOPT_BINARY: /* binary mode */ + case TELOPT_NAWS: /* window size */ + case TELOPT_TSPEED: /* terminal speed */ + case TELOPT_LFLOW: /* local flow control */ + case TELOPT_TTYPE: /* terminal type option */ + case TELOPT_SGA: /* no big deal */ +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: /* encryption variable option */ +#endif /* ENCRYPTION */ + new_state_ok = 1; + break; + + case TELOPT_NEW_ENVIRON: /* New environment variable option */ +#ifdef OLD_ENVIRON + if (my_state_is_will(TELOPT_OLD_ENVIRON)) + send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */ + goto env_common; + case TELOPT_OLD_ENVIRON: /* Old environment variable option */ + if (my_state_is_will(TELOPT_NEW_ENVIRON)) + break; /* Don't enable if new one is in use! */ + env_common: + telopt_environ = option; +#endif + new_state_ok = 1; + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + if (autologin) + new_state_ok = 1; + break; +#endif + + case TELOPT_XDISPLOC: /* X Display location */ + if (env_getvalue((const unsigned char *)"DISPLAY")) + new_state_ok = 1; + break; + + case TELOPT_LINEMODE: +#ifdef KLUDGELINEMODE + kludgelinemode = 0; + send_do(TELOPT_SGA, 1); +#endif + set_my_want_state_will(TELOPT_LINEMODE); + send_will(option, 0); + set_my_state_will(TELOPT_LINEMODE); + slc_init(); + return; + + case TELOPT_ECHO: /* We're never going to echo... */ + default: + break; + } + + if (new_state_ok) { + set_my_want_state_will(option); + send_will(option, 0); + setconnmode(0); /* Set new tty mode */ + } else { + will_wont_resp[option]++; + send_wont(option, 0); + } + } else { + /* + * Handle options that need more things done after the + * other side has acknowledged the option. + */ + switch (option) { + case TELOPT_LINEMODE: +#ifdef KLUDGELINEMODE + kludgelinemode = 0; + send_do(TELOPT_SGA, 1); +#endif + set_my_state_will(option); + slc_init(); + send_do(TELOPT_SGA, 0); + return; + } + } + } + set_my_state_will(option); +} + +static void +dontoption(int option) +{ + + if (will_wont_resp[option]) { + --will_wont_resp[option]; + if (will_wont_resp[option] && my_state_is_wont(option)) + --will_wont_resp[option]; + } + + if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) { + switch (option) { + case TELOPT_LINEMODE: + linemode = 0; /* put us back to the default state */ + break; +#ifdef OLD_ENVIRON + case TELOPT_NEW_ENVIRON: + /* + * The new environ option wasn't recognized, try + * the old one. + */ + send_will(TELOPT_OLD_ENVIRON, 1); + telopt_environ = TELOPT_OLD_ENVIRON; + break; +#endif + } + /* we always accept a DONT */ + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + setconnmode(0); /* Set new tty mode */ + } + set_my_state_wont(option); +} + +/* + * Given a buffer returned by tgetent(), this routine will turn + * the pipe separated list of names in the buffer into an array + * of pointers to null terminated names. We toss out any bad, + * duplicate, or verbose names (names with spaces). + */ + +static char name_unknown[] = "UNKNOWN"; +static char *unknown[] = { 0, 0 }; + +char ** +mklist(char *buf, char *name) +{ + int n; + char c, *cp, **argvp, *cp2, **argv, **avt; + + if (name) { + if ((int)strlen(name) > 40) { + name = 0; + unknown[0] = name_unknown; + } else { + unknown[0] = name; + upcase(name); + } + } else + unknown[0] = name_unknown; + /* + * Count up the number of names. + */ + for (n = 1, cp = buf; *cp && *cp != ':'; cp++) { + if (*cp == '|') + n++; + } + /* + * Allocate an array to put the name pointers into + */ + argv = (char **)malloc((n+3)*sizeof(char *)); + if (argv == 0) + return(unknown); + + /* + * Fill up the array of pointers to names. + */ + *argv = 0; + argvp = argv+1; + n = 0; + for (cp = cp2 = buf; (c = *cp); cp++) { + if (c == '|' || c == ':') { + *cp++ = '\0'; + /* + * Skip entries that have spaces or are over 40 + * characters long. If this is our environment + * name, then put it up front. Otherwise, as + * long as this is not a duplicate name (case + * insensitive) add it to the list. + */ + if (n || (cp - cp2 > 41)) + ; + else if (name && (strncasecmp(name, cp2, cp-cp2) == 0)) + *argv = cp2; + else if (is_unique(cp2, argv+1, argvp)) + *argvp++ = cp2; + if (c == ':') + break; + /* + * Skip multiple delimiters. Reset cp2 to + * the beginning of the next name. Reset n, + * the flag for names with spaces. + */ + while ((c = *cp) == '|') + cp++; + cp2 = cp; + n = 0; + } + /* + * Skip entries with spaces or non-ascii values. + * Convert lower case letters to upper case. + */ + if ((c == ' ') || !isascii(c)) + n = 1; + else if (islower((unsigned char)c)) + *cp = toupper((unsigned char)c); + } + + /* + * Check for an old V6 2 character name. If the second + * name points to the beginning of the buffer, and is + * only 2 characters long, move it to the end of the array. + */ + if ((argv[1] == buf) && (strlen(argv[1]) == 2)) { + --argvp; + for (avt = &argv[1]; avt < argvp; avt++) + *avt = *(avt+1); + *argvp++ = buf; + } + + /* + * Duplicate last name, for TTYPE option, and null + * terminate the array. If we didn't find a match on + * our terminal name, put that name at the beginning. + */ + cp = *(argvp-1); + *argvp++ = cp; + *argvp = 0; + + if (*argv == 0) { + if (name) + *argv = name; + else { + --argvp; + for (avt = argv; avt < argvp; avt++) + *avt = *(avt+1); + } + } + if (*argv) + return(argv); + else + return(unknown); +} + +int +is_unique(char *name, char **as, char **ae) +{ + char **ap; + int n; + + n = strlen(name) + 1; + for (ap = as; ap < ae; ap++) + if (strncasecmp(*ap, name, n) == 0) + return(0); + return (1); +} + +#ifdef TERMCAP +char *termbuf; + +/*ARGSUSED*/ +int +setupterm(char *tname, int fd, int *errp) +{ + char zz[1024], *zz_ptr; + char *ext_tc, *newptr; + size_t len; + + if ((termbuf = malloc(1024)) == NULL) + goto error; + + if (tgetent(termbuf, tname) == 1) { + /* check for ZZ capability, indicating termcap truncated */ + zz_ptr = zz; + if (tgetstr("ZZ", &zz_ptr) != NULL) { + /* it was, fish back the full termcap */ + sscanf(zz, "%p", &ext_tc); + len = strlen(ext_tc) + 1; + if ((newptr = realloc(termbuf, len)) == NULL) + goto error; + + memcpy(newptr, ext_tc, len); + termbuf = newptr; + } + + if (errp) + *errp = 1; + return(0); + } + error: + if (errp) + *errp = 0; + return(-1); +} +#else +#define termbuf ttytype +extern char ttytype[]; +#endif + +int resettermname = 1; + +char * +gettermname(void) +{ + char *tname; + static char **tnamep = 0; + static char **next; + int err; + + if (resettermname) { + resettermname = 0; + if (tnamep && tnamep != unknown) + free(tnamep); + if ((tname = (char *)env_getvalue((const unsigned char *)"TERM")) && + (setupterm(tname, 1, &err) == 0)) { + tnamep = mklist(termbuf, tname); + } else { + if (tname && ((int)strlen(tname) <= 40)) { + unknown[0] = tname; + upcase(tname); + } else + unknown[0] = name_unknown; + tnamep = unknown; + } + next = tnamep; + } + if (*next == 0) + next = tnamep; + return(*next++); +} +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type, send request. + * Terminal speed (send request). + * Local flow control (is request). + * Linemode + */ + +static void +suboption(void) +{ + unsigned char subchar; + + printsub('<', subbuffer, SB_LEN()+2); + switch (subchar = SB_GET()) { + case TELOPT_TTYPE: + if (my_want_state_is_wont(TELOPT_TTYPE)) + return; + if (SB_EOF() || SB_GET() != TELQUAL_SEND) { + return; + } else { + char *name; + unsigned char temp[50]; + int len; + +#ifdef TN3270 + if (tn3270_ttype()) { + return; + } +#endif /* defined(TN3270) */ + name = gettermname(); + len = strlen(name) + 4 + 2; + if (len < NETROOM()) { + sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, + TELQUAL_IS, name, IAC, SE); + ring_supply_data(&netoring, temp, len); + printsub('>', &temp[2], len-2); + } else { + ExitString("No room in buffer for terminal type.\n", 1); + /*NOTREACHED*/ + } + } + break; + case TELOPT_TSPEED: + if (my_want_state_is_wont(TELOPT_TSPEED)) + return; + if (SB_EOF()) + return; + if (SB_GET() == TELQUAL_SEND) { + long osp, isp; + unsigned char temp[50]; + int len; + + TerminalSpeeds(&isp, &osp); + + sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED, + TELQUAL_IS, osp, isp, IAC, SE); + len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ + + if (len < NETROOM()) { + ring_supply_data(&netoring, temp, len); + printsub('>', temp+2, len - 2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + } + break; + case TELOPT_LFLOW: + if (my_want_state_is_wont(TELOPT_LFLOW)) + return; + if (SB_EOF()) + return; + switch(SB_GET()) { + case LFLOW_RESTART_ANY: + restartany = 1; + break; + case LFLOW_RESTART_XON: + restartany = 0; + break; + case LFLOW_ON: + localflow = 1; + break; + case LFLOW_OFF: + localflow = 0; + break; + default: + return; + } + setcommandmode(); + setconnmode(0); + break; + + case TELOPT_LINEMODE: + if (my_want_state_is_wont(TELOPT_LINEMODE)) + return; + if (SB_EOF()) + return; + switch (SB_GET()) { + case WILL: + lm_will(subpointer, SB_LEN()); + break; + case WONT: + lm_wont(subpointer, SB_LEN()); + break; + case DO: + lm_do(subpointer, SB_LEN()); + break; + case DONT: + lm_dont(subpointer, SB_LEN()); + break; + case LM_SLC: + slc(subpointer, SB_LEN()); + break; + case LM_MODE: + lm_mode(subpointer, SB_LEN(), 0); + break; + default: + break; + } + break; + +#ifdef OLD_ENVIRON + case TELOPT_OLD_ENVIRON: +#endif + case TELOPT_NEW_ENVIRON: + if (SB_EOF()) + return; + switch(SB_PEEK()) { + case TELQUAL_IS: + case TELQUAL_INFO: + if (my_want_state_is_dont(subchar)) + return; + break; + case TELQUAL_SEND: + if (my_want_state_is_wont(subchar)) { + return; + } + break; + default: + return; + } + env_opt(subpointer, SB_LEN()); + break; + + case TELOPT_XDISPLOC: + if (my_want_state_is_wont(TELOPT_XDISPLOC)) + return; + if (SB_EOF()) + return; + if (SB_GET() == TELQUAL_SEND) { + unsigned char temp[50], *dp; + int len; + + if ((dp = env_getvalue((const unsigned char *)"DISPLAY")) == NULL) { + /* + * Something happened, we no longer have a DISPLAY + * variable. So, turn off the option. + */ + send_wont(TELOPT_XDISPLOC, 1); + break; + } + sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC, + TELQUAL_IS, dp, IAC, SE); + len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ + + if (len < NETROOM()) { + ring_supply_data(&netoring, temp, len); + printsub('>', temp+2, len - 2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + } + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: { + if (!autologin) + break; + if (SB_EOF()) + return; + switch(SB_GET()) { + case TELQUAL_IS: + if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) + return; + auth_is(subpointer, SB_LEN()); + break; + case TELQUAL_SEND: + if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) + return; + auth_send(subpointer, SB_LEN()); + break; + case TELQUAL_REPLY: + if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) + return; + auth_reply(subpointer, SB_LEN()); + break; + case TELQUAL_NAME: + if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) + return; + auth_name(subpointer, SB_LEN()); + break; + } + } + break; +#endif +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + if (SB_EOF()) + return; + switch(SB_GET()) { + case ENCRYPT_START: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_start(subpointer, SB_LEN()); + break; + case ENCRYPT_END: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_end(); + break; + case ENCRYPT_SUPPORT: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_support(subpointer, SB_LEN()); + break; + case ENCRYPT_REQSTART: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_request_start(subpointer, SB_LEN()); + break; + case ENCRYPT_REQEND: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + /* + * We can always send an REQEND so that we cannot + * get stuck encrypting. We should only get this + * if we have been able to get in the correct mode + * anyhow. + */ + encrypt_request_end(); + break; + case ENCRYPT_IS: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_is(subpointer, SB_LEN()); + break; + case ENCRYPT_REPLY: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_reply(subpointer, SB_LEN()); + break; + case ENCRYPT_ENC_KEYID: + if (my_want_state_is_dont(TELOPT_ENCRYPT)) + return; + encrypt_enc_keyid(subpointer, SB_LEN()); + break; + case ENCRYPT_DEC_KEYID: + if (my_want_state_is_wont(TELOPT_ENCRYPT)) + return; + encrypt_dec_keyid(subpointer, SB_LEN()); + break; + default: + break; + } + break; +#endif /* ENCRYPTION */ + default: + break; + } +} + +static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE }; + +void +lm_will(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_will: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: /* We shouldn't ever get this... */ + default: + str_lm[3] = DONT; + str_lm[4] = cmd[0]; + if ((size_t)NETROOM() > sizeof(str_lm)) { + ring_supply_data(&netoring, str_lm, sizeof(str_lm)); + printsub('>', &str_lm[2], sizeof(str_lm)-2); + } +/*@*/ else printf("lm_will: not enough room in buffer\n"); + break; + } +} + +void +lm_wont(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_wont: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: /* We shouldn't ever get this... */ + default: + /* We are always DONT, so don't respond */ + return; + } +} + +void +lm_do(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_do: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: + default: + str_lm[3] = WONT; + str_lm[4] = cmd[0]; + if ((size_t)NETROOM() > sizeof(str_lm)) { + ring_supply_data(&netoring, str_lm, sizeof(str_lm)); + printsub('>', &str_lm[2], sizeof(str_lm)-2); + } +/*@*/ else printf("lm_do: not enough room in buffer\n"); + break; + } +} + +void +lm_dont(unsigned char *cmd, int len) +{ + if (len < 1) { +/*@*/ printf("lm_dont: no command!!!\n"); /* Should not happen... */ + return; + } + switch(cmd[0]) { + case LM_FORWARDMASK: + default: + /* we are always WONT, so don't respond */ + break; + } +} + +static unsigned char str_lm_mode[] = { + IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE +}; + +void +lm_mode(unsigned char *cmd, int len, int init) +{ + if (len != 1) + return; + if ((linemode&MODE_MASK&~MODE_ACK) == *cmd) + return; + if (*cmd&MODE_ACK) + return; + linemode = *cmd&(MODE_MASK&~MODE_ACK); + str_lm_mode[4] = linemode; + if (!init) + str_lm_mode[4] |= MODE_ACK; + if ((size_t)NETROOM() > sizeof(str_lm_mode)) { + ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode)); + printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2); + } +/*@*/ else printf("lm_mode: not enough room in buffer\n"); + setconnmode(0); /* set changed mode */ +} + + + +/* + * slc() + * Handle special character suboption of LINEMODE. + */ + +struct spc { + cc_t val; + cc_t *valp; + char flags; /* Current flags & level */ + char mylevel; /* Maximum level & flags */ +} spc_data[NSLC+1]; + +#define SLC_IMPORT 0 +#define SLC_EXPORT 1 +#define SLC_RVALUE 2 +static int slc_mode = SLC_EXPORT; + +void +slc_init(void) +{ + struct spc *spcp; + + localchars = 1; + for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) { + spcp->val = 0; + spcp->valp = 0; + spcp->flags = spcp->mylevel = SLC_NOSUPPORT; + } + +#define initfunc(func, flags) { \ + spcp = &spc_data[func]; \ + if ((spcp->valp = tcval(func)) != NULL){ \ + spcp->val = *spcp->valp; \ + spcp->mylevel = SLC_VARIABLE|flags; \ + } else { \ + spcp->val = 0; \ + spcp->mylevel = SLC_DEFAULT; \ + } \ + } + + initfunc(SLC_SYNCH, 0); + /* No BRK */ + initfunc(SLC_AO, 0); + initfunc(SLC_AYT, 0); + /* No EOR */ + initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT); + initfunc(SLC_EOF, 0); + initfunc(SLC_SUSP, SLC_FLUSHIN); + initfunc(SLC_EC, 0); + initfunc(SLC_EL, 0); + initfunc(SLC_EW, 0); + initfunc(SLC_RP, 0); + initfunc(SLC_LNEXT, 0); + initfunc(SLC_XON, 0); + initfunc(SLC_XOFF, 0); + initfunc(SLC_FORW1, 0); + initfunc(SLC_FORW2, 0); + /* No FORW2 */ + + initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT); +#undef initfunc + + if (slc_mode == SLC_EXPORT) + slc_export(); + else + slc_import(1); + +} + +void +slcstate(void) +{ + printf("Special characters are %s values\n", + slc_mode == SLC_IMPORT ? "remote default" : + slc_mode == SLC_EXPORT ? "local" : + "remote"); +} + +void +slc_mode_export(int n) +{ + slc_mode = SLC_EXPORT; + if (my_state_is_will(TELOPT_LINEMODE)) + slc_export(); +} + +void +slc_mode_import(int def) +{ + slc_mode = def ? SLC_IMPORT : SLC_RVALUE; + if (my_state_is_will(TELOPT_LINEMODE)) + slc_import(def); +} + +unsigned char slc_import_val[] = { + IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE +}; +unsigned char slc_import_def[] = { + IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE +}; + +void +slc_import(int def) +{ + if ((size_t)NETROOM() > sizeof(slc_import_val)) { + if (def) { + ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def)); + printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2); + } else { + ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val)); + printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2); + } + } +/*@*/ else printf("slc_import: not enough room\n"); +} + +void +slc_export(void) +{ + struct spc *spcp; + + TerminalDefaultChars(); + + slc_start_reply(); + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (spcp->mylevel != SLC_NOSUPPORT) { + if (spcp->val == (cc_t)(_POSIX_VDISABLE)) + spcp->flags = SLC_NOSUPPORT; + else + spcp->flags = spcp->mylevel; + if (spcp->valp) + spcp->val = *spcp->valp; + slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); + } + } + slc_end_reply(); + (void)slc_update(); + setconnmode(1); /* Make sure the character values are set */ +} + +void +slc(unsigned char *cp, int len) +{ + struct spc *spcp; + int func,level; + + slc_start_reply(); + + for (; len >= 3; len -=3, cp +=3) { + + func = cp[SLC_FUNC]; + + if (func == 0) { + /* + * Client side: always ignore 0 function. + */ + continue; + } + if (func > NSLC) { + if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT) + slc_add_reply(func, SLC_NOSUPPORT, 0); + continue; + } + + spcp = &spc_data[func]; + + level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK); + + if ((cp[SLC_VALUE] == (unsigned char)spcp->val) && + ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) { + continue; + } + + if (level == (SLC_DEFAULT|SLC_ACK)) { + /* + * This is an error condition, the SLC_ACK + * bit should never be set for the SLC_DEFAULT + * level. Our best guess to recover is to + * ignore the SLC_ACK bit. + */ + cp[SLC_FLAGS] &= ~SLC_ACK; + } + + if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) { + spcp->val = (cc_t)cp[SLC_VALUE]; + spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */ + continue; + } + + level &= ~SLC_ACK; + + if (level <= (spcp->mylevel&SLC_LEVELBITS)) { + spcp->flags = cp[SLC_FLAGS]|SLC_ACK; + spcp->val = (cc_t)cp[SLC_VALUE]; + } + if (level == SLC_DEFAULT) { + if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT) + spcp->flags = spcp->mylevel; + else + spcp->flags = SLC_NOSUPPORT; + } + slc_add_reply(func, spcp->flags, spcp->val); + } + slc_end_reply(); + if (slc_update()) + setconnmode(1); /* set the new character values */ +} + +void +slc_check(void) +{ + struct spc *spcp; + + slc_start_reply(); + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (spcp->valp && spcp->val != *spcp->valp) { + spcp->val = *spcp->valp; + if (spcp->val == (cc_t)(_POSIX_VDISABLE)) + spcp->flags = SLC_NOSUPPORT; + else + spcp->flags = spcp->mylevel; + slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); + } + } + slc_end_reply(); + setconnmode(1); +} + + +unsigned char slc_reply[128]; +unsigned char *slc_replyp; + +void +slc_start_reply(void) +{ + slc_replyp = slc_reply; + *slc_replyp++ = IAC; + *slc_replyp++ = SB; + *slc_replyp++ = TELOPT_LINEMODE; + *slc_replyp++ = LM_SLC; +} + +void +slc_add_reply(unsigned int func, unsigned int flags, cc_t value) +{ + if ((size_t)(slc_replyp - slc_reply) + 6 > sizeof(slc_reply)) + return; + if ((*slc_replyp++ = func) == IAC) + *slc_replyp++ = IAC; + if ((*slc_replyp++ = flags) == IAC) + *slc_replyp++ = IAC; + if ((*slc_replyp++ = (unsigned char)value) == IAC) + *slc_replyp++ = IAC; +} + +void +slc_end_reply(void) +{ + int len; + + len = slc_replyp - slc_reply; + if (len <= 4 || ((size_t)len + 2 > sizeof(slc_reply))) + return; + *slc_replyp++ = IAC; + *slc_replyp++ = SE; + len += 2; + if (NETROOM() > len) { + ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply); + printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2); + } +/*@*/else printf("slc_end_reply: not enough room\n"); +} + +int +slc_update(void) +{ + struct spc *spcp; + int need_update = 0; + + for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { + if (!(spcp->flags&SLC_ACK)) + continue; + spcp->flags &= ~SLC_ACK; + if (spcp->valp && (*spcp->valp != spcp->val)) { + *spcp->valp = spcp->val; + need_update = 1; + } + } + return(need_update); +} + +#ifdef OLD_ENVIRON +# ifdef ENV_HACK +/* + * Earlier version of telnet/telnetd from the BSD code had + * the definitions of VALUE and VAR reversed. To ensure + * maximum interoperability, we assume that the server is + * an older BSD server, until proven otherwise. The newer + * BSD servers should be able to handle either definition, + * so it is better to use the wrong values if we don't + * know what type of server it is. + */ +int env_auto = 1; +int old_env_var = OLD_ENV_VAR; +int old_env_value = OLD_ENV_VALUE; +# else +# define old_env_var OLD_ENV_VAR +# define old_env_value OLD_ENV_VALUE +# endif +#endif + +void +env_opt(unsigned char *buf, int len) +{ + unsigned char *ep = 0, *epc = 0; + int i; + + switch(buf[0]&0xff) { + case TELQUAL_SEND: + env_opt_start(); + if (len == 1) { + env_opt_add(NULL); + } else for (i = 1; i < len; i++) { + switch (buf[i]&0xff) { +#ifdef OLD_ENVIRON + case OLD_ENV_VAR: +# ifdef ENV_HACK + if (telopt_environ == TELOPT_OLD_ENVIRON + && env_auto) { + /* Server has the same definitions */ + old_env_var = OLD_ENV_VAR; + old_env_value = OLD_ENV_VALUE; + } + /* FALL THROUGH */ +# endif + case OLD_ENV_VALUE: + /* + * Although OLD_ENV_VALUE is not legal, we will + * still recognize it, just in case it is an + * old server that has VAR & VALUE mixed up... + */ + /* FALL THROUGH */ +#else + case NEW_ENV_VAR: +#endif + case ENV_USERVAR: + if (ep) { + *epc = 0; + env_opt_add(ep); + } + ep = epc = &buf[i+1]; + break; + case ENV_ESC: + i++; + /*FALL THROUGH*/ + default: + if (epc) + *epc++ = buf[i]; + break; + } + } + if (ep) { + *epc = 0; + env_opt_add(ep); + } + env_opt_end(1); + break; + + case TELQUAL_IS: + case TELQUAL_INFO: + /* Ignore for now. We shouldn't get it anyway. */ + break; + + default: + break; + } +} + +#define OPT_REPLY_SIZE 256 +unsigned char *opt_reply; +unsigned char *opt_replyp; +unsigned char *opt_replyend; + +void +env_opt_start(void) +{ + unsigned char *p; + + if (opt_reply) { + p = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE); + if (p == NULL) + free(opt_reply); + } else + p = (unsigned char *)malloc(OPT_REPLY_SIZE); + opt_reply = p; + if (opt_reply == NULL) { +/*@*/ printf("env_opt_start: malloc()/realloc() failed!!!\n"); + opt_reply = opt_replyp = opt_replyend = NULL; + return; + } + opt_replyp = opt_reply; + opt_replyend = opt_reply + OPT_REPLY_SIZE; + *opt_replyp++ = IAC; + *opt_replyp++ = SB; + *opt_replyp++ = telopt_environ; + *opt_replyp++ = TELQUAL_IS; +} + +void +env_opt_start_info(void) +{ + env_opt_start(); + if (opt_replyp) + opt_replyp[-1] = TELQUAL_INFO; +} + +void +env_opt_add(unsigned char *ep) +{ + unsigned char *vp, c; + unsigned int len, olen, elen; + + if (opt_reply == NULL) /*XXX*/ + return; /*XXX*/ + + if (ep == NULL || *ep == '\0') { + /* Send user defined variables first. */ + env_default(1, 0); + while ((ep = env_default(0, 0)) != NULL) + env_opt_add(ep); + + /* Now add the list of well know variables. */ + env_default(1, 1); + while ((ep = env_default(0, 1)) != NULL) + env_opt_add(ep); + return; + } + vp = env_getvalue(ep); + elen = 2 * (vp ? strlen((char *)vp) : 0) + + 2 * strlen((char *)ep) + 6; + if ((unsigned int)(opt_replyend - opt_replyp) < elen) + { + unsigned char *p; + len = opt_replyend - opt_reply + elen; + olen = opt_replyp - opt_reply; + p = (unsigned char *)realloc(opt_reply, len); + if (p == NULL) + free(opt_reply); + opt_reply = p; + if (opt_reply == NULL) { +/*@*/ printf("env_opt_add: realloc() failed!!!\n"); + opt_reply = opt_replyp = opt_replyend = NULL; + return; + } + opt_replyp = opt_reply + olen; + opt_replyend = opt_reply + len; + } + if (opt_welldefined(ep)) +#ifdef OLD_ENVIRON + if (telopt_environ == TELOPT_OLD_ENVIRON) + *opt_replyp++ = old_env_var; + else +#endif + *opt_replyp++ = NEW_ENV_VAR; + else + *opt_replyp++ = ENV_USERVAR; + for (;;) { + while ((c = *ep++) != '\0') { + switch(c&0xff) { + case IAC: + *opt_replyp++ = IAC; + break; + case NEW_ENV_VAR: + case NEW_ENV_VALUE: + case ENV_ESC: + case ENV_USERVAR: + *opt_replyp++ = ENV_ESC; + break; + } + *opt_replyp++ = c; + } + if ((ep = vp) != NULL) { +#ifdef OLD_ENVIRON + if (telopt_environ == TELOPT_OLD_ENVIRON) + *opt_replyp++ = old_env_value; + else +#endif + *opt_replyp++ = NEW_ENV_VALUE; + vp = NULL; + } else + break; + } +} + +int +opt_welldefined(const char *ep) +{ + if ((strcmp(ep, "USER") == 0) || + (strcmp(ep, "DISPLAY") == 0) || + (strcmp(ep, "PRINTER") == 0) || + (strcmp(ep, "SYSTEMTYPE") == 0) || + (strcmp(ep, "JOB") == 0) || + (strcmp(ep, "ACCT") == 0)) + return(1); + return(0); +} +void +env_opt_end(int emptyok) +{ + int len; + + len = opt_replyp - opt_reply + 2; + if (emptyok || len > 6) { + *opt_replyp++ = IAC; + *opt_replyp++ = SE; + if (NETROOM() > len) { + ring_supply_data(&netoring, opt_reply, len); + printsub('>', &opt_reply[2], len - 2); + } +/*@*/ else printf("slc_end_reply: not enough room\n"); + } + if (opt_reply) { + free(opt_reply); + opt_reply = opt_replyp = opt_replyend = NULL; + } +} + + + +int +telrcv(void) +{ + int c; + int scc; + unsigned char *sbp = NULL; + int count; + int returnValue = 0; + + scc = 0; + count = 0; + while (TTYROOM() > 2) { + if (scc == 0) { + if (count) { + ring_consumed(&netiring, count); + returnValue = 1; + count = 0; + } + sbp = netiring.consume; + scc = ring_full_consecutive(&netiring); + if (scc == 0) { + /* No more data coming in */ + break; + } + } + + c = *sbp++ & 0xff, scc--; count++; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + + switch (telrcv_state) { + + case TS_CR: + telrcv_state = TS_DATA; + if (c == '\0') { + break; /* Ignore \0 after CR */ + } + else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) { + TTYADD(c); + break; + } + /* Else, fall through */ + + case TS_DATA: + if (c == IAC) { + telrcv_state = TS_IAC; + break; + } +# if defined(TN3270) + if (In3270) { + *Ifrontp++ = c; + while (scc > 0) { + c = *sbp++ & 0377, scc--; count++; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + if (c == IAC) { + telrcv_state = TS_IAC; + break; + } + *Ifrontp++ = c; + } + } else +# endif /* defined(TN3270) */ + /* + * The 'crmod' hack (see following) is needed + * since we can't * set CRMOD on output only. + * Machines like MULTICS like to send \r without + * \n; since we must turn off CRMOD to get proper + * input, the mapping is done here (sigh). + */ + if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) { + if (scc > 0) { + c = *sbp&0xff; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + if (c == 0) { + sbp++, scc--; count++; + /* a "true" CR */ + TTYADD('\r'); + } else if (my_want_state_is_dont(TELOPT_ECHO) && + (c == '\n')) { + sbp++, scc--; count++; + TTYADD('\n'); + } else { +#ifdef ENCRYPTION + if (decrypt_input) + (*decrypt_input)(-1); +#endif /* ENCRYPTION */ + + TTYADD('\r'); + if (crmod) { + TTYADD('\n'); + } + } + } else { + telrcv_state = TS_CR; + TTYADD('\r'); + if (crmod) { + TTYADD('\n'); + } + } + } else { + TTYADD(c); + } + continue; + + case TS_IAC: +process_iac: + switch (c) { + + case WILL: + telrcv_state = TS_WILL; + continue; + + case WONT: + telrcv_state = TS_WONT; + continue; + + case DO: + telrcv_state = TS_DO; + continue; + + case DONT: + telrcv_state = TS_DONT; + continue; + + case DM: + /* + * We may have missed an urgent notification, + * so make sure we flush whatever is in the + * buffer currently. + */ + printoption("RCVD", IAC, DM); + SYNCHing = 1; + (void) ttyflush(1); + SYNCHing = stilloob(); + settimer(gotDM); + break; + + case SB: + SB_CLEAR(); + telrcv_state = TS_SB; + continue; + +# if defined(TN3270) + case EOR: + if (In3270) { + if (Ibackp == Ifrontp) { + Ibackp = Ifrontp = Ibuf; + ISend = 0; /* should have been! */ + } else { + Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); + ISend = 1; + } + } + printoption("RCVD", IAC, EOR); + break; +# endif /* defined(TN3270) */ + + case IAC: +# if !defined(TN3270) + TTYADD(IAC); +# else /* !defined(TN3270) */ + if (In3270) { + *Ifrontp++ = IAC; + } else { + TTYADD(IAC); + } +# endif /* !defined(TN3270) */ + break; + + case NOP: + case GA: + default: + printoption("RCVD", IAC, c); + break; + } + telrcv_state = TS_DATA; + continue; + + case TS_WILL: + printoption("RCVD", WILL, c); + willoption(c); + SetIn3270(); + telrcv_state = TS_DATA; + continue; + + case TS_WONT: + printoption("RCVD", WONT, c); + wontoption(c); + SetIn3270(); + telrcv_state = TS_DATA; + continue; + + case TS_DO: + printoption("RCVD", DO, c); + dooption(c); + SetIn3270(); + if (c == TELOPT_NAWS) { + sendnaws(); + } else if (c == TELOPT_LFLOW) { + localflow = 1; + setcommandmode(); + setconnmode(0); + } + telrcv_state = TS_DATA; + continue; + + case TS_DONT: + printoption("RCVD", DONT, c); + dontoption(c); + flushline = 1; + setconnmode(0); /* set new tty mode (maybe) */ + SetIn3270(); + telrcv_state = TS_DATA; + continue; + + case TS_SB: + if (c == IAC) { + telrcv_state = TS_SE; + } else { + SB_ACCUM(c); + } + continue; + + case TS_SE: + if (c != SE) { + if (c != IAC) { + /* + * This is an error. We only expect to get + * "IAC IAC" or "IAC SE". Several things may + * have happened. An IAC was not doubled, the + * IAC SE was left off, or another option got + * inserted into the suboption are all possibilities. + * If we assume that the IAC was not doubled, + * and really the IAC SE was left off, we could + * get into an infinite loop here. So, instead, + * we terminate the suboption, and process the + * partial suboption if we can. + */ + SB_ACCUM(IAC); + SB_ACCUM(c); + subpointer -= 2; + SB_TERM(); + + printoption("In SUBOPTION processing, RCVD", IAC, c); + suboption(); /* handle sub-option */ + SetIn3270(); + telrcv_state = TS_IAC; + goto process_iac; + } + SB_ACCUM(c); + telrcv_state = TS_SB; + } else { + SB_ACCUM(IAC); + SB_ACCUM(SE); + subpointer -= 2; + SB_TERM(); + suboption(); /* handle sub-option */ + SetIn3270(); + telrcv_state = TS_DATA; + } + } + } + if (count) + ring_consumed(&netiring, count); + return returnValue||count; +} + +static int bol = 1, local = 0; + +int +rlogin_susp(void) +{ + if (local) { + local = 0; + bol = 1; + command(0, "z\n", 2); + return(1); + } + return(0); +} + +static int +telsnd(void) +{ + int tcc; + int count; + int returnValue = 0; + unsigned char *tbp = NULL; + + tcc = 0; + count = 0; + while (NETROOM() > 2) { + int sc; + int c; + + if (tcc == 0) { + if (count) { + ring_consumed(&ttyiring, count); + returnValue = 1; + count = 0; + } + tbp = ttyiring.consume; + tcc = ring_full_consecutive(&ttyiring); + if (tcc == 0) { + break; + } + } + c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; + if (rlogin != _POSIX_VDISABLE) { + if (bol) { + bol = 0; + if (sc == rlogin) { + local = 1; + continue; + } + } else if (local) { + local = 0; + if (sc == '.' || c == termEofChar) { + bol = 1; + command(0, "close\n", 6); + continue; + } + if (sc == termSuspChar) { + bol = 1; + command(0, "z\n", 2); + continue; + } + if (sc == escape) { + command(0, (char *)tbp, tcc); + bol = 1; + count += tcc; + tcc = 0; + flushline = 1; + break; + } + if (sc != rlogin) { + ++tcc; + --tbp; + --count; + c = sc = rlogin; + } + } + if ((sc == '\n') || (sc == '\r')) + bol = 1; + } else if (sc == escape && escape != _POSIX_VDISABLE) { + /* + * Double escape is a pass through of a single escape character. + */ + if (tcc && strip(*tbp) == escape) { + tbp++; + tcc--; + count++; + bol = 0; + } else { + command(0, (char *)tbp, tcc); + bol = 1; + count += tcc; + tcc = 0; + flushline = 1; + break; + } + } else + bol = 0; +#ifdef KLUDGELINEMODE + if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) { + if (tcc > 0 && strip(*tbp) == echoc) { + tcc--; tbp++; count++; + } else { + dontlecho = !dontlecho; + settimer(echotoggle); + setconnmode(0); + flushline = 1; + break; + } + } +#endif + if (sc != _POSIX_VDISABLE && MODE_LOCAL_CHARS(globalmode)) { + if (TerminalSpecialChars(sc) == 0) { + bol = 1; + break; + } + } + if (my_want_state_is_wont(TELOPT_BINARY)) { + switch (c) { + case '\n': + /* + * If we are in CRMOD mode (\r ==> \n) + * on our local machine, then probably + * a newline (unix) is CRLF (TELNET). + */ + if (MODE_LOCAL_CHARS(globalmode)) { + NETADD('\r'); + } + NETADD('\n'); + bol = flushline = 1; + break; + case '\r': + if (!crlf) { + NET2ADD('\r', '\0'); + } else { + NET2ADD('\r', '\n'); + } + bol = flushline = 1; + break; + case IAC: + NET2ADD(IAC, IAC); + break; + default: + NETADD(c); + break; + } + } else if (c == IAC) { + NET2ADD(IAC, IAC); + } else { + NETADD(c); + } + } + if (count) + ring_consumed(&ttyiring, count); + return returnValue||count; /* Non-zero if we did anything */ +} + +/* + * Scheduler() + * + * Try to do something. + * + * If we do something useful, return 1; else return 0. + * + */ + + +int +Scheduler(int block) /* should we block in the select ? */ +{ + /* One wants to be a bit careful about setting returnValue + * to one, since a one implies we did some useful work, + * and therefore probably won't be called to block next + * time (TN3270 mode only). + */ + int returnValue; + int netin, netout, netex, ttyin, ttyout; + + /* Decide which rings should be processed */ + + netout = ring_full_count(&netoring) && + (flushline || + (my_want_state_is_wont(TELOPT_LINEMODE) +#ifdef KLUDGELINEMODE + && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA)) +#endif + ) || + my_want_state_is_will(TELOPT_BINARY)); + ttyout = ring_full_count(&ttyoring); + +#ifdef TN3270 + ttyin = ring_empty_count(&ttyiring) && (clienteof == 0) && (shell_active == 0); +#else /* defined(TN3270) */ + ttyin = ring_empty_count(&ttyiring) && (clienteof == 0); +#endif /* defined(TN3270) */ + +#ifdef TN3270 + netin = ring_empty_count(&netiring); +# else /* !defined(TN3270) */ + netin = !ISend && ring_empty_count(&netiring); +# endif /* !defined(TN3270) */ + + netex = !SYNCHing; + + /* If we have seen a signal recently, reset things */ +# ifdef TN3270 + if (HaveInput) { + HaveInput = 0; + (void) signal(SIGIO, inputAvailable); + } +#endif /* defined(TN3270) */ + + /* Call to system code to process rings */ + + returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); + + /* Now, look at the input rings, looking for work to do. */ + + if (ring_full_count(&ttyiring)) { +# if defined(TN3270) + if (In3270) { + int c; + + c = DataFromTerminal(ttyiring.consume, + ring_full_consecutive(&ttyiring)); + if (c) { + returnValue = 1; + ring_consumed(&ttyiring, c); + } + } else { +# endif /* defined(TN3270) */ + returnValue |= telsnd(); +# if defined(TN3270) + } +# endif /* defined(TN3270) */ + } + + if (ring_full_count(&netiring)) { +# if !defined(TN3270) + returnValue |= telrcv(); +# else /* !defined(TN3270) */ + returnValue = Push3270(); +# endif /* !defined(TN3270) */ + } + return returnValue; +} + +/* + * Select from tty and network... + */ +void +telnet(const char *user) +{ + sys_telnet_init(); + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) + { + static char local_host[MAXHOSTNAMELEN + 1] = { 0 }; + + if (!local_host[0]) { + gethostname(local_host, sizeof(local_host)); + local_host[sizeof(local_host)-1] = 0; + } + auth_encrypt_init(local_host, hostname, "TELNET", 0); + auth_encrypt_user(user); + } +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ +# if !defined(TN3270) + if (telnetport) { +#ifdef AUTHENTICATION + if (autologin) + send_will(TELOPT_AUTHENTICATION, 1); +#endif +#ifdef ENCRYPTION + send_do(TELOPT_ENCRYPT, 1); + send_will(TELOPT_ENCRYPT, 1); +#endif /* ENCRYPTION */ + send_do(TELOPT_SGA, 1); + send_will(TELOPT_TTYPE, 1); + send_will(TELOPT_NAWS, 1); + send_will(TELOPT_TSPEED, 1); + send_will(TELOPT_LFLOW, 1); + send_will(TELOPT_LINEMODE, 1); + send_will(TELOPT_NEW_ENVIRON, 1); + send_do(TELOPT_STATUS, 1); + if (env_getvalue((const unsigned char *)"DISPLAY")) + send_will(TELOPT_XDISPLOC, 1); + if (eight) + tel_enter_binary(eight); + } +# endif /* !defined(TN3270) */ + +# if !defined(TN3270) + for (;;) { + int schedValue; + + while ((schedValue = Scheduler(0)) != 0) { + if (schedValue == -1) { + setcommandmode(); + return; + } + } + + if (Scheduler(1) == -1) { + setcommandmode(); + return; + } + } +# else /* !defined(TN3270) */ + for (;;) { + int schedValue; + + while (!In3270 && !shell_active) { + if (Scheduler(1) == -1) { + setcommandmode(); + return; + } + } + + while ((schedValue = Scheduler(0)) != 0) { + if (schedValue == -1) { + setcommandmode(); + return; + } + } + /* If there is data waiting to go out to terminal, don't + * schedule any more data for the terminal. + */ + if (ring_full_count(&ttyoring)) { + schedValue = 1; + } else { + if (shell_active) { + if (shell_continue() == 0) { + ConnectScreen(); + } + } else if (In3270) { + schedValue = DoTerminalOutput(); + } + } + if (schedValue && (shell_active == 0)) { + if (Scheduler(1) == -1) { + setcommandmode(); + return; + } + } + } +# endif /* !defined(TN3270) */ +} + +#if 0 /* XXX - this not being in is a bug */ +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ + +static char * +nextitem(char *current) +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} +#endif /* 0 */ + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ + +static void +netclear(void) +{ +#if 0 /* XXX */ + char *thisitem, *next; + char *good; +#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ + ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) + + thisitem = netobuf; + + while ((next = nextitem(thisitem)) <= netobuf.send) { + thisitem = next; + } + + /* Now, thisitem is first before/at boundary. */ + + good = netobuf; /* where the good bytes go */ + + while (netoring.add > thisitem) { + if (wewant(thisitem)) { + int length; + + next = thisitem; + do { + next = nextitem(next); + } while (wewant(next) && (nfrontp > next)); + length = next-thisitem; + memmove(good, thisitem, length); + good += length; + thisitem = next; + } else { + thisitem = nextitem(thisitem); + } + } + +#endif /* 0 */ +} + +/* + * These routines add various telnet commands to the data stream. + */ + +static void +doflush(void) +{ + NET2ADD(IAC, DO); + NETADD(TELOPT_TM); + flushline = 1; + flushout = 1; + (void) ttyflush(1); /* Flush/drop output */ + /* do printoption AFTER flush, otherwise the output gets tossed... */ + printoption("SENT", DO, TELOPT_TM); +} + +void +xmitAO(void) +{ + NET2ADD(IAC, AO); + printoption("SENT", IAC, AO); + if (autoflush) { + doflush(); + } +} + + +void +xmitEL(void) +{ + NET2ADD(IAC, EL); + printoption("SENT", IAC, EL); +} + +void +xmitEC(void) +{ + NET2ADD(IAC, EC); + printoption("SENT", IAC, EC); +} + + +int +dosynch(char *s) +{ + netclear(); /* clear the path to the network */ + NETADD(IAC); + setneturg(); + NETADD(DM); + printoption("SENT", IAC, DM); + return 1; +} + +int want_status_response = 0; + +int +get_status(char *s) +{ + unsigned char tmp[16]; + unsigned char *cp; + + if (my_want_state_is_dont(TELOPT_STATUS)) { + printf("Remote side does not support STATUS option\n"); + return 0; + } + cp = tmp; + + *cp++ = IAC; + *cp++ = SB; + *cp++ = TELOPT_STATUS; + *cp++ = TELQUAL_SEND; + *cp++ = IAC; + *cp++ = SE; + if (NETROOM() >= cp - tmp) { + ring_supply_data(&netoring, tmp, cp-tmp); + printsub('>', tmp+2, cp - tmp - 2); + } + ++want_status_response; + return 1; +} + +void +intp(void) +{ + NET2ADD(IAC, IP); + printoption("SENT", IAC, IP); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendbrk(void) +{ + NET2ADD(IAC, BREAK); + printoption("SENT", IAC, BREAK); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendabort(void) +{ + NET2ADD(IAC, ABORT); + printoption("SENT", IAC, ABORT); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendsusp(void) +{ + NET2ADD(IAC, SUSP); + printoption("SENT", IAC, SUSP); + flushline = 1; + if (autoflush) { + doflush(); + } + if (autosynch) { + dosynch(NULL); + } +} + +void +sendeof(void) +{ + NET2ADD(IAC, xEOF); + printoption("SENT", IAC, xEOF); +} + +void +sendayt(void) +{ + NET2ADD(IAC, AYT); + printoption("SENT", IAC, AYT); +} + +/* + * Send a window size update to the remote system. + */ + +void +sendnaws(void) +{ + long rows, cols; + unsigned char tmp[16]; + unsigned char *cp; + + if (my_state_is_wont(TELOPT_NAWS)) + return; + +#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \ + if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; } + + if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */ + return; + } + + cp = tmp; + + *cp++ = IAC; + *cp++ = SB; + *cp++ = TELOPT_NAWS; + PUTSHORT(cp, cols); + PUTSHORT(cp, rows); + *cp++ = IAC; + *cp++ = SE; + if (NETROOM() >= cp - tmp) { + ring_supply_data(&netoring, tmp, cp-tmp); + printsub('>', tmp+2, cp - tmp - 2); + } +} + +void +tel_enter_binary(int rw) +{ + if (rw&1) + send_do(TELOPT_BINARY, 1); + if (rw&2) + send_will(TELOPT_BINARY, 1); +} + +void +tel_leave_binary(int rw) +{ + if (rw&1) + send_dont(TELOPT_BINARY, 1); + if (rw&2) + send_wont(TELOPT_BINARY, 1); +} diff --git a/usr.bin/telnet/terminal.c b/usr.bin/telnet/terminal.c new file mode 100644 index 000000000..7010e063c --- /dev/null +++ b/usr.bin/telnet/terminal.c @@ -0,0 +1,220 @@ +/* $NetBSD: terminal.c,v 1.15 2005/02/19 23:28:41 christos Exp $ */ + +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)terminal.c 8.2 (Berkeley) 2/16/95"; +#else +__RCSID("$NetBSD: terminal.c,v 1.15 2005/02/19 23:28:41 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include "ring.h" + +#include "externs.h" +#include "types.h" + +#ifdef ENCRYPTION +#include +#endif + +Ring ttyoring, ttyiring; +unsigned char ttyobuf[2*BUFSIZ], ttyibuf[BUFSIZ]; +char line[] = { '\0' }; + +int termdata; /* Debugging flag */ + +/* + * initialize the terminal data structures. + */ + +void +init_terminal(void) +{ + if (ring_init(&ttyoring, ttyobuf, sizeof ttyobuf) != 1) { + exit(1); + } + if (ring_init(&ttyiring, ttyibuf, sizeof ttyibuf) != 1) { + exit(1); + } + autoflush = TerminalAutoFlush(); +} + + +/* + * Send as much data as possible to the terminal, else exits if + * it encounters a permanent failure when writing to the tty. + * + * Return value: + * -1: No useful work done, data waiting to go out. + * 0: No data was waiting, so nothing was done. + * 1: All waiting data was written out. + * n: All data - n was written out. + */ + + +int +ttyflush(int drop) +{ + int n, n0, n1; + + n0 = ring_full_count(&ttyoring); + if ((n1 = n = ring_full_consecutive(&ttyoring)) > 0) { + if (drop) { + TerminalFlushOutput(); + /* we leave 'n' alone! */ + } else { + n = TerminalWrite(ttyoring.consume, n); + } + } + if (n > 0) { + if (termdata && n) { + Dump('>', ttyoring.consume, n); + } + /* + * If we wrote everything, and the full count is + * larger than what we wrote, then write the + * rest of the buffer. + */ + if (n1 == n && n0 > n) { + n1 = n0 - n; + if (!drop) + n1 = TerminalWrite(ttyoring.bottom, n1); + if (n1 > 0) + n += n1; + } + ring_consumed(&ttyoring, n); + } + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) { + return -1; + } else { + ring_consumed(&ttyoring, ring_full_count(&ttyoring)); + setconnmode(0); + setcommandmode(); + NetClose(net); + fprintf(stderr, "Connection closed by foreign host.\n"); + exit(1); + } + } + if (n == n0) { + if (n0) + return -1; + return 0; + } + return n0 - n + 1; +} + + +/* + * These routines decides on what the mode should be (based on the values + * of various global variables). + */ + + +int +getconnmode(void) +{ + extern int linemode; + int mode = 0; +#ifdef KLUDGELINEMODE + extern int kludgelinemode; +#endif + + if (In3270) + return(MODE_FLOW); + + if (my_want_state_is_dont(TELOPT_ECHO)) + mode |= MODE_ECHO; + + if (localflow) + mode |= MODE_FLOW; + + if (my_want_state_is_will(TELOPT_BINARY)) + mode |= MODE_INBIN; + + if (his_want_state_is_will(TELOPT_BINARY)) + mode |= MODE_OUTBIN; + +#ifdef KLUDGELINEMODE + if (kludgelinemode) { + if (my_want_state_is_dont(TELOPT_SGA)) { + mode |= (MODE_TRAPSIG|MODE_EDIT); + if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { + mode &= ~MODE_ECHO; + } + } + return(mode); + } +#endif + if (my_want_state_is_will(TELOPT_LINEMODE)) + mode |= linemode; + return(mode); +} + +void +setconnmode(int force) +{ +#ifdef ENCRYPTION + static int enc_passwd = 0; +#endif + int newmode; + + newmode = getconnmode()|(force?MODE_FORCE:0); + + TerminalNewMode(newmode); + +#ifdef ENCRYPTION + if ((newmode & (MODE_ECHO|MODE_EDIT)) == MODE_EDIT) { + if (my_want_state_is_will(TELOPT_ENCRYPT) + && (enc_passwd == 0) && !encrypt_output) { + encrypt_request_start(0, 0); + enc_passwd = 1; + } + } else { + if (enc_passwd) { + encrypt_request_end(); + enc_passwd = 0; + } + } +#endif /* ENCRYPTION */ +} + + +void +setcommandmode(void) +{ + TerminalNewMode(-1); +} diff --git a/usr.bin/telnet/tn3270.c b/usr.bin/telnet/tn3270.c new file mode 100644 index 000000000..7e7ad665a --- /dev/null +++ b/usr.bin/telnet/tn3270.c @@ -0,0 +1,394 @@ +/* $NetBSD: tn3270.c,v 1.22 2006/10/07 17:27:57 elad Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tn3270.c 8.2 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: tn3270.c,v 1.22 2006/10/07 17:27:57 elad Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include "general.h" +#include "defines.h" +#include "ring.h" +#include "externs.h" + +#ifdef TN3270 + +#include "../ctlr/screen.h" +#include "../ctlr/declare.h" + +#include "../ascii/state.h" + +#include "../general/globals.h" + +#include "../sys_curses/telextrn.h" + +int + HaveInput, /* There is input available to scan */ + cursesdata, /* Do we dump curses data? */ + sigiocount; /* Number of times we got a SIGIO */ + +char tline[200]; +char *transcom = 0; /* transparent mode command (default: none) */ + +char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp; + +static char sb_terminal[] = { IAC, SB, + TELOPT_TTYPE, TELQUAL_IS, + 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2', + IAC, SE }; +#define SBTERMMODEL 13 + +static int + Sent3270TerminalType; /* Have we said we are a 3270? */ + +#endif /* defined(TN3270) */ + + +#ifdef TN3270 +void +init_3270(void) +{ + HaveInput = 0; + sigiocount = 0; + Sent3270TerminalType = 0; + Ifrontp = Ibackp = Ibuf; + init_ctlr(); /* Initialize some things */ + init_keyboard(); + init_screen(); + init_system(); +} +#endif /* defined(TN3270) */ + + +#ifdef TN3270 + +/* + * DataToNetwork - queue up some data to go to network. If "done" is set, + * then when last byte is queued, we add on an IAC EOR sequence (so, + * don't call us with "done" until you want that done...) + * + * We actually do send all the data to the network buffer, since our + * only client needs for us to do that. + */ + +int +DataToNetwork(char *buffer, /* where the data is */ + int count, /* how much to send */ + int done) /* is this the last of a logical block */ +{ + int loop, c; + int origCount; + + origCount = count; + + while (count) { + /* If not enough room for EORs, IACs, etc., wait */ + if (NETROOM() < 6) { + struct pollfd set[1]; + + set[0].fd = net; + set[0].events = POLLOUT; + netflush(); + while (NETROOM() < 6) { + (void) poll(set, 1, INFTIM); + netflush(); + } + } + c = ring_empty_count(&netoring); + if (c > count) { + c = count; + } + loop = c; + while (loop) { + if (((unsigned char)*buffer) == IAC) { + break; + } + buffer++; + loop--; + } + if ((c = c-loop)) { + ring_supply_data(&netoring, buffer-c, c); + count -= c; + } + if (loop) { + NET2ADD(IAC, IAC); + count--; + buffer++; + } + } + + if (done) { + NET2ADD(IAC, EOR); + netflush(); /* try to move along as quickly as ... */ + } + return(origCount - count); +} + + +void +inputAvailable(int signo) +{ + HaveInput = 1; + sigiocount++; +} + +void +outputPurge(void) +{ + (void) ttyflush(1); +} + + +/* + * The following routines are places where the various tn3270 + * routines make calls into telnet.c. + */ + +/* + * DataToTerminal - queue up some data to go to terminal. + * + * Note: there are people who call us and depend on our processing + * *all* the data at one time (thus the poll). + */ + +int +DataToTerminal( + char *buffer, /* where the data is */ + int count) /* how much to send */ +{ + int c; + int origCount; + + origCount = count; + + while (count) { + if (TTYROOM() == 0) { + struct pollfd set[1]; + + set[0].fd = tout; + set[0].events = POLLOUT; + (void) ttyflush(0); + while (TTYROOM() == 0) { + (void) poll(set, 1, INFTIM); + (void) ttyflush(0); + } + } + c = TTYROOM(); + if (c > count) { + c = count; + } + ring_supply_data(&ttyoring, buffer, c); + count -= c; + buffer += c; + } + return(origCount); +} + + +/* + * Push3270 - Try to send data along the 3270 output (to screen) direction. + */ + +int +Push3270(void) +{ + int save = ring_full_count(&netiring); + + if (save) { + if (Ifrontp+save > Ibuf+sizeof Ibuf) { + if (Ibackp != Ibuf) { + memmove(Ibuf, Ibackp, Ifrontp-Ibackp); + Ifrontp -= (Ibackp-Ibuf); + Ibackp = Ibuf; + } + } + if (Ifrontp+save < Ibuf+sizeof Ibuf) { + (void)telrcv(); + } + } + return save != ring_full_count(&netiring); +} + + +/* + * Finish3270 - get the last dregs of 3270 data out to the terminal + * before quitting. + */ + +void +Finish3270(void) +{ + while (Push3270() || !DoTerminalOutput()) { + HaveInput = 0; + ; + } +} + + +/* StringToTerminal - output a null terminated string to the terminal */ + +void +StringToTerminal(char *s) +{ + int count; + + count = strlen(s); + if (count) { + (void) DataToTerminal(s, count); /* we know it always goes... */ + } +} + + +/* _putchar - output a single character to the terminal. This name is so that + * curses(3x) can call us to send out data. + */ + +int +_putchar(int cc) +{ + char c = (char)cc; + if (cursesdata) { + Dump('>', &c, 1); + } + if (!TTYROOM()) { + (void) DataToTerminal(&c, 1); + } else { + TTYADD(c); + } + + return (0); +} + +void +SetIn3270(void) +{ + if (Sent3270TerminalType && my_want_state_is_will(TELOPT_BINARY) + && my_want_state_is_do(TELOPT_BINARY) && !donebinarytoggle) { + if (!In3270) { + In3270 = 1; + Init3270(); /* Initialize 3270 functions */ + /* initialize terminal key mapping */ + InitTerminal(); /* Start terminal going */ + setconnmode(0); + } + } else { + if (In3270) { + StopScreen(1); + In3270 = 0; + Stop3270(); /* Tell 3270 we aren't here anymore */ + setconnmode(0); + } + } +} + +/* + * tn3270_ttype() + * + * Send a response to a terminal type negotiation. + * + * Return '0' if no more responses to send; '1' if a response sent. + */ + +int +tn3270_ttype(void) +{ + /* + * Try to send a 3270 type terminal name. Decide which one based + * on the format of our screen, and (in the future) color + * capaiblities. + */ + InitTerminal(); /* Sets MaxNumberColumns, MaxNumberLines */ + if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) { + Sent3270TerminalType = 1; + if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) { + MaxNumberLines = 27; + MaxNumberColumns = 132; + sb_terminal[SBTERMMODEL] = '5'; + } else if (MaxNumberLines >= 43) { + MaxNumberLines = 43; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '4'; + } else if (MaxNumberLines >= 32) { + MaxNumberLines = 32; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '3'; + } else { + MaxNumberLines = 24; + MaxNumberColumns = 80; + sb_terminal[SBTERMMODEL] = '2'; + } + NumberLines = 24; /* before we start out... */ + NumberColumns = 80; + ScreenSize = NumberLines*NumberColumns; + if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) { + ExitString("Programming error: MAXSCREENSIZE too small.\n", + 1); + /*NOTREACHED*/ + } + printsub('>', sb_terminal+2, sizeof sb_terminal-2); + ring_supply_data(&netoring, sb_terminal, sizeof sb_terminal); + return 1; + } else { + return 0; + } +} + +int +settranscom(int argc, char *argv[]) +{ + int i; + + if (argc == 1 && transcom) { + transcom = 0; + } + if (argc == 1) { + return 1; + } + transcom = tline; + (void) strlcpy(tline, argv[1], sizeof(tline)); + for (i = 2; i < argc; ++i) { + (void) strlcat(tline, " ", sizeof(tline)); + (void) strlcat(tline, argv[i], sizeof(tline)); + } + return 1; +} + +#endif /* defined(TN3270) */ diff --git a/usr.bin/telnet/types.h b/usr.bin/telnet/types.h new file mode 100644 index 000000000..d85ae2ecc --- /dev/null +++ b/usr.bin/telnet/types.h @@ -0,0 +1,50 @@ +/* $NetBSD: types.h,v 1.6 2003/08/07 11:16:12 agc Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)types.h 8.1 (Berkeley) 6/6/93 + */ + +typedef struct { + char *modedescriptions; + char modetype; +} Modelist; + +extern Modelist modelist[]; + +typedef struct { + int + system, /* what the current time is */ + echotoggle, /* last time user entered echo character */ + modenegotiated, /* last time operating mode negotiated */ + didnetreceive, /* last time we read data from network */ + gotDM; /* when did we last see a data mark */ +} Clocks; + +extern Clocks clocks; diff --git a/usr.bin/telnet/utilities.c b/usr.bin/telnet/utilities.c new file mode 100644 index 000000000..2bab18b68 --- /dev/null +++ b/usr.bin/telnet/utilities.c @@ -0,0 +1,925 @@ +/* $NetBSD: utilities.c,v 1.23 2012/01/09 16:08:55 christos Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utilities.c 8.3 (Berkeley) 5/30/95"; +#else +__RCSID("$NetBSD: utilities.c,v 1.23 2012/01/09 16:08:55 christos Exp $"); +#endif +#endif /* not lint */ + +#define TELOPTS +#define TELCMDS +#define SLC_NAMES +#include +#include +#include +#include +#include +#include + +#include + +#include "general.h" +#include "ring.h" +#include "defines.h" +#include "externs.h" + +#ifdef TN3270 +#include "../sys_curses/telextrn.h" +#endif + +#ifdef AUTHENTICATION +#include +#endif +#ifdef ENCRYPTION +#include +#endif + +FILE *NetTrace = 0; /* Not in bss, since needs to stay */ +int prettydump; + +/* + * upcase() + * + * Upcase (in place) the argument. + */ + +void +upcase(char *argument) +{ + int c; + + while ((c = *argument) != 0) { + if (islower(c)) { + *argument = toupper(c); + } + argument++; + } +} + +/* + * SetSockOpt() + * + * Compensate for differences in 4.2 and 4.3 systems. + */ + +int +SetSockOpt(int fd, int level, int option, int yesno) +{ + return setsockopt(fd, level, option, (char *)&yesno, sizeof yesno); +} + +/* + * The following are routines used to print out debugging information. + */ + +char NetTraceFile[256] = "(standard output)"; + +void +SetNetTrace(char *file) +{ + if (NetTrace && NetTrace != stdout) + fclose(NetTrace); + if (file && (strcmp(file, "-") != 0)) { + NetTrace = fopen(file, "w"); + if (NetTrace) { + strlcpy(NetTraceFile, file, sizeof(NetTraceFile)); + return; + } + fprintf(stderr, "Cannot open %s.\n", file); + } + NetTrace = stdout; + strlcpy(NetTraceFile, "(standard output)", sizeof(NetTraceFile)); +} + +void +Dump(int direction, unsigned char *buffer, int length) +{ +# define BYTES_PER_LINE 32 +# define min(x,y) ((x' */ + unsigned char *pointer, /* where suboption data sits */ + int length) /* length of suboption data */ +{ + int i; +#ifdef ENCRYPTION + char buf[512]; +#endif /* ENCRYPTION */ + extern int want_status_response; + + if (showoptions || direction == 0 || + (want_status_response && (pointer[0] == TELOPT_STATUS))) { + if (direction) { + fprintf(NetTrace, "%s IAC SB ", + (direction == '<')? "RCVD":"SENT"); + if (length >= 3) { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) { + fprintf(NetTrace, "(terminated by "); + if (TELOPT_OK(i)) + fprintf(NetTrace, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + fprintf(NetTrace, "%s ", TELCMD(i)); + else + fprintf(NetTrace, "%d ", i); + if (TELOPT_OK(j)) + fprintf(NetTrace, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + fprintf(NetTrace, "%s", TELCMD(j)); + else + fprintf(NetTrace, "%d", j); + fprintf(NetTrace, ", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) { + fprintf(NetTrace, "(Empty suboption??\?)"); + if (NetTrace == stdout) + fflush(NetTrace); + return; + } + switch (pointer[0]) { + case TELOPT_TTYPE: + fprintf(NetTrace, "TERMINAL-TYPE "); + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND"); + break; + default: + fprintf(NetTrace, + "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + case TELOPT_TSPEED: + fprintf(NetTrace, "TERMINAL-SPEED"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, " IS "); + fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2); + break; + default: + if (pointer[1] == 1) + fprintf(NetTrace, " SEND"); + else + fprintf(NetTrace, " %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + } + break; + + case TELOPT_LFLOW: + fprintf(NetTrace, "TOGGLE-FLOW-CONTROL"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case LFLOW_OFF: + fprintf(NetTrace, " OFF"); break; + case LFLOW_ON: + fprintf(NetTrace, " ON"); break; + case LFLOW_RESTART_ANY: + fprintf(NetTrace, " RESTART-ANY"); break; + case LFLOW_RESTART_XON: + fprintf(NetTrace, " RESTART-XON"); break; + default: + fprintf(NetTrace, " %d (unknown)", pointer[1]); + } + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + + case TELOPT_NAWS: + fprintf(NetTrace, "NAWS"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + if (length == 2) { + fprintf(NetTrace, " ?%d?", pointer[1]); + break; + } + fprintf(NetTrace, " %d %d (%d)", + pointer[1], pointer[2], + (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); + if (length == 4) { + fprintf(NetTrace, " ?%d?", pointer[3]); + break; + } + fprintf(NetTrace, " %d %d (%d)", + pointer[3], pointer[4], + (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); + for (i = 5; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + fprintf(NetTrace, "AUTHENTICATION"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case TELQUAL_REPLY: + case TELQUAL_IS: + fprintf(NetTrace, " %s ", (pointer[1] == TELQUAL_IS) ? + "IS" : "REPLY"); + if (AUTHTYPE_NAME_OK(pointer[2])) + fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[2])); + else + fprintf(NetTrace, "%d ", pointer[2]); + if (length < 3) { + fprintf(NetTrace, "(partial suboption??\?)"); + break; + } + fprintf(NetTrace, "%s|%s", + ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + + auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); + fprintf(NetTrace, "%s", buf); + break; + + case TELQUAL_SEND: + i = 2; + fprintf(NetTrace, " SEND "); + while (i < length) { + if (AUTHTYPE_NAME_OK(pointer[i])) + fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[i])); + else + fprintf(NetTrace, "%d ", pointer[i]); + if (++i >= length) { + fprintf(NetTrace, "(partial suboption??\?)"); + break; + } + fprintf(NetTrace, "%s|%s ", + ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + ++i; + } + break; + + case TELQUAL_NAME: + i = 2; + fprintf(NetTrace, " NAME \""); + while (i < length) + putc(pointer[i++], NetTrace); + putc('"', NetTrace); + break; + + default: + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + } + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + fprintf(NetTrace, "ENCRYPT"); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case ENCRYPT_START: + fprintf(NetTrace, " START"); + break; + + case ENCRYPT_END: + fprintf(NetTrace, " END"); + break; + + case ENCRYPT_REQSTART: + fprintf(NetTrace, " REQUEST-START"); + break; + + case ENCRYPT_REQEND: + fprintf(NetTrace, " REQUEST-END"); + break; + + case ENCRYPT_IS: + case ENCRYPT_REPLY: + fprintf(NetTrace, " %s ", (pointer[1] == ENCRYPT_IS) ? + "IS" : "REPLY"); + if (length < 3) { + fprintf(NetTrace, " (partial suboption??\?)"); + break; + } + if (ENCTYPE_NAME_OK(pointer[2])) + fprintf(NetTrace, "%s ", + ENCTYPE_NAME(pointer[2])); + else + fprintf(NetTrace, " %d (unknown)", pointer[2]); + + encrypt_printsub(&pointer[1], length - 1, buf, + sizeof(buf)); + fprintf(NetTrace, "%s", buf); + break; + + case ENCRYPT_SUPPORT: + i = 2; + fprintf(NetTrace, " SUPPORT "); + while (i < length) { + if (ENCTYPE_NAME_OK(pointer[i])) + fprintf(NetTrace, "%s ", + ENCTYPE_NAME(pointer[i])); + else + fprintf(NetTrace, "%d ", pointer[i]); + i++; + } + break; + + case ENCRYPT_ENC_KEYID: + fprintf(NetTrace, " ENC_KEYID "); + goto encommon; + + case ENCRYPT_DEC_KEYID: + fprintf(NetTrace, " DEC_KEYID "); + goto encommon; + + default: + fprintf(NetTrace, " %d (unknown)", pointer[1]); + encommon: + for (i = 2; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + break; +#endif /* ENCRYPTION */ + + case TELOPT_LINEMODE: + fprintf(NetTrace, "LINEMODE "); + if (length < 2) { + fprintf(NetTrace, " (empty suboption??\?)"); + break; + } + switch (pointer[1]) { + case WILL: + fprintf(NetTrace, "WILL "); + goto common; + case WONT: + fprintf(NetTrace, "WONT "); + goto common; + case DO: + fprintf(NetTrace, "DO "); + goto common; + case DONT: + fprintf(NetTrace, "DONT "); + common: + if (length < 3) { + fprintf(NetTrace, "(no option??\?)"); + break; + } + switch (pointer[2]) { + case LM_FORWARDMASK: + fprintf(NetTrace, "Forward Mask"); + for (i = 3; i < length; i++) + fprintf(NetTrace, " %x", pointer[i]); + break; + default: + fprintf(NetTrace, "%d (unknown)", pointer[2]); + for (i = 3; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + break; + + case LM_SLC: + fprintf(NetTrace, "SLC"); + for (i = 2; i < length - 2; i += 3) { + if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC])); + else + fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]); + switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { + case SLC_NOSUPPORT: + fprintf(NetTrace, " NOSUPPORT"); break; + case SLC_CANTCHANGE: + fprintf(NetTrace, " CANTCHANGE"); break; + case SLC_VARIABLE: + fprintf(NetTrace, " VARIABLE"); break; + case SLC_DEFAULT: + fprintf(NetTrace, " DEFAULT"); break; + } + fprintf(NetTrace, "%s%s%s", + pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); + if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| + SLC_FLUSHOUT| SLC_LEVELBITS)) + fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]); + fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]); + if ((pointer[i+SLC_VALUE] == IAC) && + (pointer[i+SLC_VALUE+1] == IAC)) + i++; + } + for (; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + + case LM_MODE: + fprintf(NetTrace, "MODE "); + if (length < 3) { + fprintf(NetTrace, "(no mode??\?)"); + break; + } + { + char tbuf[64]; + sprintf(tbuf, "%s%s%s%s%s", + pointer[2]&MODE_EDIT ? "|EDIT" : "", + pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", + pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", + pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", + pointer[2]&MODE_ACK ? "|ACK" : ""); + fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0"); + } + if (pointer[2]&~(MODE_MASK)) + fprintf(NetTrace, " (0x%x)", pointer[2]); + for (i = 3; i < length; i++) + fprintf(NetTrace, " ?0x%x?", pointer[i]); + break; + default: + fprintf(NetTrace, "%d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + } + break; + + case TELOPT_STATUS: { + const char *cp; + int j, k; + + fprintf(NetTrace, "STATUS"); + + switch (pointer[1]) { + default: + if (pointer[1] == TELQUAL_SEND) + fprintf(NetTrace, " SEND"); + else + fprintf(NetTrace, " %d (unknown)", pointer[1]); + for (i = 2; i < length; i++) + fprintf(NetTrace, " ?%d?", pointer[i]); + break; + case TELQUAL_IS: + if (--want_status_response < 0) + want_status_response = 0; + if (NetTrace == stdout) + fprintf(NetTrace, " IS\r\n"); + else + fprintf(NetTrace, " IS\n"); + + for (i = 2; i < length; i++) { + switch(pointer[i]) { + case DO: cp = "DO"; goto common2; + case DONT: cp = "DONT"; goto common2; + case WILL: cp = "WILL"; goto common2; + case WONT: cp = "WONT"; goto common2; + common2: + i++; + if (TELOPT_OK((int)pointer[i])) + fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i])); + else + fprintf(NetTrace, " %s %d", cp, pointer[i]); + + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + break; + + case SB: + fprintf(NetTrace, " SB "); + i++; + j = k = i; + while (j < length) { + if (pointer[j] == SE) { + if (j+1 == length) + break; + if (pointer[j+1] == SE) + j++; + else + break; + } + pointer[k++] = pointer[j++]; + } + printsub(0, &pointer[i], k - i); + if (i < length) { + fprintf(NetTrace, " SE"); + i = j; + } else + i = j - 1; + + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + + break; + + default: + fprintf(NetTrace, " %d", pointer[i]); + break; + } + } + break; + } + break; + } + + case TELOPT_XDISPLOC: + fprintf(NetTrace, "X-DISPLAY-LOCATION "); + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND"); + break; + default: + fprintf(NetTrace, "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + break; + + case TELOPT_NEW_ENVIRON: + fprintf(NetTrace, "NEW-ENVIRON "); +#ifdef OLD_ENVIRON + goto env_common1; + case TELOPT_OLD_ENVIRON: + fprintf(NetTrace, "OLD-ENVIRON"); + env_common1: +#endif + switch (pointer[1]) { + case TELQUAL_IS: + fprintf(NetTrace, "IS "); + goto env_common; + case TELQUAL_SEND: + fprintf(NetTrace, "SEND "); + goto env_common; + case TELQUAL_INFO: + fprintf(NetTrace, "INFO "); + env_common: + { + static const char NQ[] = "\" "; + const char *noquote = NQ; +#if defined(ENV_HACK) && defined(OLD_ENVIRON) + extern int old_env_var, old_env_value; +#endif + for (i = 2; i < length; i++ ) { + switch (pointer[i]) { + case NEW_ENV_VALUE: +#ifdef OLD_ENVIRON + /* case NEW_ENV_OVAR: */ + if (pointer[0] == TELOPT_OLD_ENVIRON) { +# ifdef ENV_HACK + if (old_env_var == OLD_ENV_VALUE) + fprintf(NetTrace, "%s(VALUE) ", noquote); + else +# endif + fprintf(NetTrace, "%sVAR ", noquote); + } else +#endif /* OLD_ENVIRON */ + fprintf(NetTrace, "%sVALUE ", noquote); + noquote = NQ; + break; + + case NEW_ENV_VAR: +#ifdef OLD_ENVIRON + /* case OLD_ENV_VALUE: */ + if (pointer[0] == TELOPT_OLD_ENVIRON) { +# ifdef ENV_HACK + if (old_env_value == OLD_ENV_VAR) + fprintf(NetTrace, "%s(VAR) ", noquote); + else +# endif + fprintf(NetTrace, "%sVALUE ", noquote); + } else +#endif /* OLD_ENVIRON */ + fprintf(NetTrace, "%sVAR ", noquote); + noquote = NQ; + break; + + case ENV_ESC: + fprintf(NetTrace, "%sESC ", noquote); + noquote = NQ; + break; + + case ENV_USERVAR: + fprintf(NetTrace, "%sUSERVAR ", noquote); + noquote = NQ; + break; + + default: + if (isprint(pointer[i]) && pointer[i] != '"') { + if (*noquote) { + putc('"', NetTrace); + noquote = ""; + } + putc(pointer[i], NetTrace); + } else { + fprintf(NetTrace, "%s%03o ", noquote, + pointer[i]); + noquote = NQ; + } + break; + } + } + if (!noquote) + putc('"', NetTrace); + break; + } + } + break; + + default: + if (TELOPT_OK(pointer[0])) + fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0])); + else + fprintf(NetTrace, "%d (unknown)", pointer[0]); + for (i = 1; i < length; i++) + fprintf(NetTrace, " %d", pointer[i]); + break; + } + if (direction) { + if (NetTrace == stdout) + fprintf(NetTrace, "\r\n"); + else + fprintf(NetTrace, "\n"); + } + if (NetTrace == stdout) + fflush(NetTrace); + } +} + +/* EmptyTerminal - called to make sure that the terminal buffer is empty. + * Note that we consider the buffer to run all the + * way to the kernel (thus the poll). + */ + +void +EmptyTerminal(void) +{ + struct pollfd set[1]; + + set[0].fd = tout; + set[0].events = POLLOUT; + + if (TTYBYTES() == 0) { + (void) poll(set, 1, INFTIM); + } else { + while (TTYBYTES()) { + (void) ttyflush(0); + (void) poll(set, 1, INFTIM); + } + } +} + +void +SetForExit(void) +{ + setconnmode(0); +#ifdef TN3270 + if (In3270) { + Finish3270(); + } +#else /* defined(TN3270) */ + do { + (void)telrcv(); /* Process any incoming data */ + EmptyTerminal(); + } while (ring_full_count(&netiring)); /* While there is any */ +#endif /* defined(TN3270) */ + setcommandmode(); + fflush(stdout); + fflush(stderr); +#ifdef TN3270 + if (In3270) { + StopScreen(1); + } +#endif /* defined(TN3270) */ + setconnmode(0); + EmptyTerminal(); /* Flush the path to the tty */ + setcommandmode(); +} + +void +Exit(int returnCode) +{ + SetForExit(); + exit(returnCode); +} + +void +ExitString(const char *string, int returnCode) +{ + SetForExit(); + fwrite(string, 1, strlen(string), stderr); + exit(returnCode); +}