17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
506e1a714Sraf * Common Development and Distribution License (the "License").
606e1a714Sraf * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
2006e1a714Sraf */
2106e1a714Sraf
2206e1a714Sraf /*
23*0b4fd3b1SSurya Prakki * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
297c478bd9Sstevel@tonic-gate * All Rights Reserved.
307c478bd9Sstevel@tonic-gate */
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988
347c478bd9Sstevel@tonic-gate * The Regents of the University of California.
357c478bd9Sstevel@tonic-gate * All Rights Reserved.
367c478bd9Sstevel@tonic-gate *
377c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from
387c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its
397c478bd9Sstevel@tonic-gate * contributors.
407c478bd9Sstevel@tonic-gate */
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate * Telnet server.
447c478bd9Sstevel@tonic-gate */
457c478bd9Sstevel@tonic-gate #include <sys/types.h>
467c478bd9Sstevel@tonic-gate #include <sys/param.h>
477c478bd9Sstevel@tonic-gate #include <sys/socket.h>
487c478bd9Sstevel@tonic-gate #include <sys/wait.h>
497c478bd9Sstevel@tonic-gate #include <sys/file.h>
507c478bd9Sstevel@tonic-gate #include <sys/stat.h>
517c478bd9Sstevel@tonic-gate #include <sys/filio.h>
527c478bd9Sstevel@tonic-gate #include <sys/time.h>
537c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
547c478bd9Sstevel@tonic-gate #include <sys/stream.h>
557c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
567c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
577c478bd9Sstevel@tonic-gate #include <unistd.h>
587c478bd9Sstevel@tonic-gate
597c478bd9Sstevel@tonic-gate #include <netinet/in.h>
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate #define AUTHWHO_STR
627c478bd9Sstevel@tonic-gate #define AUTHTYPE_NAMES
637c478bd9Sstevel@tonic-gate #define AUTHHOW_NAMES
647c478bd9Sstevel@tonic-gate #define AUTHRSP_NAMES
657c478bd9Sstevel@tonic-gate #define ENCRYPT_NAMES
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate #include <arpa/telnet.h>
687c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
697c478bd9Sstevel@tonic-gate #include <stdio.h>
707c478bd9Sstevel@tonic-gate #include <stdarg.h>
717c478bd9Sstevel@tonic-gate #include <signal.h>
727c478bd9Sstevel@tonic-gate #include <errno.h>
737c478bd9Sstevel@tonic-gate #include <netdb.h>
747c478bd9Sstevel@tonic-gate #include <syslog.h>
757c478bd9Sstevel@tonic-gate #include <ctype.h>
767c478bd9Sstevel@tonic-gate #include <fcntl.h>
777c478bd9Sstevel@tonic-gate #include <sac.h> /* for SC_WILDC */
787c478bd9Sstevel@tonic-gate #include <utmpx.h>
797c478bd9Sstevel@tonic-gate #include <sys/ttold.h>
807c478bd9Sstevel@tonic-gate #include <malloc.h>
817c478bd9Sstevel@tonic-gate #include <string.h>
827c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
837c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
847c478bd9Sstevel@tonic-gate #include <sys/logindmux.h>
857c478bd9Sstevel@tonic-gate #include <sys/telioctl.h>
867c478bd9Sstevel@tonic-gate #include <deflt.h>
877c478bd9Sstevel@tonic-gate #include <stdlib.h>
887c478bd9Sstevel@tonic-gate #include <string.h>
897c478bd9Sstevel@tonic-gate #include <stropts.h>
907c478bd9Sstevel@tonic-gate #include <termios.h>
917c478bd9Sstevel@tonic-gate
927c478bd9Sstevel@tonic-gate #include <com_err.h>
937c478bd9Sstevel@tonic-gate #include <krb5.h>
947c478bd9Sstevel@tonic-gate #include <krb5_repository.h>
957c478bd9Sstevel@tonic-gate #include <des/des.h>
967c478bd9Sstevel@tonic-gate #include <rpc/des_crypt.h>
977c478bd9Sstevel@tonic-gate #include <sys/cryptmod.h>
987c478bd9Sstevel@tonic-gate #include <bsm/adt.h>
997c478bd9Sstevel@tonic-gate
1007c478bd9Sstevel@tonic-gate #define TELNETD_OPTS "Ss:a:dEXUhR:M:"
1017c478bd9Sstevel@tonic-gate #ifdef DEBUG
1027c478bd9Sstevel@tonic-gate #define DEBUG_OPTS "p:e"
1037c478bd9Sstevel@tonic-gate #else
1047c478bd9Sstevel@tonic-gate #define DEBUG_OPTS ""
1057c478bd9Sstevel@tonic-gate #endif /* DEBUG */
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate #define OPT_NO 0 /* won't do this option */
1087c478bd9Sstevel@tonic-gate #define OPT_YES 1 /* will do this option */
1097c478bd9Sstevel@tonic-gate #define OPT_YES_BUT_ALWAYS_LOOK 2
1107c478bd9Sstevel@tonic-gate #define OPT_NO_BUT_ALWAYS_LOOK 3
1117c478bd9Sstevel@tonic-gate
1127c478bd9Sstevel@tonic-gate #define MAXOPTLEN 256
1137c478bd9Sstevel@tonic-gate #define MAXUSERNAMELEN 256
1147c478bd9Sstevel@tonic-gate
1157c478bd9Sstevel@tonic-gate static char remopts[MAXOPTLEN];
1167c478bd9Sstevel@tonic-gate static char myopts[MAXOPTLEN];
1177c478bd9Sstevel@tonic-gate static uchar_t doopt[] = { (uchar_t)IAC, (uchar_t)DO, '%', 'c', 0 };
1187c478bd9Sstevel@tonic-gate static uchar_t dont[] = { (uchar_t)IAC, (uchar_t)DONT, '%', 'c', 0 };
1197c478bd9Sstevel@tonic-gate static uchar_t will[] = { (uchar_t)IAC, (uchar_t)WILL, '%', 'c', 0 };
1207c478bd9Sstevel@tonic-gate static uchar_t wont[] = { (uchar_t)IAC, (uchar_t)WONT, '%', 'c', 0 };
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate * I/O data buffers, pointers, and counters.
1237c478bd9Sstevel@tonic-gate */
1247c478bd9Sstevel@tonic-gate static char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
1257c478bd9Sstevel@tonic-gate
1267c478bd9Sstevel@tonic-gate static char *netibuf, *netip;
1277c478bd9Sstevel@tonic-gate static int netibufsize;
1287c478bd9Sstevel@tonic-gate
1297c478bd9Sstevel@tonic-gate #define NIACCUM(c) { *netip++ = c; \
1307c478bd9Sstevel@tonic-gate ncc++; \
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate static char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
1347c478bd9Sstevel@tonic-gate static char *neturg = 0; /* one past last bye of urgent data */
1357c478bd9Sstevel@tonic-gate /* the remote system seems to NOT be an old 4.2 */
1367c478bd9Sstevel@tonic-gate static int not42 = 1;
1377c478bd9Sstevel@tonic-gate static char defaultfile[] = "/etc/default/telnetd";
1387c478bd9Sstevel@tonic-gate static char bannervar[] = "BANNER=";
1397c478bd9Sstevel@tonic-gate
1407c478bd9Sstevel@tonic-gate static char BANNER1[] = "\r\n\r\n";
1417c478bd9Sstevel@tonic-gate static char BANNER2[] = "\r\n\r\0\r\n\r\0";
1427c478bd9Sstevel@tonic-gate
1437c478bd9Sstevel@tonic-gate /*
1447c478bd9Sstevel@tonic-gate * buffer for sub-options - enlarged to 4096 to handle credentials
1457c478bd9Sstevel@tonic-gate * from AUTH options
1467c478bd9Sstevel@tonic-gate */
1477c478bd9Sstevel@tonic-gate static char subbuffer[4096], *subpointer = subbuffer, *subend = subbuffer;
1487c478bd9Sstevel@tonic-gate #define SB_CLEAR() subpointer = subbuffer;
1497c478bd9Sstevel@tonic-gate #define SB_TERM() { subend = subpointer; SB_CLEAR(); }
1507c478bd9Sstevel@tonic-gate #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof (subbuffer))) { \
1517c478bd9Sstevel@tonic-gate *subpointer++ = (c); \
1527c478bd9Sstevel@tonic-gate }
1537c478bd9Sstevel@tonic-gate #define SB_GET() ((*subpointer++)&0xff)
1547c478bd9Sstevel@tonic-gate #define SB_EOF() (subpointer >= subend)
1557c478bd9Sstevel@tonic-gate #define SB_LEN() (subend - subpointer)
1567c478bd9Sstevel@tonic-gate
1577c478bd9Sstevel@tonic-gate #define MAXERRSTRLEN 1024
1587c478bd9Sstevel@tonic-gate #define MAXPRINCLEN 256
1597c478bd9Sstevel@tonic-gate
16032885d59Sgtb extern uint_t kwarn_add_warning(char *, int);
16132885d59Sgtb extern uint_t kwarn_del_warning(char *);
16232885d59Sgtb
1637c478bd9Sstevel@tonic-gate static boolean_t auth_debug = 0;
1647c478bd9Sstevel@tonic-gate static boolean_t negotiate_auth_krb5 = 1;
1657c478bd9Sstevel@tonic-gate static boolean_t auth_negotiated = 0;
1667c478bd9Sstevel@tonic-gate static int auth_status = 0;
1677c478bd9Sstevel@tonic-gate static int auth_level = 0;
1687c478bd9Sstevel@tonic-gate static char *AuthenticatingUser = NULL;
1697c478bd9Sstevel@tonic-gate static char *krb5_name = NULL;
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate static krb5_address rsaddr = { 0, 0, 0, NULL };
1727c478bd9Sstevel@tonic-gate static krb5_address rsport = { 0, 0, 0, NULL };
1737c478bd9Sstevel@tonic-gate
1747c478bd9Sstevel@tonic-gate static krb5_context telnet_context = 0;
1757c478bd9Sstevel@tonic-gate static krb5_auth_context auth_context = 0;
1767c478bd9Sstevel@tonic-gate
1777c478bd9Sstevel@tonic-gate /* telnetd gets session key from here */
1787c478bd9Sstevel@tonic-gate static krb5_ticket *ticket = NULL;
1797c478bd9Sstevel@tonic-gate static krb5_keyblock *session_key = NULL;
1807c478bd9Sstevel@tonic-gate static char *telnet_srvtab = NULL;
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate typedef struct {
1837c478bd9Sstevel@tonic-gate uchar_t AuthName;
1847c478bd9Sstevel@tonic-gate uchar_t AuthHow;
1857c478bd9Sstevel@tonic-gate char *AuthString;
1867c478bd9Sstevel@tonic-gate } AuthInfo;
1877c478bd9Sstevel@tonic-gate
1887c478bd9Sstevel@tonic-gate static AuthInfo auth_list[] = {
1897c478bd9Sstevel@tonic-gate {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL |
1907c478bd9Sstevel@tonic-gate AUTH_ENCRYPT_ON, "KRB5 MUTUAL CRYPTO"},
1917c478bd9Sstevel@tonic-gate {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_MUTUAL,
1927c478bd9Sstevel@tonic-gate "KRB5 MUTUAL" },
1937c478bd9Sstevel@tonic-gate {AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT | AUTH_HOW_ONE_WAY,
1947c478bd9Sstevel@tonic-gate "KRB5 1-WAY" },
1957c478bd9Sstevel@tonic-gate {0, 0, "NONE"}
1967c478bd9Sstevel@tonic-gate };
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate static AuthInfo NoAuth = {0, 0, NULL};
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate static AuthInfo *authenticated = NULL;
2017c478bd9Sstevel@tonic-gate
2027c478bd9Sstevel@tonic-gate #define PREAMBLE_SIZE 5 /* for auth_reply_str allocation */
2037c478bd9Sstevel@tonic-gate #define POSTAMBLE_SIZE 5
2047c478bd9Sstevel@tonic-gate #define STR_DATA_LEN(len) ((len) * 2 + PREAMBLE_SIZE + POSTAMBLE_SIZE)
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate static void auth_name(uchar_t *, int);
2077c478bd9Sstevel@tonic-gate static void auth_is(uchar_t *, int);
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate #define NO_ENCRYPTION 0x00
2107c478bd9Sstevel@tonic-gate #define SEND_ENCRYPTED 0x01
2117c478bd9Sstevel@tonic-gate #define RECV_ENCRYPTED 0x02
2127c478bd9Sstevel@tonic-gate #define ENCRYPT_BOTH_WAYS (SEND_ENCRYPTED | RECV_ENCRYPTED)
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate static telnet_enc_data_t encr_data;
2157c478bd9Sstevel@tonic-gate static boolean_t negotiate_encrypt = B_TRUE;
2167c478bd9Sstevel@tonic-gate static boolean_t sent_encrypt_support = B_FALSE;
2177c478bd9Sstevel@tonic-gate static boolean_t sent_will_encrypt = B_FALSE;
2187c478bd9Sstevel@tonic-gate static boolean_t sent_do_encrypt = B_FALSE;
2197c478bd9Sstevel@tonic-gate static boolean_t enc_debug = 0;
2207c478bd9Sstevel@tonic-gate
2217c478bd9Sstevel@tonic-gate static void encrypt_session_key(Session_Key *key, cipher_info_t *cinfo);
2227c478bd9Sstevel@tonic-gate static int encrypt_send_encrypt_is();
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gate extern void mit_des_fixup_key_parity(Block);
2257c478bd9Sstevel@tonic-gate extern int krb5_setenv(const char *, const char *, int);
2267c478bd9Sstevel@tonic-gate /* need to know what FD to use to talk to the crypto module */
2277c478bd9Sstevel@tonic-gate static int cryptmod_fd = -1;
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate #define LOGIN_PROGRAM "/bin/login"
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate /*
2327c478bd9Sstevel@tonic-gate * State for recv fsm
2337c478bd9Sstevel@tonic-gate */
2347c478bd9Sstevel@tonic-gate #define TS_DATA 0 /* base state */
2357c478bd9Sstevel@tonic-gate #define TS_IAC 1 /* look for double IAC's */
2367c478bd9Sstevel@tonic-gate #define TS_CR 2 /* CR-LF ->'s CR */
2377c478bd9Sstevel@tonic-gate #define TS_SB 3 /* throw away begin's... */
2387c478bd9Sstevel@tonic-gate #define TS_SE 4 /* ...end's (suboption negotiation) */
2397c478bd9Sstevel@tonic-gate #define TS_WILL 5 /* will option negotiation */
2407c478bd9Sstevel@tonic-gate #define TS_WONT 6 /* wont " */
2417c478bd9Sstevel@tonic-gate #define TS_DO 7 /* do " */
2427c478bd9Sstevel@tonic-gate #define TS_DONT 8 /* dont " */
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate static int ncc;
2457c478bd9Sstevel@tonic-gate static int master; /* master side of pty */
2467c478bd9Sstevel@tonic-gate static int pty; /* side of pty that gets ioctls */
2477c478bd9Sstevel@tonic-gate static int net;
2487c478bd9Sstevel@tonic-gate static int inter;
2497c478bd9Sstevel@tonic-gate extern char **environ;
2507c478bd9Sstevel@tonic-gate static char *line;
2517c478bd9Sstevel@tonic-gate static int SYNCHing = 0; /* we are in TELNET SYNCH mode */
2527c478bd9Sstevel@tonic-gate static int state = TS_DATA;
2537c478bd9Sstevel@tonic-gate
2547c478bd9Sstevel@tonic-gate static int env_ovar = -1; /* XXX.sparker */
2557c478bd9Sstevel@tonic-gate static int env_ovalue = -1; /* XXX.sparker */
2567c478bd9Sstevel@tonic-gate static char pam_svc_name[64];
2577c478bd9Sstevel@tonic-gate static boolean_t telmod_init_done = B_FALSE;
2587c478bd9Sstevel@tonic-gate
2597c478bd9Sstevel@tonic-gate static void doit(int, struct sockaddr_storage *);
2607c478bd9Sstevel@tonic-gate static void willoption(int);
2617c478bd9Sstevel@tonic-gate static void wontoption(int);
2627c478bd9Sstevel@tonic-gate static void dooption(int);
2637c478bd9Sstevel@tonic-gate static void dontoption(int);
2647c478bd9Sstevel@tonic-gate static void fatal(int, char *);
2657c478bd9Sstevel@tonic-gate static void fatalperror(int, char *, int);
2667c478bd9Sstevel@tonic-gate static void mode(int, int);
2677c478bd9Sstevel@tonic-gate static void interrupt(void);
2687c478bd9Sstevel@tonic-gate static void drainstream(int);
2697c478bd9Sstevel@tonic-gate static int readstream(int, char *, int);
2707c478bd9Sstevel@tonic-gate static int send_oob(int fd, char *ptr, int count);
2717c478bd9Sstevel@tonic-gate static int local_setenv(const char *name, const char *value, int rewrite);
2727c478bd9Sstevel@tonic-gate static void local_unsetenv(const char *name);
2737c478bd9Sstevel@tonic-gate static void suboption(void);
2747c478bd9Sstevel@tonic-gate static int removemod(int f, char *modname);
2757c478bd9Sstevel@tonic-gate static void willoption(int option);
2767c478bd9Sstevel@tonic-gate static void wontoption(int option);
2777c478bd9Sstevel@tonic-gate static void dooption(int option);
2787c478bd9Sstevel@tonic-gate static void dontoption(int option);
2797c478bd9Sstevel@tonic-gate static void write_data(const char *, ...);
2807c478bd9Sstevel@tonic-gate static void write_data_len(const char *, int);
2817c478bd9Sstevel@tonic-gate static void rmut(void);
2827c478bd9Sstevel@tonic-gate static void cleanup(int);
2837c478bd9Sstevel@tonic-gate static void telnet(int, int);
2847c478bd9Sstevel@tonic-gate static void telrcv(void);
2857c478bd9Sstevel@tonic-gate static void sendbrk(void);
2867c478bd9Sstevel@tonic-gate static void ptyflush(void);
2877c478bd9Sstevel@tonic-gate static void netclear(void);
2887c478bd9Sstevel@tonic-gate static void netflush(void);
2897c478bd9Sstevel@tonic-gate static void showbanner(void);
2907c478bd9Sstevel@tonic-gate static void map_banner(char *);
2917c478bd9Sstevel@tonic-gate static void defbanner(void);
2927c478bd9Sstevel@tonic-gate static void ttloop(void);
2937c478bd9Sstevel@tonic-gate
2947c478bd9Sstevel@tonic-gate /*
2957c478bd9Sstevel@tonic-gate * The env_list linked list is used to store the environment variables
2967c478bd9Sstevel@tonic-gate * until the final exec of login. A malevolent client might try to
2977c478bd9Sstevel@tonic-gate * send an environment variable intended to affect the telnet daemon's
2987c478bd9Sstevel@tonic-gate * execution. Right now the BANNER expansion is the only instance.
2997c478bd9Sstevel@tonic-gate * Note that it is okay to pass the environment variables to login
3007c478bd9Sstevel@tonic-gate * because login protects itself against environment variables mischief.
3017c478bd9Sstevel@tonic-gate */
3027c478bd9Sstevel@tonic-gate
3037c478bd9Sstevel@tonic-gate struct envlist {
3047c478bd9Sstevel@tonic-gate struct envlist *next;
3057c478bd9Sstevel@tonic-gate char *name;
3067c478bd9Sstevel@tonic-gate char *value;
3077c478bd9Sstevel@tonic-gate int delete;
3087c478bd9Sstevel@tonic-gate };
3097c478bd9Sstevel@tonic-gate
3107c478bd9Sstevel@tonic-gate static struct envlist *envlist_head = NULL;
3117c478bd9Sstevel@tonic-gate
3127c478bd9Sstevel@tonic-gate /*
3137c478bd9Sstevel@tonic-gate * The following are some clocks used to decide how to interpret
3147c478bd9Sstevel@tonic-gate * the relationship between various variables.
3157c478bd9Sstevel@tonic-gate */
3167c478bd9Sstevel@tonic-gate
3177c478bd9Sstevel@tonic-gate static struct {
3187c478bd9Sstevel@tonic-gate int
3197c478bd9Sstevel@tonic-gate system, /* what the current time is */
3207c478bd9Sstevel@tonic-gate echotoggle, /* last time user entered echo character */
3217c478bd9Sstevel@tonic-gate modenegotiated, /* last time operating mode negotiated */
3227c478bd9Sstevel@tonic-gate didnetreceive, /* last time we read data from network */
3237c478bd9Sstevel@tonic-gate ttypeopt, /* ttype will/won't received */
3247c478bd9Sstevel@tonic-gate ttypesubopt, /* ttype subopt is received */
3257c478bd9Sstevel@tonic-gate getterminal, /* time started to get terminal information */
3267c478bd9Sstevel@tonic-gate xdisplocopt, /* xdisploc will/wont received */
3277c478bd9Sstevel@tonic-gate xdisplocsubopt, /* xdisploc suboption received */
3287c478bd9Sstevel@tonic-gate nawsopt, /* window size will/wont received */
3297c478bd9Sstevel@tonic-gate nawssubopt, /* window size received */
3307c478bd9Sstevel@tonic-gate environopt, /* environment option will/wont received */
3317c478bd9Sstevel@tonic-gate oenvironopt, /* "old" environ option will/wont received */
3327c478bd9Sstevel@tonic-gate environsubopt, /* environment option suboption received */
3337c478bd9Sstevel@tonic-gate oenvironsubopt, /* "old environ option suboption received */
3347c478bd9Sstevel@tonic-gate gotDM; /* when did we last see a data mark */
3357c478bd9Sstevel@tonic-gate
3367c478bd9Sstevel@tonic-gate int getauth;
3377c478bd9Sstevel@tonic-gate int authopt; /* Authentication option negotiated */
3387c478bd9Sstevel@tonic-gate int authdone;
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate int getencr;
3417c478bd9Sstevel@tonic-gate int encropt;
3427c478bd9Sstevel@tonic-gate int encr_support;
3437c478bd9Sstevel@tonic-gate } clocks;
3447c478bd9Sstevel@tonic-gate
3457c478bd9Sstevel@tonic-gate static int init_neg_done = 0;
3467c478bd9Sstevel@tonic-gate static boolean_t resolve_hostname = 0;
3477c478bd9Sstevel@tonic-gate static boolean_t show_hostinfo = 1;
3487c478bd9Sstevel@tonic-gate
3497c478bd9Sstevel@tonic-gate #define settimer(x) (clocks.x = ++clocks.system)
3507c478bd9Sstevel@tonic-gate #define sequenceIs(x, y) (clocks.x < clocks.y)
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate static void send_will(int);
3537c478bd9Sstevel@tonic-gate static void send_wont(int);
3547c478bd9Sstevel@tonic-gate static void send_do(int);
3557c478bd9Sstevel@tonic-gate static char *__findenv(const char *name, int *offset);
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate /* ARGSUSED */
3587c478bd9Sstevel@tonic-gate static void
auth_finished(AuthInfo * ap,int result)3597c478bd9Sstevel@tonic-gate auth_finished(AuthInfo *ap, int result)
3607c478bd9Sstevel@tonic-gate {
3617c478bd9Sstevel@tonic-gate if ((authenticated = ap) == NULL) {
3627c478bd9Sstevel@tonic-gate authenticated = &NoAuth;
3637c478bd9Sstevel@tonic-gate if (myopts[TELOPT_ENCRYPT] == OPT_YES)
3647c478bd9Sstevel@tonic-gate send_wont(TELOPT_ENCRYPT);
3657c478bd9Sstevel@tonic-gate myopts[TELOPT_ENCRYPT] = remopts[TELOPT_ENCRYPT] = OPT_NO;
3667c478bd9Sstevel@tonic-gate encr_data.encrypt.autoflag = 0;
3677c478bd9Sstevel@tonic-gate } else if (result != AUTH_REJECT &&
3687c478bd9Sstevel@tonic-gate myopts[TELOPT_ENCRYPT] == OPT_YES &&
3697c478bd9Sstevel@tonic-gate remopts[TELOPT_ENCRYPT] == OPT_YES) {
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate /*
3727c478bd9Sstevel@tonic-gate * Authentication successful, so we have a session key, and
3737c478bd9Sstevel@tonic-gate * we're willing to do ENCRYPT, so send our ENCRYPT SUPPORT.
3747c478bd9Sstevel@tonic-gate *
3757c478bd9Sstevel@tonic-gate * Can't have sent ENCRYPT SUPPORT yet! And if we're sending it
3767c478bd9Sstevel@tonic-gate * now it's really only because we did the DO ENCRYPT/WILL
3777c478bd9Sstevel@tonic-gate * ENCRYPT dance before authentication, which is ok, but not too
3787c478bd9Sstevel@tonic-gate * bright since we have to do the DONT ENCRYPT/WONT ENCRYPT
3797c478bd9Sstevel@tonic-gate * dance if authentication fails, though clients typically just
3807c478bd9Sstevel@tonic-gate * don't care.
3817c478bd9Sstevel@tonic-gate */
3827c478bd9Sstevel@tonic-gate write_data("%c%c%c%c%c%c%c",
3837c478bd9Sstevel@tonic-gate (uchar_t)IAC,
3847c478bd9Sstevel@tonic-gate (uchar_t)SB,
3857c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
3867c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_SUPPORT,
3877c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCTYPE_DES_CFB64,
3887c478bd9Sstevel@tonic-gate (uchar_t)IAC,
3897c478bd9Sstevel@tonic-gate (uchar_t)SE);
3907c478bd9Sstevel@tonic-gate
3917c478bd9Sstevel@tonic-gate netflush();
3927c478bd9Sstevel@tonic-gate
3937c478bd9Sstevel@tonic-gate sent_encrypt_support = B_TRUE;
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate if (enc_debug)
3967c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
3977c478bd9Sstevel@tonic-gate "SENT ENCRYPT SUPPORT\n");
3987c478bd9Sstevel@tonic-gate
3997c478bd9Sstevel@tonic-gate (void) encrypt_send_encrypt_is();
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate
4027c478bd9Sstevel@tonic-gate auth_status = result;
4037c478bd9Sstevel@tonic-gate
4047c478bd9Sstevel@tonic-gate settimer(authdone);
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate
4077c478bd9Sstevel@tonic-gate static void
reply_to_client(AuthInfo * ap,int type,void * data,int len)4087c478bd9Sstevel@tonic-gate reply_to_client(AuthInfo *ap, int type, void *data, int len)
4097c478bd9Sstevel@tonic-gate {
4107c478bd9Sstevel@tonic-gate uchar_t reply[BUFSIZ];
4117c478bd9Sstevel@tonic-gate uchar_t *p = reply;
4127c478bd9Sstevel@tonic-gate uchar_t *cd = (uchar_t *)data;
4137c478bd9Sstevel@tonic-gate
4147c478bd9Sstevel@tonic-gate if (len == -1 && data != NULL)
4157c478bd9Sstevel@tonic-gate len = strlen((char *)data);
4167c478bd9Sstevel@tonic-gate else if (len > (sizeof (reply) - 9)) {
4177c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
4187c478bd9Sstevel@tonic-gate "krb5 auth reply length too large (%d)", len);
4197c478bd9Sstevel@tonic-gate if (auth_debug)
4207c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
4217c478bd9Sstevel@tonic-gate "krb5 auth reply length too large (%d)\n",
4227c478bd9Sstevel@tonic-gate len);
4237c478bd9Sstevel@tonic-gate return;
4247c478bd9Sstevel@tonic-gate } else if (data == NULL)
4257c478bd9Sstevel@tonic-gate len = 0;
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate *p++ = IAC;
4287c478bd9Sstevel@tonic-gate *p++ = SB;
4297c478bd9Sstevel@tonic-gate *p++ = TELOPT_AUTHENTICATION;
4307c478bd9Sstevel@tonic-gate *p++ = AUTHTYPE_KERBEROS_V5;
4317c478bd9Sstevel@tonic-gate *p++ = ap->AuthName;
4327c478bd9Sstevel@tonic-gate *p++ = ap->AuthHow; /* MUTUAL, ONE-WAY, etc */
4337c478bd9Sstevel@tonic-gate *p++ = type; /* RESPONSE or ACCEPT */
4347c478bd9Sstevel@tonic-gate while (len-- > 0) {
4357c478bd9Sstevel@tonic-gate if ((*p++ = *cd++) == IAC)
4367c478bd9Sstevel@tonic-gate *p++ = IAC;
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate *p++ = IAC;
4397c478bd9Sstevel@tonic-gate *p++ = SE;
4407c478bd9Sstevel@tonic-gate
4417c478bd9Sstevel@tonic-gate /* queue the data to be sent */
4427c478bd9Sstevel@tonic-gate write_data_len((const char *)reply, p-reply);
4437c478bd9Sstevel@tonic-gate
4447c478bd9Sstevel@tonic-gate #if defined(AUTHTYPE_NAMES) && defined(AUTHWHO_STR) &&\
4457c478bd9Sstevel@tonic-gate defined(AUTHHOW_NAMES) && defined(AUTHRSP_NAMES)
4467c478bd9Sstevel@tonic-gate if (auth_debug) {
4477c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT TELOPT_AUTHENTICATION REPLY "
4487c478bd9Sstevel@tonic-gate "%s %s|%s %s\n",
4497c478bd9Sstevel@tonic-gate AUTHTYPE_NAME(ap->AuthName),
4507c478bd9Sstevel@tonic-gate AUTHWHO_NAME(ap->AuthHow & AUTH_WHO_MASK),
4517c478bd9Sstevel@tonic-gate AUTHHOW_NAME(ap->AuthHow & AUTH_HOW_MASK),
4527c478bd9Sstevel@tonic-gate AUTHRSP_NAME(type));
4537c478bd9Sstevel@tonic-gate }
4547c478bd9Sstevel@tonic-gate #endif /* AUTHTYPE_NAMES && AUTHWHO_NAMES && AUTHHOW_NAMES && AUTHRSP_NAMES */
4557c478bd9Sstevel@tonic-gate
4567c478bd9Sstevel@tonic-gate netflush();
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate
4597c478bd9Sstevel@tonic-gate /* Decode, decrypt and store the forwarded creds in the local ccache. */
4607c478bd9Sstevel@tonic-gate static krb5_error_code
rd_and_store_forwarded_creds(krb5_context context,krb5_auth_context auth_context,krb5_data * inbuf,krb5_ticket * ticket,char * username)4617c478bd9Sstevel@tonic-gate rd_and_store_forwarded_creds(krb5_context context,
4627c478bd9Sstevel@tonic-gate krb5_auth_context auth_context,
4637c478bd9Sstevel@tonic-gate krb5_data *inbuf, krb5_ticket *ticket,
4647c478bd9Sstevel@tonic-gate char *username)
4657c478bd9Sstevel@tonic-gate {
4667c478bd9Sstevel@tonic-gate krb5_creds **creds;
4677c478bd9Sstevel@tonic-gate krb5_error_code retval;
46832885d59Sgtb char ccname[MAXPATHLEN];
4697c478bd9Sstevel@tonic-gate krb5_ccache ccache = NULL;
47032885d59Sgtb char *client_name = NULL;
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate if (retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))
4737c478bd9Sstevel@tonic-gate return (retval);
4747c478bd9Sstevel@tonic-gate
4757c478bd9Sstevel@tonic-gate (void) sprintf(ccname, "FILE:/tmp/krb5cc_p%ld", getpid());
4767c478bd9Sstevel@tonic-gate (void) krb5_setenv("KRB5CCNAME", ccname, 1);
4777c478bd9Sstevel@tonic-gate
4787c478bd9Sstevel@tonic-gate if ((retval = krb5_cc_default(context, &ccache)))
4797c478bd9Sstevel@tonic-gate goto cleanup;
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate if ((retval = krb5_cc_initialize(context, ccache,
4827c478bd9Sstevel@tonic-gate ticket->enc_part2->client)) != 0)
4837c478bd9Sstevel@tonic-gate goto cleanup;
4847c478bd9Sstevel@tonic-gate
4857c478bd9Sstevel@tonic-gate if ((retval = krb5_cc_store_cred(context, ccache, *creds)) != 0)
4867c478bd9Sstevel@tonic-gate goto cleanup;
4877c478bd9Sstevel@tonic-gate
4887c478bd9Sstevel@tonic-gate if ((retval = krb5_cc_close(context, ccache)) != 0)
4897c478bd9Sstevel@tonic-gate goto cleanup;
4907c478bd9Sstevel@tonic-gate
49132885d59Sgtb /* Register with ktkt_warnd(1M) */
49232885d59Sgtb if ((retval = krb5_unparse_name(context, (*creds)->client,
49332885d59Sgtb &client_name)) != 0)
49432885d59Sgtb goto cleanup;
49532885d59Sgtb (void) kwarn_del_warning(client_name);
49632885d59Sgtb if (kwarn_add_warning(client_name, (*creds)->times.endtime) != 0) {
49732885d59Sgtb syslog(LOG_AUTH|LOG_NOTICE,
49832885d59Sgtb "rd_and_store_forwarded_creds: kwarn_add_warning"
49932885d59Sgtb " failed: ktkt_warnd(1M) down? ");
50032885d59Sgtb if (auth_debug)
50132885d59Sgtb (void) fprintf(stderr,
50232885d59Sgtb "kwarn_add_warning failed:"
50332885d59Sgtb " ktkt_warnd(1M) down?\n");
50432885d59Sgtb }
50532885d59Sgtb free(client_name);
50632885d59Sgtb client_name = NULL;
50732885d59Sgtb
5087c478bd9Sstevel@tonic-gate if (username != NULL) {
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate * This verifies that the user is valid on the local system,
5117c478bd9Sstevel@tonic-gate * maps the username from KerberosV5 to unix,
5127c478bd9Sstevel@tonic-gate * and moves the KRB5CCNAME file to the correct place
5137c478bd9Sstevel@tonic-gate * /tmp/krb5cc_[uid] with correct ownership (0600 uid gid).
5147c478bd9Sstevel@tonic-gate *
5157c478bd9Sstevel@tonic-gate * NOTE: the user must be in the gsscred table in order to map
5167c478bd9Sstevel@tonic-gate * from KRB5 to Unix.
5177c478bd9Sstevel@tonic-gate */
5187c478bd9Sstevel@tonic-gate (void) krb5_kuserok(context, ticket->enc_part2->client,
5197c478bd9Sstevel@tonic-gate username);
5207c478bd9Sstevel@tonic-gate }
5217c478bd9Sstevel@tonic-gate if (auth_debug)
5227c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
5237c478bd9Sstevel@tonic-gate "Successfully stored forwarded creds\n");
5247c478bd9Sstevel@tonic-gate
5257c478bd9Sstevel@tonic-gate cleanup:
5267c478bd9Sstevel@tonic-gate krb5_free_creds(context, *creds);
5277c478bd9Sstevel@tonic-gate return (retval);
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate static void
kerberos5_is(AuthInfo * ap,uchar_t * data,int cnt)5317c478bd9Sstevel@tonic-gate kerberos5_is(AuthInfo *ap, uchar_t *data, int cnt)
5327c478bd9Sstevel@tonic-gate {
5337c478bd9Sstevel@tonic-gate krb5_error_code err = 0;
5347c478bd9Sstevel@tonic-gate krb5_principal server;
5357c478bd9Sstevel@tonic-gate krb5_keyblock *newkey = NULL;
5367c478bd9Sstevel@tonic-gate krb5_keytab keytabid = 0;
5377c478bd9Sstevel@tonic-gate krb5_data outbuf;
5387c478bd9Sstevel@tonic-gate krb5_data inbuf;
5397c478bd9Sstevel@tonic-gate krb5_authenticator *authenticator;
5407c478bd9Sstevel@tonic-gate char errbuf[MAXERRSTRLEN];
5417c478bd9Sstevel@tonic-gate char *name;
5427c478bd9Sstevel@tonic-gate krb5_data auth;
5437c478bd9Sstevel@tonic-gate
5447c478bd9Sstevel@tonic-gate Session_Key skey;
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate if (cnt-- < 1)
5477c478bd9Sstevel@tonic-gate return;
5487c478bd9Sstevel@tonic-gate switch (*data++) {
5497c478bd9Sstevel@tonic-gate case KRB_AUTH:
5507c478bd9Sstevel@tonic-gate auth.data = (char *)data;
5517c478bd9Sstevel@tonic-gate auth.length = cnt;
5527c478bd9Sstevel@tonic-gate
5537c478bd9Sstevel@tonic-gate if (auth_context == NULL) {
5547c478bd9Sstevel@tonic-gate err = krb5_auth_con_init(telnet_context, &auth_context);
5557c478bd9Sstevel@tonic-gate if (err)
5567c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
5577c478bd9Sstevel@tonic-gate "Error getting krb5 auth "
5587c478bd9Sstevel@tonic-gate "context: %s", error_message(err));
5597c478bd9Sstevel@tonic-gate }
5607c478bd9Sstevel@tonic-gate if (!err) {
5617c478bd9Sstevel@tonic-gate krb5_rcache rcache;
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate err = krb5_auth_con_getrcache(telnet_context,
5647c478bd9Sstevel@tonic-gate auth_context,
5657c478bd9Sstevel@tonic-gate &rcache);
5667c478bd9Sstevel@tonic-gate if (!err && !rcache) {
5677c478bd9Sstevel@tonic-gate err = krb5_sname_to_principal(telnet_context,
5687c478bd9Sstevel@tonic-gate 0, 0,
5697c478bd9Sstevel@tonic-gate KRB5_NT_SRV_HST,
5707c478bd9Sstevel@tonic-gate &server);
5717c478bd9Sstevel@tonic-gate if (!err) {
5727c478bd9Sstevel@tonic-gate err = krb5_get_server_rcache(
5737c478bd9Sstevel@tonic-gate telnet_context,
5747c478bd9Sstevel@tonic-gate krb5_princ_component(
5757c478bd9Sstevel@tonic-gate telnet_context,
5767c478bd9Sstevel@tonic-gate server, 0),
5777c478bd9Sstevel@tonic-gate &rcache);
5787c478bd9Sstevel@tonic-gate
5797c478bd9Sstevel@tonic-gate krb5_free_principal(telnet_context,
5807c478bd9Sstevel@tonic-gate server);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate if (err)
5847c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
5857c478bd9Sstevel@tonic-gate "Error allocating krb5 replay cache: %s",
5867c478bd9Sstevel@tonic-gate error_message(err));
5877c478bd9Sstevel@tonic-gate else {
5887c478bd9Sstevel@tonic-gate err = krb5_auth_con_setrcache(telnet_context,
5897c478bd9Sstevel@tonic-gate auth_context,
5907c478bd9Sstevel@tonic-gate rcache);
5917c478bd9Sstevel@tonic-gate if (err)
5927c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
5937c478bd9Sstevel@tonic-gate "Error creating krb5 "
5947c478bd9Sstevel@tonic-gate "replay cache: %s",
5957c478bd9Sstevel@tonic-gate error_message(err));
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate if (!err && telnet_srvtab != NULL)
5997c478bd9Sstevel@tonic-gate err = krb5_kt_resolve(telnet_context,
6007c478bd9Sstevel@tonic-gate telnet_srvtab, &keytabid);
6017c478bd9Sstevel@tonic-gate if (!err)
6027c478bd9Sstevel@tonic-gate err = krb5_rd_req(telnet_context, &auth_context, &auth,
6037c478bd9Sstevel@tonic-gate NULL, keytabid, NULL, &ticket);
6047c478bd9Sstevel@tonic-gate if (err) {
6057c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
6067c478bd9Sstevel@tonic-gate "Error reading krb5 auth information:"
6077c478bd9Sstevel@tonic-gate " %s", error_message(err));
6087c478bd9Sstevel@tonic-gate goto errout;
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate /*
6127c478bd9Sstevel@tonic-gate * Verify that the correct principal was used
6137c478bd9Sstevel@tonic-gate */
6147c478bd9Sstevel@tonic-gate if (krb5_princ_component(telnet_context,
6157c478bd9Sstevel@tonic-gate ticket->server, 0)->length < MAXPRINCLEN) {
6167c478bd9Sstevel@tonic-gate char princ[MAXPRINCLEN];
6177c478bd9Sstevel@tonic-gate (void) strncpy(princ,
6187c478bd9Sstevel@tonic-gate krb5_princ_component(telnet_context,
6197c478bd9Sstevel@tonic-gate ticket->server, 0)->data,
6207c478bd9Sstevel@tonic-gate krb5_princ_component(telnet_context,
6217c478bd9Sstevel@tonic-gate ticket->server, 0)->length);
6227c478bd9Sstevel@tonic-gate princ[krb5_princ_component(telnet_context,
6237c478bd9Sstevel@tonic-gate ticket->server, 0)->length] = '\0';
6247c478bd9Sstevel@tonic-gate if (strcmp("host", princ)) {
6257c478bd9Sstevel@tonic-gate if (strlen(princ) < sizeof (errbuf) - 39) {
6267c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
6277c478bd9Sstevel@tonic-gate "incorrect service "
6287c478bd9Sstevel@tonic-gate "name: \"%s\" != "
6297c478bd9Sstevel@tonic-gate "\"host\"",
6307c478bd9Sstevel@tonic-gate princ);
6317c478bd9Sstevel@tonic-gate } else {
6327c478bd9Sstevel@tonic-gate (void) strncpy(errbuf,
6337c478bd9Sstevel@tonic-gate "incorrect service "
6347c478bd9Sstevel@tonic-gate "name: principal != "
6357c478bd9Sstevel@tonic-gate "\"host\"",
6367c478bd9Sstevel@tonic-gate sizeof (errbuf));
6377c478bd9Sstevel@tonic-gate }
6387c478bd9Sstevel@tonic-gate goto errout;
6397c478bd9Sstevel@tonic-gate }
6407c478bd9Sstevel@tonic-gate } else {
6417c478bd9Sstevel@tonic-gate (void) strlcpy(errbuf, "service name too long",
6427c478bd9Sstevel@tonic-gate sizeof (errbuf));
6437c478bd9Sstevel@tonic-gate goto errout;
6447c478bd9Sstevel@tonic-gate }
6457c478bd9Sstevel@tonic-gate
6467c478bd9Sstevel@tonic-gate err = krb5_auth_con_getauthenticator(telnet_context,
6477c478bd9Sstevel@tonic-gate auth_context,
6487c478bd9Sstevel@tonic-gate &authenticator);
6497c478bd9Sstevel@tonic-gate if (err) {
6507c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
6517c478bd9Sstevel@tonic-gate "Failed to get authenticator: %s",
6527c478bd9Sstevel@tonic-gate error_message(err));
6537c478bd9Sstevel@tonic-gate goto errout;
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate if ((ap->AuthHow & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON &&
6567c478bd9Sstevel@tonic-gate !authenticator->checksum) {
6577c478bd9Sstevel@tonic-gate (void) strlcpy(errbuf,
6587c478bd9Sstevel@tonic-gate "authenticator is missing checksum",
6597c478bd9Sstevel@tonic-gate sizeof (errbuf));
6607c478bd9Sstevel@tonic-gate goto errout;
6617c478bd9Sstevel@tonic-gate }
6627c478bd9Sstevel@tonic-gate if (authenticator->checksum) {
6637c478bd9Sstevel@tonic-gate char type_check[2];
6647c478bd9Sstevel@tonic-gate krb5_checksum *cksum = authenticator->checksum;
6657c478bd9Sstevel@tonic-gate krb5_keyblock *key;
6667c478bd9Sstevel@tonic-gate krb5_data input;
6677c478bd9Sstevel@tonic-gate krb5_boolean valid;
6687c478bd9Sstevel@tonic-gate
6697c478bd9Sstevel@tonic-gate type_check[0] = ap->AuthName;
6707c478bd9Sstevel@tonic-gate type_check[1] = ap->AuthHow;
6717c478bd9Sstevel@tonic-gate
6727c478bd9Sstevel@tonic-gate err = krb5_auth_con_getkey(telnet_context,
6737c478bd9Sstevel@tonic-gate auth_context, &key);
6747c478bd9Sstevel@tonic-gate if (err) {
6757c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
6767c478bd9Sstevel@tonic-gate "Failed to get key from "
6777c478bd9Sstevel@tonic-gate "authenticator: %s",
6787c478bd9Sstevel@tonic-gate error_message(err));
6797c478bd9Sstevel@tonic-gate goto errout;
6807c478bd9Sstevel@tonic-gate }
6817c478bd9Sstevel@tonic-gate
6827c478bd9Sstevel@tonic-gate input.data = type_check;
6837c478bd9Sstevel@tonic-gate input.length = 2;
6847c478bd9Sstevel@tonic-gate err = krb5_c_verify_checksum(telnet_context,
6857c478bd9Sstevel@tonic-gate key, 0,
6867c478bd9Sstevel@tonic-gate &input,
6877c478bd9Sstevel@tonic-gate cksum,
6887c478bd9Sstevel@tonic-gate &valid);
6897c478bd9Sstevel@tonic-gate if (!err && !valid)
6907c478bd9Sstevel@tonic-gate err = KRB5KRB_AP_ERR_BAD_INTEGRITY;
6917c478bd9Sstevel@tonic-gate
6927c478bd9Sstevel@tonic-gate if (err) {
6937c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
6947c478bd9Sstevel@tonic-gate "Kerberos checksum "
6957c478bd9Sstevel@tonic-gate "verification failed: "
6967c478bd9Sstevel@tonic-gate "%s",
6977c478bd9Sstevel@tonic-gate error_message(err));
6987c478bd9Sstevel@tonic-gate goto errout;
6997c478bd9Sstevel@tonic-gate }
7007c478bd9Sstevel@tonic-gate krb5_free_keyblock(telnet_context, key);
7017c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate
7037c478bd9Sstevel@tonic-gate krb5_free_authenticator(telnet_context, authenticator);
7047c478bd9Sstevel@tonic-gate if ((ap->AuthHow & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) {
7057c478bd9Sstevel@tonic-gate /* do ap_rep stuff here */
7067c478bd9Sstevel@tonic-gate if ((err = krb5_mk_rep(telnet_context, auth_context,
7077c478bd9Sstevel@tonic-gate &outbuf))) {
7087c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
7097c478bd9Sstevel@tonic-gate "Failed to make "
7107c478bd9Sstevel@tonic-gate "Kerberos auth reply: "
7117c478bd9Sstevel@tonic-gate "%s",
7127c478bd9Sstevel@tonic-gate error_message(err));
7137c478bd9Sstevel@tonic-gate goto errout;
7147c478bd9Sstevel@tonic-gate }
7157c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_RESPONSE, outbuf.data,
7167c478bd9Sstevel@tonic-gate outbuf.length);
7177c478bd9Sstevel@tonic-gate }
7187c478bd9Sstevel@tonic-gate if (krb5_unparse_name(telnet_context,
7197c478bd9Sstevel@tonic-gate ticket->enc_part2->client,
7207c478bd9Sstevel@tonic-gate &name))
7217c478bd9Sstevel@tonic-gate name = 0;
7227c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_ACCEPT, name, name ? -1 : 0);
7237c478bd9Sstevel@tonic-gate if (auth_debug) {
7247c478bd9Sstevel@tonic-gate syslog(LOG_NOTICE,
7257c478bd9Sstevel@tonic-gate "\tKerberos5 identifies user as ``%s''\r\n",
7267c478bd9Sstevel@tonic-gate name ? name : "");
7277c478bd9Sstevel@tonic-gate }
7287c478bd9Sstevel@tonic-gate if (name != NULL) {
7297c478bd9Sstevel@tonic-gate krb5_name = (char *)strdup(name);
7307c478bd9Sstevel@tonic-gate }
7317c478bd9Sstevel@tonic-gate auth_finished(ap, AUTH_USER);
7327c478bd9Sstevel@tonic-gate
7337c478bd9Sstevel@tonic-gate if (name != NULL)
7347c478bd9Sstevel@tonic-gate free(name);
735*0b4fd3b1SSurya Prakki (void) krb5_auth_con_getremotesubkey(telnet_context,
736*0b4fd3b1SSurya Prakki auth_context, &newkey);
7377c478bd9Sstevel@tonic-gate if (session_key != NULL) {
7387c478bd9Sstevel@tonic-gate krb5_free_keyblock(telnet_context, session_key);
7397c478bd9Sstevel@tonic-gate session_key = 0;
7407c478bd9Sstevel@tonic-gate }
7417c478bd9Sstevel@tonic-gate if (newkey != NULL) {
742*0b4fd3b1SSurya Prakki (void) krb5_copy_keyblock(telnet_context,
7437c478bd9Sstevel@tonic-gate newkey, &session_key);
7447c478bd9Sstevel@tonic-gate krb5_free_keyblock(telnet_context, newkey);
7457c478bd9Sstevel@tonic-gate } else {
746*0b4fd3b1SSurya Prakki (void) krb5_copy_keyblock(telnet_context,
747*0b4fd3b1SSurya Prakki ticket->enc_part2->session, &session_key);
7487c478bd9Sstevel@tonic-gate }
7497c478bd9Sstevel@tonic-gate
7507c478bd9Sstevel@tonic-gate /*
7517c478bd9Sstevel@tonic-gate * Initialize encryption stuff. Currently, we are only
7527c478bd9Sstevel@tonic-gate * supporting 8 byte keys and blocks. Check for this later.
7537c478bd9Sstevel@tonic-gate */
7547c478bd9Sstevel@tonic-gate skey.type = SK_DES;
7557c478bd9Sstevel@tonic-gate skey.length = DES_BLOCKSIZE;
7567c478bd9Sstevel@tonic-gate skey.data = session_key->contents;
7577c478bd9Sstevel@tonic-gate encrypt_session_key(&skey, &encr_data.encrypt);
7587c478bd9Sstevel@tonic-gate encrypt_session_key(&skey, &encr_data.decrypt);
7597c478bd9Sstevel@tonic-gate break;
7607c478bd9Sstevel@tonic-gate case KRB_FORWARD:
7617c478bd9Sstevel@tonic-gate inbuf.length = cnt;
7627c478bd9Sstevel@tonic-gate inbuf.data = (char *)data;
7637c478bd9Sstevel@tonic-gate if (auth_debug)
7647c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
7657c478bd9Sstevel@tonic-gate "RCVD KRB_FORWARD data (%d bytes)\n", cnt);
7667c478bd9Sstevel@tonic-gate
7677c478bd9Sstevel@tonic-gate if (auth_context != NULL) {
7687c478bd9Sstevel@tonic-gate krb5_rcache rcache;
7697c478bd9Sstevel@tonic-gate
7707c478bd9Sstevel@tonic-gate err = krb5_auth_con_getrcache(telnet_context,
7717c478bd9Sstevel@tonic-gate auth_context, &rcache);
7727c478bd9Sstevel@tonic-gate if (!err && !rcache) {
7737c478bd9Sstevel@tonic-gate err = krb5_sname_to_principal(telnet_context,
7747c478bd9Sstevel@tonic-gate 0, 0, KRB5_NT_SRV_HST, &server);
7757c478bd9Sstevel@tonic-gate if (!err) {
7767c478bd9Sstevel@tonic-gate err = krb5_get_server_rcache(
7777c478bd9Sstevel@tonic-gate telnet_context,
7787c478bd9Sstevel@tonic-gate krb5_princ_component(
7797c478bd9Sstevel@tonic-gate telnet_context,
7807c478bd9Sstevel@tonic-gate server, 0),
7817c478bd9Sstevel@tonic-gate &rcache);
7827c478bd9Sstevel@tonic-gate krb5_free_principal(telnet_context,
7837c478bd9Sstevel@tonic-gate server);
7847c478bd9Sstevel@tonic-gate }
7857c478bd9Sstevel@tonic-gate }
7867c478bd9Sstevel@tonic-gate if (err) {
7877c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
7887c478bd9Sstevel@tonic-gate "Error allocating krb5 replay cache: %s",
7897c478bd9Sstevel@tonic-gate error_message(err));
7907c478bd9Sstevel@tonic-gate } else {
7917c478bd9Sstevel@tonic-gate err = krb5_auth_con_setrcache(telnet_context,
7927c478bd9Sstevel@tonic-gate auth_context, rcache);
7937c478bd9Sstevel@tonic-gate if (err)
7947c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
7957c478bd9Sstevel@tonic-gate "Error creating krb5 replay cache:"
7967c478bd9Sstevel@tonic-gate " %s",
7977c478bd9Sstevel@tonic-gate error_message(err));
7987c478bd9Sstevel@tonic-gate }
7997c478bd9Sstevel@tonic-gate }
8007c478bd9Sstevel@tonic-gate /*
8017c478bd9Sstevel@tonic-gate * Use the 'rsaddr' and 'rsport' (remote service addr/port)
8027c478bd9Sstevel@tonic-gate * from the original connection. This data is used to
8037c478bd9Sstevel@tonic-gate * verify the forwarded credentials.
8047c478bd9Sstevel@tonic-gate */
8057c478bd9Sstevel@tonic-gate if (!(err = krb5_auth_con_setaddrs(telnet_context, auth_context,
8067c478bd9Sstevel@tonic-gate NULL, &rsaddr)))
8077c478bd9Sstevel@tonic-gate err = krb5_auth_con_setports(telnet_context,
8087c478bd9Sstevel@tonic-gate auth_context, NULL, &rsport);
8097c478bd9Sstevel@tonic-gate
8107c478bd9Sstevel@tonic-gate if (err == 0)
8117c478bd9Sstevel@tonic-gate /*
8127c478bd9Sstevel@tonic-gate * If all is well, store the forwarded creds in
8137c478bd9Sstevel@tonic-gate * the users local credential cache.
8147c478bd9Sstevel@tonic-gate */
8157c478bd9Sstevel@tonic-gate err = rd_and_store_forwarded_creds(telnet_context,
8167c478bd9Sstevel@tonic-gate auth_context, &inbuf,
8177c478bd9Sstevel@tonic-gate ticket,
8187c478bd9Sstevel@tonic-gate AuthenticatingUser);
8197c478bd9Sstevel@tonic-gate if (err) {
8207c478bd9Sstevel@tonic-gate (void) snprintf(errbuf, sizeof (errbuf),
8217c478bd9Sstevel@tonic-gate "Read forwarded creds failed: %s",
8227c478bd9Sstevel@tonic-gate error_message(err));
8237c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "%s", errbuf);
8247c478bd9Sstevel@tonic-gate
8257c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_FORWARD_REJECT, errbuf, -1);
8267c478bd9Sstevel@tonic-gate if (auth_debug)
8277c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8287c478bd9Sstevel@tonic-gate "\tCould not read "
8297c478bd9Sstevel@tonic-gate "forwarded credentials\r\n");
8307c478bd9Sstevel@tonic-gate } else
8317c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_FORWARD_ACCEPT, (void *) 0, 0);
8327c478bd9Sstevel@tonic-gate
8337c478bd9Sstevel@tonic-gate if (rsaddr.contents != NULL)
8347c478bd9Sstevel@tonic-gate free(rsaddr.contents);
8357c478bd9Sstevel@tonic-gate
8367c478bd9Sstevel@tonic-gate if (rsport.contents != NULL)
8377c478bd9Sstevel@tonic-gate free(rsport.contents);
8387c478bd9Sstevel@tonic-gate
8397c478bd9Sstevel@tonic-gate if (auth_debug)
8407c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\tForwarded "
8417c478bd9Sstevel@tonic-gate "credentials obtained\r\n");
8427c478bd9Sstevel@tonic-gate break;
8437c478bd9Sstevel@tonic-gate default:
8447c478bd9Sstevel@tonic-gate if (auth_debug)
8457c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8467c478bd9Sstevel@tonic-gate "\tUnknown Kerberos option %d\r\n",
8477c478bd9Sstevel@tonic-gate data[-1]);
8487c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_REJECT, (void *) 0, 0);
8497c478bd9Sstevel@tonic-gate break;
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate return;
8527c478bd9Sstevel@tonic-gate
8537c478bd9Sstevel@tonic-gate errout:
8547c478bd9Sstevel@tonic-gate reply_to_client(ap, KRB_REJECT, errbuf, -1);
8557c478bd9Sstevel@tonic-gate
8567c478bd9Sstevel@tonic-gate if (auth_debug)
8577c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\tKerberos V5 error: %s\r\n", errbuf);
8587c478bd9Sstevel@tonic-gate
8597c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "%s", errbuf);
8607c478bd9Sstevel@tonic-gate
8617c478bd9Sstevel@tonic-gate if (auth_context != NULL) {
862*0b4fd3b1SSurya Prakki (void) krb5_auth_con_free(telnet_context, auth_context);
8637c478bd9Sstevel@tonic-gate auth_context = 0;
8647c478bd9Sstevel@tonic-gate }
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate
8677c478bd9Sstevel@tonic-gate static int
krb5_init()8687c478bd9Sstevel@tonic-gate krb5_init()
8697c478bd9Sstevel@tonic-gate {
8707c478bd9Sstevel@tonic-gate int code = 0;
8717c478bd9Sstevel@tonic-gate
8727c478bd9Sstevel@tonic-gate if (telnet_context == NULL) {
8737c478bd9Sstevel@tonic-gate code = krb5_init_context(&telnet_context);
8747c478bd9Sstevel@tonic-gate if (code != 0 && auth_debug)
8757c478bd9Sstevel@tonic-gate syslog(LOG_NOTICE,
8767c478bd9Sstevel@tonic-gate "Cannot initialize Kerberos V5: %s",
8777c478bd9Sstevel@tonic-gate error_message(code));
8787c478bd9Sstevel@tonic-gate }
8797c478bd9Sstevel@tonic-gate
8807c478bd9Sstevel@tonic-gate return (code);
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate
8837c478bd9Sstevel@tonic-gate static void
auth_name(uchar_t * data,int cnt)8847c478bd9Sstevel@tonic-gate auth_name(uchar_t *data, int cnt)
8857c478bd9Sstevel@tonic-gate {
8867c478bd9Sstevel@tonic-gate char namebuf[MAXPRINCLEN];
8877c478bd9Sstevel@tonic-gate
8887c478bd9Sstevel@tonic-gate if (cnt < 1) {
8897c478bd9Sstevel@tonic-gate if (auth_debug)
8907c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8917c478bd9Sstevel@tonic-gate "\t(auth_name) Empty NAME in auth "
8927c478bd9Sstevel@tonic-gate "reply\n");
8937c478bd9Sstevel@tonic-gate return;
8947c478bd9Sstevel@tonic-gate }
8957c478bd9Sstevel@tonic-gate if (cnt > sizeof (namebuf)-1) {
8967c478bd9Sstevel@tonic-gate if (auth_debug)
8977c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
8987c478bd9Sstevel@tonic-gate "\t(auth_name) NAME exceeds %d bytes\n",
8997c478bd9Sstevel@tonic-gate sizeof (namebuf)-1);
9007c478bd9Sstevel@tonic-gate return;
9017c478bd9Sstevel@tonic-gate }
9027c478bd9Sstevel@tonic-gate (void) memcpy((void *)namebuf, (void *)data, cnt);
9037c478bd9Sstevel@tonic-gate namebuf[cnt] = 0;
9047c478bd9Sstevel@tonic-gate if (auth_debug)
9057c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(auth_name) name [%s]\n", namebuf);
9067c478bd9Sstevel@tonic-gate AuthenticatingUser = (char *)strdup(namebuf);
9077c478bd9Sstevel@tonic-gate }
9087c478bd9Sstevel@tonic-gate
9097c478bd9Sstevel@tonic-gate static void
auth_is(uchar_t * data,int cnt)9107c478bd9Sstevel@tonic-gate auth_is(uchar_t *data, int cnt)
9117c478bd9Sstevel@tonic-gate {
9127c478bd9Sstevel@tonic-gate AuthInfo *aptr = auth_list;
9137c478bd9Sstevel@tonic-gate
9147c478bd9Sstevel@tonic-gate if (cnt < 2)
9157c478bd9Sstevel@tonic-gate return;
9167c478bd9Sstevel@tonic-gate
9177c478bd9Sstevel@tonic-gate /*
9187c478bd9Sstevel@tonic-gate * We failed to negoiate secure authentication
9197c478bd9Sstevel@tonic-gate */
9207c478bd9Sstevel@tonic-gate if (data[0] == AUTHTYPE_NULL) {
9217c478bd9Sstevel@tonic-gate auth_finished(0, AUTH_REJECT);
9227c478bd9Sstevel@tonic-gate return;
9237c478bd9Sstevel@tonic-gate }
9247c478bd9Sstevel@tonic-gate
9257c478bd9Sstevel@tonic-gate while (aptr->AuthName != NULL &&
9267c478bd9Sstevel@tonic-gate (aptr->AuthName != data[0] || aptr->AuthHow != data[1]))
9277c478bd9Sstevel@tonic-gate aptr++;
9287c478bd9Sstevel@tonic-gate
9297c478bd9Sstevel@tonic-gate if (aptr != NULL) {
9307c478bd9Sstevel@tonic-gate if (auth_debug)
9317c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(auth_is) auth type is %s "
9327c478bd9Sstevel@tonic-gate "(%d bytes)\n", aptr->AuthString, cnt);
9337c478bd9Sstevel@tonic-gate
9347c478bd9Sstevel@tonic-gate if (aptr->AuthName == AUTHTYPE_KERBEROS_V5)
9357c478bd9Sstevel@tonic-gate kerberos5_is(aptr, data+2, cnt-2);
9367c478bd9Sstevel@tonic-gate }
9377c478bd9Sstevel@tonic-gate }
9387c478bd9Sstevel@tonic-gate
9397c478bd9Sstevel@tonic-gate static int
krb5_user_status(char * name,int namelen,int level)9407c478bd9Sstevel@tonic-gate krb5_user_status(char *name, int namelen, int level)
9417c478bd9Sstevel@tonic-gate {
9427c478bd9Sstevel@tonic-gate int retval = AUTH_USER;
9437c478bd9Sstevel@tonic-gate
9447c478bd9Sstevel@tonic-gate if (auth_debug)
9457c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(krb5_user_status) level = %d "
9467c478bd9Sstevel@tonic-gate "auth_level = %d user = %s\n",
9477c478bd9Sstevel@tonic-gate level, auth_level,
9487c478bd9Sstevel@tonic-gate (AuthenticatingUser != NULL ? AuthenticatingUser : ""));
9497c478bd9Sstevel@tonic-gate
9507c478bd9Sstevel@tonic-gate if (level < AUTH_USER)
9517c478bd9Sstevel@tonic-gate return (level);
9527c478bd9Sstevel@tonic-gate
9537c478bd9Sstevel@tonic-gate if (AuthenticatingUser != NULL &&
9547c478bd9Sstevel@tonic-gate (retval = krb5_kuserok(telnet_context, ticket->enc_part2->client,
9557c478bd9Sstevel@tonic-gate AuthenticatingUser))) {
9567c478bd9Sstevel@tonic-gate (void) strncpy(name, AuthenticatingUser, namelen);
9577c478bd9Sstevel@tonic-gate return (AUTH_VALID);
9587c478bd9Sstevel@tonic-gate } else {
9597c478bd9Sstevel@tonic-gate if (!retval)
9607c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
9617c478bd9Sstevel@tonic-gate "Krb5 principal lacks permission to "
9627c478bd9Sstevel@tonic-gate "access local account for %s",
9637c478bd9Sstevel@tonic-gate AuthenticatingUser);
9647c478bd9Sstevel@tonic-gate return (AUTH_USER);
9657c478bd9Sstevel@tonic-gate }
9667c478bd9Sstevel@tonic-gate }
9677c478bd9Sstevel@tonic-gate
9687c478bd9Sstevel@tonic-gate /*
9697c478bd9Sstevel@tonic-gate * Wrapper around /dev/urandom
9707c478bd9Sstevel@tonic-gate */
9717c478bd9Sstevel@tonic-gate static int
getrandom(char * buf,int buflen)9727c478bd9Sstevel@tonic-gate getrandom(char *buf, int buflen)
9737c478bd9Sstevel@tonic-gate {
9747c478bd9Sstevel@tonic-gate static int devrandom = -1;
9757c478bd9Sstevel@tonic-gate
9767c478bd9Sstevel@tonic-gate if (devrandom == -1 &&
9777c478bd9Sstevel@tonic-gate (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
9787c478bd9Sstevel@tonic-gate fatalperror(net, "Unable to open /dev/urandom: ",
9797c478bd9Sstevel@tonic-gate errno);
9807c478bd9Sstevel@tonic-gate return (-1);
9817c478bd9Sstevel@tonic-gate }
9827c478bd9Sstevel@tonic-gate
9837c478bd9Sstevel@tonic-gate if (read(devrandom, buf, buflen) == -1) {
9847c478bd9Sstevel@tonic-gate fatalperror(net, "Unable to read from /dev/urandom: ",
9857c478bd9Sstevel@tonic-gate errno);
9867c478bd9Sstevel@tonic-gate return (-1);
9877c478bd9Sstevel@tonic-gate }
9887c478bd9Sstevel@tonic-gate
9897c478bd9Sstevel@tonic-gate return (0);
9907c478bd9Sstevel@tonic-gate }
9917c478bd9Sstevel@tonic-gate
9927c478bd9Sstevel@tonic-gate /*
9937c478bd9Sstevel@tonic-gate * encrypt_init
9947c478bd9Sstevel@tonic-gate *
9957c478bd9Sstevel@tonic-gate * Initialize the encryption data structures
9967c478bd9Sstevel@tonic-gate */
9977c478bd9Sstevel@tonic-gate static void
encrypt_init()9987c478bd9Sstevel@tonic-gate encrypt_init()
9997c478bd9Sstevel@tonic-gate {
10007c478bd9Sstevel@tonic-gate (void) memset(&encr_data.encrypt, 0, sizeof (cipher_info_t));
10017c478bd9Sstevel@tonic-gate (void) memset(&encr_data.decrypt, 0, sizeof (cipher_info_t));
10027c478bd9Sstevel@tonic-gate
10037c478bd9Sstevel@tonic-gate encr_data.encrypt.state = ENCR_STATE_NOT_READY;
10047c478bd9Sstevel@tonic-gate encr_data.decrypt.state = ENCR_STATE_NOT_READY;
10057c478bd9Sstevel@tonic-gate }
10067c478bd9Sstevel@tonic-gate
10077c478bd9Sstevel@tonic-gate /*
10087c478bd9Sstevel@tonic-gate * encrypt_send_request_start
10097c478bd9Sstevel@tonic-gate *
10107c478bd9Sstevel@tonic-gate * Request that the remote side automatically start sending
10117c478bd9Sstevel@tonic-gate * encrypted output
10127c478bd9Sstevel@tonic-gate */
10137c478bd9Sstevel@tonic-gate static void
encrypt_send_request_start()10147c478bd9Sstevel@tonic-gate encrypt_send_request_start()
10157c478bd9Sstevel@tonic-gate {
10167c478bd9Sstevel@tonic-gate uchar_t buf[6+TELNET_MAXKEYIDLEN], *p;
10177c478bd9Sstevel@tonic-gate
10187c478bd9Sstevel@tonic-gate p = buf;
10197c478bd9Sstevel@tonic-gate
10207c478bd9Sstevel@tonic-gate *p++ = IAC;
10217c478bd9Sstevel@tonic-gate *p++ = SB;
10227c478bd9Sstevel@tonic-gate *p++ = TELOPT_ENCRYPT;
10237c478bd9Sstevel@tonic-gate *p++ = ENCRYPT_REQSTART;
10247c478bd9Sstevel@tonic-gate /*
10257c478bd9Sstevel@tonic-gate * We are telling the remote side which
10267c478bd9Sstevel@tonic-gate * decrypt key we will use so that it may
10277c478bd9Sstevel@tonic-gate * encrypt in the same key.
10287c478bd9Sstevel@tonic-gate */
10297c478bd9Sstevel@tonic-gate (void) memcpy(p, encr_data.decrypt.keyid, encr_data.decrypt.keyidlen);
10307c478bd9Sstevel@tonic-gate p += encr_data.decrypt.keyidlen;
10317c478bd9Sstevel@tonic-gate
10327c478bd9Sstevel@tonic-gate *p++ = IAC;
10337c478bd9Sstevel@tonic-gate *p++ = SE;
10347c478bd9Sstevel@tonic-gate
10357c478bd9Sstevel@tonic-gate write_data_len((const char *)buf, p-buf);
10367c478bd9Sstevel@tonic-gate netflush();
10377c478bd9Sstevel@tonic-gate if (enc_debug)
10387c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10397c478bd9Sstevel@tonic-gate "SENT TELOPT_ENCRYPT ENCRYPT_REQSTART\n");
10407c478bd9Sstevel@tonic-gate }
10417c478bd9Sstevel@tonic-gate
10427c478bd9Sstevel@tonic-gate /*
10437c478bd9Sstevel@tonic-gate * encrypt_is
10447c478bd9Sstevel@tonic-gate *
10457c478bd9Sstevel@tonic-gate * When we receive the TELOPT_ENCRYPT ENCRYPT_IS ...
10467c478bd9Sstevel@tonic-gate * message, the client is telling us that it will be sending
10477c478bd9Sstevel@tonic-gate * encrypted data using the indicated cipher.
10487c478bd9Sstevel@tonic-gate * We must initialize the read (decrypt) side of our connection
10497c478bd9Sstevel@tonic-gate */
10507c478bd9Sstevel@tonic-gate static void
encrypt_is(uchar_t * data,int cnt)10517c478bd9Sstevel@tonic-gate encrypt_is(uchar_t *data, int cnt)
10527c478bd9Sstevel@tonic-gate {
10537c478bd9Sstevel@tonic-gate register int type;
10547c478bd9Sstevel@tonic-gate register int iv_status = CFB64_IV_OK;
10557c478bd9Sstevel@tonic-gate register int lstate = 0;
10567c478bd9Sstevel@tonic-gate
10577c478bd9Sstevel@tonic-gate uchar_t sbbuf[] = {
10587c478bd9Sstevel@tonic-gate (uchar_t)IAC,
10597c478bd9Sstevel@tonic-gate (uchar_t)SB,
10607c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
10617c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_REPLY,
10627c478bd9Sstevel@tonic-gate (uchar_t)0, /* placeholder: sbbuf[4] */
10637c478bd9Sstevel@tonic-gate (uchar_t)CFB64_IV_OK, /* placeholder: sbbuf[5] */
10647c478bd9Sstevel@tonic-gate (uchar_t)IAC,
10657c478bd9Sstevel@tonic-gate (uchar_t)SE,
10667c478bd9Sstevel@tonic-gate };
10677c478bd9Sstevel@tonic-gate
10687c478bd9Sstevel@tonic-gate if (--cnt < 0)
10697c478bd9Sstevel@tonic-gate return;
10707c478bd9Sstevel@tonic-gate
10717c478bd9Sstevel@tonic-gate type = sbbuf[4] = *data++;
10727c478bd9Sstevel@tonic-gate
10737c478bd9Sstevel@tonic-gate /*
10747c478bd9Sstevel@tonic-gate * Steps to take:
10757c478bd9Sstevel@tonic-gate * 1. Create the proper stream Initialization vector
10767c478bd9Sstevel@tonic-gate * - copy the correct 'seed' to IV and output blocks
10777c478bd9Sstevel@tonic-gate * - set the correct key schedule
10787c478bd9Sstevel@tonic-gate * 2. Generate reply for the other side:
10797c478bd9Sstevel@tonic-gate * IAC SB TELOPT_ENCRYPT ENCRYPT_REPLY type CFB64_IV_OK
10807c478bd9Sstevel@tonic-gate * [ data ... ] IAC SE
10817c478bd9Sstevel@tonic-gate * 3. Tell crypto module: method, direction, IV
10827c478bd9Sstevel@tonic-gate */
10837c478bd9Sstevel@tonic-gate switch (type) {
10847c478bd9Sstevel@tonic-gate case TELOPT_ENCTYPE_DES_CFB64:
10857c478bd9Sstevel@tonic-gate encr_data.decrypt.type = type;
10867c478bd9Sstevel@tonic-gate
10877c478bd9Sstevel@tonic-gate lstate = encr_data.decrypt.state;
10887c478bd9Sstevel@tonic-gate if (enc_debug)
10897c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
10907c478bd9Sstevel@tonic-gate "\t(encrypt_is) initial state = %d\n",
10917c478bd9Sstevel@tonic-gate lstate);
10927c478bd9Sstevel@tonic-gate /*
10937c478bd9Sstevel@tonic-gate * Before we extract the IV bytes, make sure we got
10947c478bd9Sstevel@tonic-gate * enough data.
10957c478bd9Sstevel@tonic-gate */
10967c478bd9Sstevel@tonic-gate if (cnt < sizeof (Block)) {
10977c478bd9Sstevel@tonic-gate iv_status = CFB64_IV_BAD;
10987c478bd9Sstevel@tonic-gate if (enc_debug)
10997c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11007c478bd9Sstevel@tonic-gate "\t(encrypt_is) Not enough "
11017c478bd9Sstevel@tonic-gate "IV bytes\n");
11027c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_NOT_READY;
11037c478bd9Sstevel@tonic-gate } else {
11047c478bd9Sstevel@tonic-gate data++; /* skip over the CFB64_IV byte */
11057c478bd9Sstevel@tonic-gate (void) memcpy(encr_data.decrypt.ivec, data,
11067c478bd9Sstevel@tonic-gate sizeof (Block));
11077c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_IN_PROGRESS;
11087c478bd9Sstevel@tonic-gate }
11097c478bd9Sstevel@tonic-gate break;
11107c478bd9Sstevel@tonic-gate case TELOPT_ENCTYPE_NULL:
11117c478bd9Sstevel@tonic-gate encr_data.decrypt.type = type;
11127c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_RECV_IV;
11137c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_SEND_IV;
11147c478bd9Sstevel@tonic-gate if (enc_debug)
11157c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11167c478bd9Sstevel@tonic-gate "\t(encrypt_is) We accept NULL encr\n");
11177c478bd9Sstevel@tonic-gate break;
11187c478bd9Sstevel@tonic-gate default:
11197c478bd9Sstevel@tonic-gate iv_status = CFB64_IV_BAD;
11207c478bd9Sstevel@tonic-gate encr_data.decrypt.type = NULL;
11217c478bd9Sstevel@tonic-gate if (enc_debug)
11227c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11237c478bd9Sstevel@tonic-gate "\t(encrypt_is) Can't find type (%d) "
11247c478bd9Sstevel@tonic-gate "for initial negotiation\r\n",
11257c478bd9Sstevel@tonic-gate type);
11267c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_NOT_READY;
11277c478bd9Sstevel@tonic-gate break;
11287c478bd9Sstevel@tonic-gate }
11297c478bd9Sstevel@tonic-gate
11307c478bd9Sstevel@tonic-gate sbbuf[5] = (uchar_t)iv_status; /* either CFB64_IV_OK or BAD */
11317c478bd9Sstevel@tonic-gate
11327c478bd9Sstevel@tonic-gate if (iv_status == CFB64_IV_OK) {
11337c478bd9Sstevel@tonic-gate /*
11347c478bd9Sstevel@tonic-gate * send IV to crypto module and indicate it is for
11357c478bd9Sstevel@tonic-gate * decrypt only
11367c478bd9Sstevel@tonic-gate */
11377c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_RECV_IV; /* we received an OK IV */
11387c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont send an IV */
11397c478bd9Sstevel@tonic-gate } else {
11407c478bd9Sstevel@tonic-gate /* tell crypto module to disable crypto on "read" stream */
11417c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_NOT_READY;
11427c478bd9Sstevel@tonic-gate }
11437c478bd9Sstevel@tonic-gate
11447c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, sizeof (sbbuf));
11457c478bd9Sstevel@tonic-gate netflush();
11467c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
11477c478bd9Sstevel@tonic-gate if (enc_debug)
11487c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11497c478bd9Sstevel@tonic-gate "SENT TELOPT_ENCRYPT ENCRYPT_REPLY %s %s\n",
11507c478bd9Sstevel@tonic-gate ENCTYPE_NAME(type),
11517c478bd9Sstevel@tonic-gate (iv_status == CFB64_IV_OK ? "CFB64_IV_OK" :
11527c478bd9Sstevel@tonic-gate "CFB64_IV_BAD"));
11537c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
11547c478bd9Sstevel@tonic-gate /* Update the state of the decryption negotiation */
11557c478bd9Sstevel@tonic-gate encr_data.decrypt.state = lstate;
11567c478bd9Sstevel@tonic-gate
11577c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_NOT_READY)
11587c478bd9Sstevel@tonic-gate encr_data.decrypt.autoflag = 0;
11597c478bd9Sstevel@tonic-gate else {
11607c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_OK && encr_data.decrypt.autoflag)
11617c478bd9Sstevel@tonic-gate encrypt_send_request_start();
11627c478bd9Sstevel@tonic-gate }
11637c478bd9Sstevel@tonic-gate if (enc_debug)
11647c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
11657c478bd9Sstevel@tonic-gate "\t(encrypt_is) final DECRYPT state = %d\n",
11667c478bd9Sstevel@tonic-gate encr_data.decrypt.state);
11677c478bd9Sstevel@tonic-gate }
11687c478bd9Sstevel@tonic-gate
11697c478bd9Sstevel@tonic-gate /*
11707c478bd9Sstevel@tonic-gate * encrypt_send_encrypt_is
11717c478bd9Sstevel@tonic-gate *
11727c478bd9Sstevel@tonic-gate * Tell the client what encryption we will use
11737c478bd9Sstevel@tonic-gate * and what our IV will be.
11747c478bd9Sstevel@tonic-gate */
11757c478bd9Sstevel@tonic-gate static int
encrypt_send_encrypt_is()11767c478bd9Sstevel@tonic-gate encrypt_send_encrypt_is()
11777c478bd9Sstevel@tonic-gate {
11787c478bd9Sstevel@tonic-gate register int lstate;
11797c478bd9Sstevel@tonic-gate krb5_error_code kret;
11807c478bd9Sstevel@tonic-gate uchar_t sbbuf[MAXOPTLEN], *p;
11817c478bd9Sstevel@tonic-gate int i;
11827c478bd9Sstevel@tonic-gate
11837c478bd9Sstevel@tonic-gate lstate = encr_data.encrypt.state;
11847c478bd9Sstevel@tonic-gate
11857c478bd9Sstevel@tonic-gate if (encr_data.encrypt.type == ENCTYPE_NULL) {
11867c478bd9Sstevel@tonic-gate /*
11877c478bd9Sstevel@tonic-gate * Haven't received ENCRYPT SUPPORT yet or we couldn't agree
11887c478bd9Sstevel@tonic-gate * on a cipher.
11897c478bd9Sstevel@tonic-gate */
11907c478bd9Sstevel@tonic-gate return (lstate);
11917c478bd9Sstevel@tonic-gate }
11927c478bd9Sstevel@tonic-gate
11937c478bd9Sstevel@tonic-gate /*
11947c478bd9Sstevel@tonic-gate * - Create a random DES key
11957c478bd9Sstevel@tonic-gate *
11967c478bd9Sstevel@tonic-gate * - DES ECB encrypt
11977c478bd9Sstevel@tonic-gate * encrypt the IV using itself as the key.
11987c478bd9Sstevel@tonic-gate *
11997c478bd9Sstevel@tonic-gate * - Send response
12007c478bd9Sstevel@tonic-gate * IAC SB TELOPT_ENCRYPT ENCRYPT_IS CFB64 FB64_IV [ feed block ]
12017c478bd9Sstevel@tonic-gate * IAC SE
12027c478bd9Sstevel@tonic-gate *
12037c478bd9Sstevel@tonic-gate */
12047c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_NOT_READY)
12057c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_IN_PROGRESS;
12067c478bd9Sstevel@tonic-gate else if ((lstate & ENCR_STATE_NO_SEND_IV) == 0) {
12077c478bd9Sstevel@tonic-gate if (enc_debug)
12087c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12097c478bd9Sstevel@tonic-gate "\t(encrypt_send_is) IV already sent,"
12107c478bd9Sstevel@tonic-gate " state = %d\n", lstate);
12117c478bd9Sstevel@tonic-gate return (lstate);
12127c478bd9Sstevel@tonic-gate }
12137c478bd9Sstevel@tonic-gate
12147c478bd9Sstevel@tonic-gate if (!VALIDKEY(encr_data.encrypt.krbdes_key)) {
12157c478bd9Sstevel@tonic-gate /*
12167c478bd9Sstevel@tonic-gate * Invalid key, set flag so we try again later
12177c478bd9Sstevel@tonic-gate * when we get a good one
12187c478bd9Sstevel@tonic-gate */
12197c478bd9Sstevel@tonic-gate encr_data.encrypt.need_start = 1;
12207c478bd9Sstevel@tonic-gate if (enc_debug)
12217c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12227c478bd9Sstevel@tonic-gate "\t(encrypt_send_is) No Key, cannot "
12237c478bd9Sstevel@tonic-gate "start encryption yet\n");
12247c478bd9Sstevel@tonic-gate return (lstate);
12257c478bd9Sstevel@tonic-gate }
12267c478bd9Sstevel@tonic-gate if (enc_debug)
12277c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12287c478bd9Sstevel@tonic-gate "\t(encrypt_send_is) Creating new feed\n");
12297c478bd9Sstevel@tonic-gate
12307c478bd9Sstevel@tonic-gate /*
12317c478bd9Sstevel@tonic-gate * Create a random feed and send it over.
12327c478bd9Sstevel@tonic-gate *
12337c478bd9Sstevel@tonic-gate * Use the /dev/[u]random interface to generate
12347c478bd9Sstevel@tonic-gate * our encryption IV.
12357c478bd9Sstevel@tonic-gate */
12367c478bd9Sstevel@tonic-gate kret = getrandom((char *)encr_data.encrypt.ivec, sizeof (Block));
12377c478bd9Sstevel@tonic-gate
12387c478bd9Sstevel@tonic-gate if (kret) {
12397c478bd9Sstevel@tonic-gate if (enc_debug)
12407c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12417c478bd9Sstevel@tonic-gate "\t(encrypt_send_is) error from "
12427c478bd9Sstevel@tonic-gate "getrandom: %d\n", kret);
12437c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Failed to create encryption key (err %d)\n");
12447c478bd9Sstevel@tonic-gate encr_data.encrypt.type = ENCTYPE_NULL;
12457c478bd9Sstevel@tonic-gate } else {
12467c478bd9Sstevel@tonic-gate mit_des_fixup_key_parity(encr_data.encrypt.ivec);
12477c478bd9Sstevel@tonic-gate }
12487c478bd9Sstevel@tonic-gate
12497c478bd9Sstevel@tonic-gate p = sbbuf;
12507c478bd9Sstevel@tonic-gate *p++ = IAC;
12517c478bd9Sstevel@tonic-gate *p++ = SB;
12527c478bd9Sstevel@tonic-gate *p++ = TELOPT_ENCRYPT;
12537c478bd9Sstevel@tonic-gate *p++ = ENCRYPT_IS;
12547c478bd9Sstevel@tonic-gate *p++ = encr_data.encrypt.type;
12557c478bd9Sstevel@tonic-gate *p++ = CFB64_IV;
12567c478bd9Sstevel@tonic-gate
12577c478bd9Sstevel@tonic-gate /*
12587c478bd9Sstevel@tonic-gate * Copy the IV bytes individually so that when a
12597c478bd9Sstevel@tonic-gate * 255 (telnet IAC) is used, it can be "escaped" by
12607c478bd9Sstevel@tonic-gate * adding it twice (telnet RFC 854).
12617c478bd9Sstevel@tonic-gate */
12627c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof (Block); i++)
12637c478bd9Sstevel@tonic-gate if ((*p++ = encr_data.encrypt.ivec[i]) == IAC)
12647c478bd9Sstevel@tonic-gate *p++ = IAC;
12657c478bd9Sstevel@tonic-gate
12667c478bd9Sstevel@tonic-gate *p++ = IAC;
12677c478bd9Sstevel@tonic-gate *p++ = SE;
12687c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
12697c478bd9Sstevel@tonic-gate netflush();
12707c478bd9Sstevel@tonic-gate
12717c478bd9Sstevel@tonic-gate if (!kret) {
12727c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_SEND_IV; /* we sent our IV */
12737c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_SEND_IV; /* dont need decrypt IV */
12747c478bd9Sstevel@tonic-gate }
12757c478bd9Sstevel@tonic-gate encr_data.encrypt.state = lstate;
12767c478bd9Sstevel@tonic-gate
12777c478bd9Sstevel@tonic-gate if (enc_debug) {
12787c478bd9Sstevel@tonic-gate int i;
12797c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
12807c478bd9Sstevel@tonic-gate "SENT TELOPT_ENCRYPT ENCRYPT_IS %d CFB64_IV ",
12817c478bd9Sstevel@tonic-gate encr_data.encrypt.type);
12827c478bd9Sstevel@tonic-gate for (i = 0; i < (p-sbbuf); i++)
12837c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%d ", (int)sbbuf[i]);
12847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n");
12857c478bd9Sstevel@tonic-gate }
12867c478bd9Sstevel@tonic-gate
12877c478bd9Sstevel@tonic-gate return (lstate);
12887c478bd9Sstevel@tonic-gate }
12897c478bd9Sstevel@tonic-gate
12907c478bd9Sstevel@tonic-gate /*
12917c478bd9Sstevel@tonic-gate * stop_stream
12927c478bd9Sstevel@tonic-gate *
12937c478bd9Sstevel@tonic-gate * Utility routine to send a CRIOCSTOP ioctl to the
12947c478bd9Sstevel@tonic-gate * crypto module (cryptmod).
12957c478bd9Sstevel@tonic-gate */
12967c478bd9Sstevel@tonic-gate static void
stop_stream(int fd,int dir)12977c478bd9Sstevel@tonic-gate stop_stream(int fd, int dir)
12987c478bd9Sstevel@tonic-gate {
12997c478bd9Sstevel@tonic-gate struct strioctl crioc;
13007c478bd9Sstevel@tonic-gate uint32_t stopdir = dir;
13017c478bd9Sstevel@tonic-gate
13027c478bd9Sstevel@tonic-gate crioc.ic_cmd = CRYPTIOCSTOP;
13037c478bd9Sstevel@tonic-gate crioc.ic_timout = -1;
13047c478bd9Sstevel@tonic-gate crioc.ic_len = sizeof (stopdir);
13057c478bd9Sstevel@tonic-gate crioc.ic_dp = (char *)&stopdir;
13067c478bd9Sstevel@tonic-gate
13077c478bd9Sstevel@tonic-gate if (ioctl(fd, I_STR, &crioc)) {
13087c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Error sending CRYPTIOCSTOP ioctl: %m");
13097c478bd9Sstevel@tonic-gate }
13107c478bd9Sstevel@tonic-gate }
13117c478bd9Sstevel@tonic-gate
13127c478bd9Sstevel@tonic-gate /*
13137c478bd9Sstevel@tonic-gate * start_stream
13147c478bd9Sstevel@tonic-gate *
13157c478bd9Sstevel@tonic-gate * Utility routine to send a CRYPTIOCSTART ioctl to the
13167c478bd9Sstevel@tonic-gate * crypto module (cryptmod). This routine may contain optional
13177c478bd9Sstevel@tonic-gate * payload data that the cryptmod will interpret as bytes that
13187c478bd9Sstevel@tonic-gate * need to be decrypted and sent back up to the application
13197c478bd9Sstevel@tonic-gate * via the data stream.
13207c478bd9Sstevel@tonic-gate */
13217c478bd9Sstevel@tonic-gate static void
start_stream(int fd,int dir,int datalen,char * data)13227c478bd9Sstevel@tonic-gate start_stream(int fd, int dir, int datalen, char *data)
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate struct strioctl crioc;
13257c478bd9Sstevel@tonic-gate
13267c478bd9Sstevel@tonic-gate crioc.ic_cmd = (dir == CRYPT_ENCRYPT ? CRYPTIOCSTARTENC :
13277c478bd9Sstevel@tonic-gate CRYPTIOCSTARTDEC);
13287c478bd9Sstevel@tonic-gate crioc.ic_timout = -1;
13297c478bd9Sstevel@tonic-gate crioc.ic_len = datalen;
13307c478bd9Sstevel@tonic-gate crioc.ic_dp = data;
13317c478bd9Sstevel@tonic-gate
13327c478bd9Sstevel@tonic-gate if (ioctl(fd, I_STR, &crioc)) {
13337c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Error sending CRYPTIOCSTART ioctl: %m");
13347c478bd9Sstevel@tonic-gate }
13357c478bd9Sstevel@tonic-gate }
13367c478bd9Sstevel@tonic-gate
13377c478bd9Sstevel@tonic-gate /*
13387c478bd9Sstevel@tonic-gate * encrypt_start_output
13397c478bd9Sstevel@tonic-gate *
13407c478bd9Sstevel@tonic-gate * Tell the other side to start encrypting its data
13417c478bd9Sstevel@tonic-gate */
13427c478bd9Sstevel@tonic-gate static void
encrypt_start_output()13437c478bd9Sstevel@tonic-gate encrypt_start_output()
13447c478bd9Sstevel@tonic-gate {
13457c478bd9Sstevel@tonic-gate int lstate;
13467c478bd9Sstevel@tonic-gate uchar_t *p;
13477c478bd9Sstevel@tonic-gate uchar_t sbbuf[MAXOPTLEN];
13487c478bd9Sstevel@tonic-gate struct strioctl crioc;
13497c478bd9Sstevel@tonic-gate struct cr_info_t cki;
13507c478bd9Sstevel@tonic-gate
13517c478bd9Sstevel@tonic-gate /*
13527c478bd9Sstevel@tonic-gate * Initialize crypto and send the ENCRYPT_IS msg
13537c478bd9Sstevel@tonic-gate */
13547c478bd9Sstevel@tonic-gate lstate = encrypt_send_encrypt_is();
13557c478bd9Sstevel@tonic-gate
13567c478bd9Sstevel@tonic-gate if (lstate != ENCR_STATE_OK) {
13577c478bd9Sstevel@tonic-gate if (enc_debug)
13587c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
13597c478bd9Sstevel@tonic-gate "\t(encrypt_start_output) ENCRYPT state "
13607c478bd9Sstevel@tonic-gate "= %d\n", lstate);
13617c478bd9Sstevel@tonic-gate return;
13627c478bd9Sstevel@tonic-gate }
13637c478bd9Sstevel@tonic-gate
13647c478bd9Sstevel@tonic-gate p = sbbuf;
13657c478bd9Sstevel@tonic-gate
13667c478bd9Sstevel@tonic-gate *p++ = IAC;
13677c478bd9Sstevel@tonic-gate *p++ = SB;
13687c478bd9Sstevel@tonic-gate *p++ = TELOPT_ENCRYPT;
13697c478bd9Sstevel@tonic-gate *p++ = ENCRYPT_START;
13707c478bd9Sstevel@tonic-gate
13717c478bd9Sstevel@tonic-gate (void) memcpy(p, encr_data.encrypt.keyid, encr_data.encrypt.keyidlen);
13727c478bd9Sstevel@tonic-gate p += encr_data.encrypt.keyidlen;
13737c478bd9Sstevel@tonic-gate
13747c478bd9Sstevel@tonic-gate *p++ = IAC;
13757c478bd9Sstevel@tonic-gate *p++ = SE;
13767c478bd9Sstevel@tonic-gate
13777c478bd9Sstevel@tonic-gate /* Flush this data out before we start encrypting */
13787c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, (int)(p-sbbuf));
13797c478bd9Sstevel@tonic-gate netflush();
13807c478bd9Sstevel@tonic-gate
13817c478bd9Sstevel@tonic-gate if (enc_debug)
13827c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_START %d "
13837c478bd9Sstevel@tonic-gate "(lstate = %d) data waiting = %d\n",
13847c478bd9Sstevel@tonic-gate (int)encr_data.encrypt.keyid[0],
13857c478bd9Sstevel@tonic-gate lstate, nfrontp-nbackp);
13867c478bd9Sstevel@tonic-gate
13877c478bd9Sstevel@tonic-gate encr_data.encrypt.state = lstate;
13887c478bd9Sstevel@tonic-gate
13897c478bd9Sstevel@tonic-gate /*
13907c478bd9Sstevel@tonic-gate * tell crypto module what key to use for encrypting
13917c478bd9Sstevel@tonic-gate * Note that the ENCRYPT has not yet been enabled, but we
13927c478bd9Sstevel@tonic-gate * need to first set the crypto key to use.
13937c478bd9Sstevel@tonic-gate */
13947c478bd9Sstevel@tonic-gate cki.direction_mask = CRYPT_ENCRYPT;
13957c478bd9Sstevel@tonic-gate
13967c478bd9Sstevel@tonic-gate if (encr_data.encrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
13977c478bd9Sstevel@tonic-gate cki.crypto_method = CRYPT_METHOD_DES_CFB;
13987c478bd9Sstevel@tonic-gate } else {
13997c478bd9Sstevel@tonic-gate if (enc_debug)
14007c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
14017c478bd9Sstevel@tonic-gate "\t(encrypt_start_output) - unknown "
14027c478bd9Sstevel@tonic-gate "crypto_method %d\n",
14037c478bd9Sstevel@tonic-gate encr_data.encrypt.type);
14047c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "unrecognized crypto encrypt method: %d",
14057c478bd9Sstevel@tonic-gate encr_data.encrypt.type);
14067c478bd9Sstevel@tonic-gate
14077c478bd9Sstevel@tonic-gate return;
14087c478bd9Sstevel@tonic-gate }
14097c478bd9Sstevel@tonic-gate
14107c478bd9Sstevel@tonic-gate /*
14117c478bd9Sstevel@tonic-gate * If we previously configured this crypto method, we dont want to
14127c478bd9Sstevel@tonic-gate * overwrite the key or ivec information already given to the crypto
14137c478bd9Sstevel@tonic-gate * module as it will cause the cipher data between the client and server
14147c478bd9Sstevel@tonic-gate * to become out of synch and impossible to decipher.
14157c478bd9Sstevel@tonic-gate */
14167c478bd9Sstevel@tonic-gate if (encr_data.encrypt.setup == cki.crypto_method) {
14177c478bd9Sstevel@tonic-gate cki.keylen = 0;
14187c478bd9Sstevel@tonic-gate cki.iveclen = 0;
14197c478bd9Sstevel@tonic-gate } else {
14207c478bd9Sstevel@tonic-gate cki.keylen = DES_BLOCKSIZE;
14217c478bd9Sstevel@tonic-gate (void) memcpy(cki.key, (void *)encr_data.encrypt.krbdes_key,
14227c478bd9Sstevel@tonic-gate DES_BLOCKSIZE);
14237c478bd9Sstevel@tonic-gate
14247c478bd9Sstevel@tonic-gate cki.iveclen = DES_BLOCKSIZE;
14257c478bd9Sstevel@tonic-gate (void) memcpy(cki.ivec, (void *)encr_data.encrypt.ivec,
14267c478bd9Sstevel@tonic-gate DES_BLOCKSIZE);
14277c478bd9Sstevel@tonic-gate
14287c478bd9Sstevel@tonic-gate cki.ivec_usage = IVEC_ONETIME;
14297c478bd9Sstevel@tonic-gate }
14307c478bd9Sstevel@tonic-gate
14317c478bd9Sstevel@tonic-gate cki.option_mask = 0;
14327c478bd9Sstevel@tonic-gate
14337c478bd9Sstevel@tonic-gate /* Stop encrypt side prior to setup so we dont lose data */
14347c478bd9Sstevel@tonic-gate stop_stream(cryptmod_fd, CRYPT_ENCRYPT);
14357c478bd9Sstevel@tonic-gate
14367c478bd9Sstevel@tonic-gate crioc.ic_cmd = CRYPTIOCSETUP;
14377c478bd9Sstevel@tonic-gate crioc.ic_timout = -1;
14387c478bd9Sstevel@tonic-gate crioc.ic_len = sizeof (struct cr_info_t);
14397c478bd9Sstevel@tonic-gate crioc.ic_dp = (char *)&cki;
14407c478bd9Sstevel@tonic-gate
14417c478bd9Sstevel@tonic-gate if (ioctl(cryptmod_fd, I_STR, &crioc)) {
14427c478bd9Sstevel@tonic-gate perror("ioctl(CRYPTIOCSETUP) [encrypt_start_output] error");
14437c478bd9Sstevel@tonic-gate } else {
14447c478bd9Sstevel@tonic-gate /* Setup completed OK */
14457c478bd9Sstevel@tonic-gate encr_data.encrypt.setup = cki.crypto_method;
14467c478bd9Sstevel@tonic-gate }
14477c478bd9Sstevel@tonic-gate
14487c478bd9Sstevel@tonic-gate /*
14497c478bd9Sstevel@tonic-gate * We do not check for "stuck" data when setting up the
14507c478bd9Sstevel@tonic-gate * outbound "encrypt" channel. Any data queued prior to
14517c478bd9Sstevel@tonic-gate * this IOCTL will get processed correctly without our help.
14527c478bd9Sstevel@tonic-gate */
14537c478bd9Sstevel@tonic-gate start_stream(cryptmod_fd, CRYPT_ENCRYPT, 0, NULL);
14547c478bd9Sstevel@tonic-gate
14557c478bd9Sstevel@tonic-gate /*
14567c478bd9Sstevel@tonic-gate * tell crypto module to start encrypting
14577c478bd9Sstevel@tonic-gate */
14587c478bd9Sstevel@tonic-gate if (enc_debug)
14597c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
14607c478bd9Sstevel@tonic-gate "\t(encrypt_start_output) Encrypting output\n");
14617c478bd9Sstevel@tonic-gate }
14627c478bd9Sstevel@tonic-gate
14637c478bd9Sstevel@tonic-gate /*
14647c478bd9Sstevel@tonic-gate * encrypt_request_start
14657c478bd9Sstevel@tonic-gate *
14667c478bd9Sstevel@tonic-gate * The client requests that we start encryption immediately after
14677c478bd9Sstevel@tonic-gate * successful negotiation
14687c478bd9Sstevel@tonic-gate */
14697c478bd9Sstevel@tonic-gate static void
encrypt_request_start(void)14707c478bd9Sstevel@tonic-gate encrypt_request_start(void)
14717c478bd9Sstevel@tonic-gate {
14727c478bd9Sstevel@tonic-gate if (encr_data.encrypt.type == ENCTYPE_NULL) {
14737c478bd9Sstevel@tonic-gate encr_data.encrypt.autoflag = 1;
14747c478bd9Sstevel@tonic-gate if (enc_debug)
14757c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(encrypt_request_start) "
14767c478bd9Sstevel@tonic-gate "autoencrypt = ON\n");
14777c478bd9Sstevel@tonic-gate } else {
14787c478bd9Sstevel@tonic-gate encrypt_start_output();
14797c478bd9Sstevel@tonic-gate }
14807c478bd9Sstevel@tonic-gate }
14817c478bd9Sstevel@tonic-gate
14827c478bd9Sstevel@tonic-gate /*
14837c478bd9Sstevel@tonic-gate * encrypt_end
14847c478bd9Sstevel@tonic-gate *
14857c478bd9Sstevel@tonic-gate * ENCRYPT END received, stop decrypting the read stream
14867c478bd9Sstevel@tonic-gate */
14877c478bd9Sstevel@tonic-gate static void
encrypt_end(int direction)14887c478bd9Sstevel@tonic-gate encrypt_end(int direction)
14897c478bd9Sstevel@tonic-gate {
14907c478bd9Sstevel@tonic-gate struct cr_info_t cki;
14917c478bd9Sstevel@tonic-gate struct strioctl crioc;
14927c478bd9Sstevel@tonic-gate uint32_t stopdir;
14937c478bd9Sstevel@tonic-gate
14947c478bd9Sstevel@tonic-gate stopdir = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
14957c478bd9Sstevel@tonic-gate CRYPT_ENCRYPT);
14967c478bd9Sstevel@tonic-gate
14977c478bd9Sstevel@tonic-gate stop_stream(cryptmod_fd, stopdir);
14987c478bd9Sstevel@tonic-gate
14997c478bd9Sstevel@tonic-gate /*
15007c478bd9Sstevel@tonic-gate * Call this function when we wish to disable crypto in
15017c478bd9Sstevel@tonic-gate * either direction (ENCRYPT or DECRYPT)
15027c478bd9Sstevel@tonic-gate */
15037c478bd9Sstevel@tonic-gate cki.direction_mask = (direction == TELNET_DIR_DECRYPT ? CRYPT_DECRYPT :
15047c478bd9Sstevel@tonic-gate CRYPT_ENCRYPT);
15057c478bd9Sstevel@tonic-gate cki.crypto_method = CRYPT_METHOD_NONE;
15067c478bd9Sstevel@tonic-gate cki.option_mask = 0;
15077c478bd9Sstevel@tonic-gate
15087c478bd9Sstevel@tonic-gate cki.keylen = 0;
15097c478bd9Sstevel@tonic-gate cki.iveclen = 0;
15103ff1fd9dSethindra cki.ivec_usage = IVEC_ONETIME;
15117c478bd9Sstevel@tonic-gate
15127c478bd9Sstevel@tonic-gate crioc.ic_cmd = CRYPTIOCSETUP;
15137c478bd9Sstevel@tonic-gate crioc.ic_timout = -1;
15147c478bd9Sstevel@tonic-gate crioc.ic_len = sizeof (cki);
15157c478bd9Sstevel@tonic-gate crioc.ic_dp = (char *)&cki;
15167c478bd9Sstevel@tonic-gate
15177c478bd9Sstevel@tonic-gate if (ioctl(cryptmod_fd, I_STR, &crioc)) {
15187c478bd9Sstevel@tonic-gate perror("ioctl(CRYPTIOCSETUP) [encrypt_end] error");
15197c478bd9Sstevel@tonic-gate }
15207c478bd9Sstevel@tonic-gate
15217c478bd9Sstevel@tonic-gate start_stream(cryptmod_fd, stopdir, 0, NULL);
15227c478bd9Sstevel@tonic-gate }
15237c478bd9Sstevel@tonic-gate
15247c478bd9Sstevel@tonic-gate /*
15257c478bd9Sstevel@tonic-gate * encrypt_request_end
15267c478bd9Sstevel@tonic-gate *
15277c478bd9Sstevel@tonic-gate * When we receive a REQEND from the client, it means
15287c478bd9Sstevel@tonic-gate * that we are supposed to stop encrypting
15297c478bd9Sstevel@tonic-gate */
15307c478bd9Sstevel@tonic-gate static void
encrypt_request_end()15317c478bd9Sstevel@tonic-gate encrypt_request_end()
15327c478bd9Sstevel@tonic-gate {
15337c478bd9Sstevel@tonic-gate /*
15347c478bd9Sstevel@tonic-gate * Tell the other side we are done encrypting
15357c478bd9Sstevel@tonic-gate */
15367c478bd9Sstevel@tonic-gate
15377c478bd9Sstevel@tonic-gate write_data("%c%c%c%c%c%c",
15387c478bd9Sstevel@tonic-gate (uchar_t)IAC,
15397c478bd9Sstevel@tonic-gate (uchar_t)SB,
15407c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
15417c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_END,
15427c478bd9Sstevel@tonic-gate (uchar_t)IAC,
15437c478bd9Sstevel@tonic-gate (uchar_t)SE);
15447c478bd9Sstevel@tonic-gate netflush();
15457c478bd9Sstevel@tonic-gate if (enc_debug)
15467c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_END\n");
15477c478bd9Sstevel@tonic-gate
15487c478bd9Sstevel@tonic-gate /*
15497c478bd9Sstevel@tonic-gate * Turn off encryption of the write stream
15507c478bd9Sstevel@tonic-gate */
15517c478bd9Sstevel@tonic-gate encrypt_end(TELNET_DIR_ENCRYPT);
15527c478bd9Sstevel@tonic-gate }
15537c478bd9Sstevel@tonic-gate
15547c478bd9Sstevel@tonic-gate /*
15557c478bd9Sstevel@tonic-gate * encrypt_send_request_end
15567c478bd9Sstevel@tonic-gate *
15577c478bd9Sstevel@tonic-gate * We stop encrypting the write stream and tell the other side about it.
15587c478bd9Sstevel@tonic-gate */
15597c478bd9Sstevel@tonic-gate static void
encrypt_send_request_end()15607c478bd9Sstevel@tonic-gate encrypt_send_request_end()
15617c478bd9Sstevel@tonic-gate {
15627c478bd9Sstevel@tonic-gate write_data("%c%c%c%c%c%c",
15637c478bd9Sstevel@tonic-gate (uchar_t)IAC,
15647c478bd9Sstevel@tonic-gate (uchar_t)SB,
15657c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
15667c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_REQEND,
15677c478bd9Sstevel@tonic-gate (uchar_t)IAC,
15687c478bd9Sstevel@tonic-gate (uchar_t)SE);
15697c478bd9Sstevel@tonic-gate netflush();
15707c478bd9Sstevel@tonic-gate if (enc_debug)
15717c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT TELOPT_ENCRYPT ENCRYPT_REQEND\n");
15727c478bd9Sstevel@tonic-gate }
15737c478bd9Sstevel@tonic-gate
15747c478bd9Sstevel@tonic-gate /*
15757c478bd9Sstevel@tonic-gate * encrypt_start
15767c478bd9Sstevel@tonic-gate *
15777c478bd9Sstevel@tonic-gate * The client is going to start sending encrypted data
15787c478bd9Sstevel@tonic-gate * using the previously negotiated cipher (see what we set
15797c478bd9Sstevel@tonic-gate * when we did the REPLY in encrypt_is).
15807c478bd9Sstevel@tonic-gate */
15817c478bd9Sstevel@tonic-gate static void
encrypt_start(void)15827c478bd9Sstevel@tonic-gate encrypt_start(void)
15837c478bd9Sstevel@tonic-gate {
15847c478bd9Sstevel@tonic-gate struct cr_info_t cki;
15857c478bd9Sstevel@tonic-gate struct strioctl crioc;
15867c478bd9Sstevel@tonic-gate int bytes = 0;
15877c478bd9Sstevel@tonic-gate char *dataptr = NULL;
15887c478bd9Sstevel@tonic-gate
15897c478bd9Sstevel@tonic-gate if (encr_data.decrypt.type == ENCTYPE_NULL) {
15907c478bd9Sstevel@tonic-gate if (enc_debug)
15917c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
15927c478bd9Sstevel@tonic-gate "\t(encrypt_start) No DECRYPT method "
15937c478bd9Sstevel@tonic-gate "defined yet\n");
15947c478bd9Sstevel@tonic-gate encrypt_send_request_end();
15957c478bd9Sstevel@tonic-gate return;
15967c478bd9Sstevel@tonic-gate }
15977c478bd9Sstevel@tonic-gate
15987c478bd9Sstevel@tonic-gate cki.direction_mask = CRYPT_DECRYPT;
15997c478bd9Sstevel@tonic-gate
16007c478bd9Sstevel@tonic-gate if (encr_data.decrypt.type == TELOPT_ENCTYPE_DES_CFB64) {
16017c478bd9Sstevel@tonic-gate cki.crypto_method = CRYPT_METHOD_DES_CFB;
16027c478bd9Sstevel@tonic-gate } else {
16037c478bd9Sstevel@tonic-gate if (enc_debug)
16047c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16057c478bd9Sstevel@tonic-gate "\t(encrypt_start) - unknown "
16067c478bd9Sstevel@tonic-gate "crypto_method %d\n", encr_data.decrypt.type);
16077c478bd9Sstevel@tonic-gate
16087c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "unrecognized crypto decrypt method: %d",
16097c478bd9Sstevel@tonic-gate encr_data.decrypt.type);
16107c478bd9Sstevel@tonic-gate
16117c478bd9Sstevel@tonic-gate return;
16127c478bd9Sstevel@tonic-gate }
16137c478bd9Sstevel@tonic-gate
16147c478bd9Sstevel@tonic-gate /*
16157c478bd9Sstevel@tonic-gate * Don't overwrite previously configured key and ivec info
16167c478bd9Sstevel@tonic-gate */
16177c478bd9Sstevel@tonic-gate if (encr_data.decrypt.setup != cki.crypto_method) {
16187c478bd9Sstevel@tonic-gate (void) memcpy(cki.key, (void *)encr_data.decrypt.krbdes_key,
16197c478bd9Sstevel@tonic-gate DES_BLOCKSIZE);
16207c478bd9Sstevel@tonic-gate (void) memcpy(cki.ivec, (void *)encr_data.decrypt.ivec,
16217c478bd9Sstevel@tonic-gate DES_BLOCKSIZE);
16227c478bd9Sstevel@tonic-gate
16237c478bd9Sstevel@tonic-gate cki.keylen = DES_BLOCKSIZE;
16247c478bd9Sstevel@tonic-gate cki.iveclen = DES_BLOCKSIZE;
16257c478bd9Sstevel@tonic-gate cki.ivec_usage = IVEC_ONETIME;
16267c478bd9Sstevel@tonic-gate } else {
16277c478bd9Sstevel@tonic-gate cki.keylen = 0;
16287c478bd9Sstevel@tonic-gate cki.iveclen = 0;
16297c478bd9Sstevel@tonic-gate }
16307c478bd9Sstevel@tonic-gate cki.option_mask = 0;
16317c478bd9Sstevel@tonic-gate
16327c478bd9Sstevel@tonic-gate stop_stream(cryptmod_fd, CRYPT_DECRYPT);
16337c478bd9Sstevel@tonic-gate
16347c478bd9Sstevel@tonic-gate crioc.ic_cmd = CRYPTIOCSETUP;
16357c478bd9Sstevel@tonic-gate crioc.ic_timout = -1;
16367c478bd9Sstevel@tonic-gate crioc.ic_len = sizeof (struct cr_info_t);
16377c478bd9Sstevel@tonic-gate crioc.ic_dp = (char *)&cki;
16387c478bd9Sstevel@tonic-gate
16397c478bd9Sstevel@tonic-gate if (ioctl(cryptmod_fd, I_STR, &crioc)) {
16407c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "ioctl(CRYPTIOCSETUP) [encrypt_start] "
16417c478bd9Sstevel@tonic-gate "error: %m");
16427c478bd9Sstevel@tonic-gate } else {
16437c478bd9Sstevel@tonic-gate encr_data.decrypt.setup = cki.crypto_method;
16447c478bd9Sstevel@tonic-gate }
16457c478bd9Sstevel@tonic-gate if (enc_debug)
16467c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16477c478bd9Sstevel@tonic-gate "\t(encrypt_start) called CRYPTIOCSETUP for "
16487c478bd9Sstevel@tonic-gate "decrypt side\n");
16497c478bd9Sstevel@tonic-gate
16507c478bd9Sstevel@tonic-gate /*
16517c478bd9Sstevel@tonic-gate * Read any data stuck between the cryptmod and the application
16527c478bd9Sstevel@tonic-gate * so we can pass it back down to be properly decrypted after
16537c478bd9Sstevel@tonic-gate * this operation finishes.
16547c478bd9Sstevel@tonic-gate */
16557c478bd9Sstevel@tonic-gate if (ioctl(cryptmod_fd, I_NREAD, &bytes) < 0) {
16567c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "I_NREAD returned error %m");
16577c478bd9Sstevel@tonic-gate bytes = 0;
16587c478bd9Sstevel@tonic-gate }
16597c478bd9Sstevel@tonic-gate
16607c478bd9Sstevel@tonic-gate /*
16617c478bd9Sstevel@tonic-gate * Any data which was read AFTER the ENCRYPT START message
16627c478bd9Sstevel@tonic-gate * must be sent back down to be decrypted properly.
16637c478bd9Sstevel@tonic-gate *
16647c478bd9Sstevel@tonic-gate * 'ncc' is the number of bytes that have been read but
16657c478bd9Sstevel@tonic-gate * not yet processed by the telnet state machine.
16667c478bd9Sstevel@tonic-gate *
16677c478bd9Sstevel@tonic-gate * 'bytes' is the number of bytes waiting to be read from
16687c478bd9Sstevel@tonic-gate * the stream.
16697c478bd9Sstevel@tonic-gate *
16707c478bd9Sstevel@tonic-gate * If either one is a positive value, then those bytes
16717c478bd9Sstevel@tonic-gate * must be pulled up and sent back down to be decrypted.
16727c478bd9Sstevel@tonic-gate */
16737c478bd9Sstevel@tonic-gate if (ncc || bytes) {
16747c478bd9Sstevel@tonic-gate drainstream(bytes);
16757c478bd9Sstevel@tonic-gate if (enc_debug)
16767c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16777c478bd9Sstevel@tonic-gate "\t(encrypt_start) after drainstream, "
16787c478bd9Sstevel@tonic-gate "ncc=%d bytes = %d\n", ncc, bytes);
16797c478bd9Sstevel@tonic-gate bytes += ncc;
16807c478bd9Sstevel@tonic-gate dataptr = netip;
16817c478bd9Sstevel@tonic-gate }
16827c478bd9Sstevel@tonic-gate
16837c478bd9Sstevel@tonic-gate start_stream(cryptmod_fd, CRYPT_DECRYPT, bytes, dataptr);
16847c478bd9Sstevel@tonic-gate
16857c478bd9Sstevel@tonic-gate /*
16867c478bd9Sstevel@tonic-gate * The bytes putback into the stream are no longer
16877c478bd9Sstevel@tonic-gate * available to be read by the server, so adjust the
16887c478bd9Sstevel@tonic-gate * counter accordingly.
16897c478bd9Sstevel@tonic-gate */
16907c478bd9Sstevel@tonic-gate ncc = 0;
16917c478bd9Sstevel@tonic-gate netip = netibuf;
16927c478bd9Sstevel@tonic-gate (void) memset(netip, 0, netibufsize);
16937c478bd9Sstevel@tonic-gate
16947c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
16957c478bd9Sstevel@tonic-gate if (enc_debug) {
16967c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
16977c478bd9Sstevel@tonic-gate "\t(encrypt_start) Start DECRYPT using %s\n",
16987c478bd9Sstevel@tonic-gate ENCTYPE_NAME(encr_data.decrypt.type));
16997c478bd9Sstevel@tonic-gate }
17007c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
17017c478bd9Sstevel@tonic-gate }
17027c478bd9Sstevel@tonic-gate
17037c478bd9Sstevel@tonic-gate /*
17047c478bd9Sstevel@tonic-gate * encrypt_support
17057c478bd9Sstevel@tonic-gate *
17067c478bd9Sstevel@tonic-gate * Called when we recieve the TELOPT_ENCRYPT SUPPORT [ encr type list ]
17077c478bd9Sstevel@tonic-gate * message from a client.
17087c478bd9Sstevel@tonic-gate *
17097c478bd9Sstevel@tonic-gate * Choose an agreeable method (DES_CFB64) and
17107c478bd9Sstevel@tonic-gate * respond with TELOPT_ENCRYPT ENCRYPT_IS [ desired crypto method ]
17117c478bd9Sstevel@tonic-gate *
17127c478bd9Sstevel@tonic-gate * from: RFC 2946
17137c478bd9Sstevel@tonic-gate */
17147c478bd9Sstevel@tonic-gate static void
encrypt_support(char * data,int cnt)17157c478bd9Sstevel@tonic-gate encrypt_support(char *data, int cnt)
17167c478bd9Sstevel@tonic-gate {
17177c478bd9Sstevel@tonic-gate int lstate = ENCR_STATE_NOT_READY;
17187c478bd9Sstevel@tonic-gate int type, use_type = 0;
17197c478bd9Sstevel@tonic-gate
17207c478bd9Sstevel@tonic-gate while (cnt-- > 0 && use_type == 0) {
17217c478bd9Sstevel@tonic-gate type = *data++;
17227c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
17237c478bd9Sstevel@tonic-gate if (enc_debug)
17247c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
17257c478bd9Sstevel@tonic-gate "RCVD ENCRYPT SUPPORT %s\n",
17267c478bd9Sstevel@tonic-gate ENCTYPE_NAME(type));
17277c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
17287c478bd9Sstevel@tonic-gate /*
17297c478bd9Sstevel@tonic-gate * Prefer CFB64
17307c478bd9Sstevel@tonic-gate */
17317c478bd9Sstevel@tonic-gate if (type == TELOPT_ENCTYPE_DES_CFB64) {
17327c478bd9Sstevel@tonic-gate use_type = type;
17337c478bd9Sstevel@tonic-gate }
17347c478bd9Sstevel@tonic-gate }
17357c478bd9Sstevel@tonic-gate encr_data.encrypt.type = use_type;
17367c478bd9Sstevel@tonic-gate
17377c478bd9Sstevel@tonic-gate if (use_type != TELOPT_ENCTYPE_NULL &&
17387c478bd9Sstevel@tonic-gate authenticated != NULL && authenticated != &NoAuth &&
17397c478bd9Sstevel@tonic-gate auth_status != AUTH_REJECT) {
17407c478bd9Sstevel@tonic-gate
17417c478bd9Sstevel@tonic-gate /* Authenticated -> have session key -> send ENCRYPT IS */
17427c478bd9Sstevel@tonic-gate lstate = encrypt_send_encrypt_is();
17437c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_OK)
17447c478bd9Sstevel@tonic-gate encrypt_start_output();
17457c478bd9Sstevel@tonic-gate } else if (use_type == TELOPT_ENCTYPE_NULL) {
17467c478bd9Sstevel@tonic-gate if (enc_debug)
17477c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
17487c478bd9Sstevel@tonic-gate "\t(encrypt_support) Cannot agree "
17497c478bd9Sstevel@tonic-gate "on crypto algorithm, output encryption "
17507c478bd9Sstevel@tonic-gate "disabled.\n");
17517c478bd9Sstevel@tonic-gate
17527c478bd9Sstevel@tonic-gate /*
17537c478bd9Sstevel@tonic-gate * Cannot agree on crypto algorithm
17547c478bd9Sstevel@tonic-gate * RFC 2946 sez:
17557c478bd9Sstevel@tonic-gate * send "IAC SB ENCRYPT IS NULL IAC SE"
17567c478bd9Sstevel@tonic-gate * optionally, also send IAC WONT ENCRYPT
17577c478bd9Sstevel@tonic-gate */
17587c478bd9Sstevel@tonic-gate write_data("%c%c%c%c%c%c%c",
17597c478bd9Sstevel@tonic-gate (uchar_t)IAC,
17607c478bd9Sstevel@tonic-gate (uchar_t)SB,
17617c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
17627c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_IS,
17637c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCTYPE_NULL,
17647c478bd9Sstevel@tonic-gate (uchar_t)IAC,
17657c478bd9Sstevel@tonic-gate (uchar_t)SE);
17667c478bd9Sstevel@tonic-gate send_wont(TELOPT_ENCRYPT);
17677c478bd9Sstevel@tonic-gate netflush();
17687c478bd9Sstevel@tonic-gate if (enc_debug)
17697c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
17707c478bd9Sstevel@tonic-gate "SENT TELOPT_ENCRYPT ENCRYPT_IS "
17717c478bd9Sstevel@tonic-gate "[NULL]\n");
17727c478bd9Sstevel@tonic-gate
17737c478bd9Sstevel@tonic-gate remopts[TELOPT_ENCRYPT] = OPT_NO;
17747c478bd9Sstevel@tonic-gate }
17757c478bd9Sstevel@tonic-gate settimer(encr_support);
17767c478bd9Sstevel@tonic-gate }
17777c478bd9Sstevel@tonic-gate
17787c478bd9Sstevel@tonic-gate /*
17797c478bd9Sstevel@tonic-gate * encrypt_send_keyid
17807c478bd9Sstevel@tonic-gate *
17817c478bd9Sstevel@tonic-gate * Sent the key id we will use to the client
17827c478bd9Sstevel@tonic-gate */
17837c478bd9Sstevel@tonic-gate static void
encrypt_send_keyid(int dir,uchar_t * keyid,int keylen,boolean_t saveit)17847c478bd9Sstevel@tonic-gate encrypt_send_keyid(int dir, uchar_t *keyid, int keylen, boolean_t saveit)
17857c478bd9Sstevel@tonic-gate {
17867c478bd9Sstevel@tonic-gate uchar_t sbbuf[128], *p;
17877c478bd9Sstevel@tonic-gate
17887c478bd9Sstevel@tonic-gate p = sbbuf;
17897c478bd9Sstevel@tonic-gate
17907c478bd9Sstevel@tonic-gate *p++ = IAC;
17917c478bd9Sstevel@tonic-gate *p++ = SB;
17927c478bd9Sstevel@tonic-gate *p++ = TELOPT_ENCRYPT;
17937c478bd9Sstevel@tonic-gate *p++ = (dir == TELNET_DIR_ENCRYPT ? ENCRYPT_ENC_KEYID :
17947c478bd9Sstevel@tonic-gate ENCRYPT_DEC_KEYID);
17957c478bd9Sstevel@tonic-gate if (saveit) {
17967c478bd9Sstevel@tonic-gate if (enc_debug)
17977c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
17987c478bd9Sstevel@tonic-gate "\t(send_keyid) store %d byte %s keyid\n",
17997c478bd9Sstevel@tonic-gate keylen,
18007c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
18017c478bd9Sstevel@tonic-gate "DECRYPT"));
18027c478bd9Sstevel@tonic-gate
18037c478bd9Sstevel@tonic-gate if (dir == TELNET_DIR_ENCRYPT) {
18047c478bd9Sstevel@tonic-gate (void) memcpy(encr_data.encrypt.keyid, keyid, keylen);
18057c478bd9Sstevel@tonic-gate encr_data.encrypt.keyidlen = keylen;
18067c478bd9Sstevel@tonic-gate } else {
18077c478bd9Sstevel@tonic-gate (void) memcpy(encr_data.decrypt.keyid, keyid, keylen);
18087c478bd9Sstevel@tonic-gate encr_data.decrypt.keyidlen = keylen;
18097c478bd9Sstevel@tonic-gate }
18107c478bd9Sstevel@tonic-gate }
18117c478bd9Sstevel@tonic-gate (void) memcpy(p, keyid, keylen);
18127c478bd9Sstevel@tonic-gate p += keylen;
18137c478bd9Sstevel@tonic-gate
18147c478bd9Sstevel@tonic-gate *p++ = IAC;
18157c478bd9Sstevel@tonic-gate *p++ = SE;
18167c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, (size_t)(p-sbbuf));
18177c478bd9Sstevel@tonic-gate netflush();
18187c478bd9Sstevel@tonic-gate
18197c478bd9Sstevel@tonic-gate if (enc_debug)
18207c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT TELOPT_ENCRYPT %s %d\n",
18217c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENC_KEYID" :
18227c478bd9Sstevel@tonic-gate "DEC_KEYID"), keyid[0]);
18237c478bd9Sstevel@tonic-gate }
18247c478bd9Sstevel@tonic-gate
18257c478bd9Sstevel@tonic-gate /*
18267c478bd9Sstevel@tonic-gate * encrypt_reply
18277c478bd9Sstevel@tonic-gate *
18287c478bd9Sstevel@tonic-gate * When we receive the TELOPT_ENCRYPT REPLY [crtype] CFB64_IV_OK IAC SE
18297c478bd9Sstevel@tonic-gate * message, process it accordingly.
18307c478bd9Sstevel@tonic-gate * If the vector is acceptable, tell client we are encrypting and
18317c478bd9Sstevel@tonic-gate * enable encryption on our write stream.
18327c478bd9Sstevel@tonic-gate *
18337c478bd9Sstevel@tonic-gate * Negotiate the KEYID next..
18347c478bd9Sstevel@tonic-gate * RFC 2946, 2952
18357c478bd9Sstevel@tonic-gate */
18367c478bd9Sstevel@tonic-gate static void
encrypt_reply(char * data,int len)18377c478bd9Sstevel@tonic-gate encrypt_reply(char *data, int len)
18387c478bd9Sstevel@tonic-gate {
18397c478bd9Sstevel@tonic-gate uchar_t type = (uchar_t)(*data++);
18407c478bd9Sstevel@tonic-gate uchar_t result = (uchar_t)(*data);
18417c478bd9Sstevel@tonic-gate int lstate;
18427c478bd9Sstevel@tonic-gate
18437c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
18447c478bd9Sstevel@tonic-gate if (enc_debug)
18457c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18467c478bd9Sstevel@tonic-gate "\t(encrypt_reply) ENCRYPT REPLY %s %s [len=%d]\n",
18477c478bd9Sstevel@tonic-gate ENCRYPT_NAME(type),
18487c478bd9Sstevel@tonic-gate (result == CFB64_IV_OK ? "CFB64_IV_OK" :
18497c478bd9Sstevel@tonic-gate "CFB64_IV_BAD"), len);
18507c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
18517c478bd9Sstevel@tonic-gate
18527c478bd9Sstevel@tonic-gate lstate = encr_data.encrypt.state;
18537c478bd9Sstevel@tonic-gate if (enc_debug)
18547c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18557c478bd9Sstevel@tonic-gate "\t(encrypt_reply) initial ENCRYPT state = %d\n",
18567c478bd9Sstevel@tonic-gate lstate);
18577c478bd9Sstevel@tonic-gate switch (result) {
18587c478bd9Sstevel@tonic-gate case CFB64_IV_OK:
18597c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_NOT_READY)
18607c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_IN_PROGRESS;
18617c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_RECV_IV; /* we got the IV */
18627c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_SEND_IV; /* we dont need to send IV */
18637c478bd9Sstevel@tonic-gate
18647c478bd9Sstevel@tonic-gate /*
18657c478bd9Sstevel@tonic-gate * The correct response here is to send the encryption key id
18667c478bd9Sstevel@tonic-gate * RFC 2752.
18677c478bd9Sstevel@tonic-gate *
18687c478bd9Sstevel@tonic-gate * Send keyid 0 to indicate that we will just use default
18697c478bd9Sstevel@tonic-gate * keys.
18707c478bd9Sstevel@tonic-gate */
18717c478bd9Sstevel@tonic-gate encrypt_send_keyid(TELNET_DIR_ENCRYPT, (uchar_t *)"\0", 1, 1);
18727c478bd9Sstevel@tonic-gate
18737c478bd9Sstevel@tonic-gate break;
18747c478bd9Sstevel@tonic-gate case CFB64_IV_BAD:
18757c478bd9Sstevel@tonic-gate /*
18767c478bd9Sstevel@tonic-gate * Clear the ivec
18777c478bd9Sstevel@tonic-gate */
18787c478bd9Sstevel@tonic-gate (void) memset(encr_data.encrypt.ivec, 0, sizeof (Block));
18797c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_NOT_READY;
18807c478bd9Sstevel@tonic-gate break;
18817c478bd9Sstevel@tonic-gate default:
18827c478bd9Sstevel@tonic-gate if (enc_debug)
18837c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18847c478bd9Sstevel@tonic-gate "\t(encrypt_reply) Got unknown IV value in "
18857c478bd9Sstevel@tonic-gate "REPLY message\n");
18867c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_NOT_READY;
18877c478bd9Sstevel@tonic-gate break;
18887c478bd9Sstevel@tonic-gate }
18897c478bd9Sstevel@tonic-gate
18907c478bd9Sstevel@tonic-gate encr_data.encrypt.state = lstate;
18917c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_NOT_READY) {
18927c478bd9Sstevel@tonic-gate encr_data.encrypt.autoflag = 0;
18937c478bd9Sstevel@tonic-gate encr_data.encrypt.type = ENCTYPE_NULL;
18947c478bd9Sstevel@tonic-gate if (enc_debug)
18957c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
18967c478bd9Sstevel@tonic-gate "\t(encrypt_reply) encrypt.autoflag = "
18977c478bd9Sstevel@tonic-gate "OFF\n");
18987c478bd9Sstevel@tonic-gate } else {
18997c478bd9Sstevel@tonic-gate encr_data.encrypt.type = type;
19007c478bd9Sstevel@tonic-gate if ((lstate == ENCR_STATE_OK) && encr_data.encrypt.autoflag)
19017c478bd9Sstevel@tonic-gate encrypt_start_output();
19027c478bd9Sstevel@tonic-gate }
19037c478bd9Sstevel@tonic-gate
19047c478bd9Sstevel@tonic-gate if (enc_debug)
19057c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19067c478bd9Sstevel@tonic-gate "\t(encrypt_reply) ENCRYPT final state = %d\n",
19077c478bd9Sstevel@tonic-gate lstate);
19087c478bd9Sstevel@tonic-gate }
19097c478bd9Sstevel@tonic-gate
19107c478bd9Sstevel@tonic-gate static void
encrypt_set_keyid_state(uchar_t * keyid,int * keyidlen,int dir)19117c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(uchar_t *keyid, int *keyidlen, int dir)
19127c478bd9Sstevel@tonic-gate {
19137c478bd9Sstevel@tonic-gate int lstate;
19147c478bd9Sstevel@tonic-gate
19157c478bd9Sstevel@tonic-gate lstate = (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state :
19167c478bd9Sstevel@tonic-gate encr_data.decrypt.state);
19177c478bd9Sstevel@tonic-gate
19187c478bd9Sstevel@tonic-gate if (enc_debug)
19197c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19207c478bd9Sstevel@tonic-gate "\t(set_keyid_state) %s initial state = %d\n",
19217c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19227c478bd9Sstevel@tonic-gate "DECRYPT"), lstate);
19237c478bd9Sstevel@tonic-gate
19247c478bd9Sstevel@tonic-gate /*
19257c478bd9Sstevel@tonic-gate * Currently, we only support using the default keyid,
19267c478bd9Sstevel@tonic-gate * so it should be an error if the len > 1 or the keyid != 0.
19277c478bd9Sstevel@tonic-gate */
19287c478bd9Sstevel@tonic-gate if (*keyidlen != 1 || (*keyid != '\0')) {
19297c478bd9Sstevel@tonic-gate if (enc_debug)
19307c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19317c478bd9Sstevel@tonic-gate "\t(set_keyid_state) unexpected keyid: "
19327c478bd9Sstevel@tonic-gate "len=%d value=%d\n", *keyidlen, *keyid);
19337c478bd9Sstevel@tonic-gate *keyidlen = 0;
19347c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "rcvd unexpected keyid %d - only keyid of 0 "
19357c478bd9Sstevel@tonic-gate "is supported", *keyid);
19367c478bd9Sstevel@tonic-gate } else {
19377c478bd9Sstevel@tonic-gate /*
19387c478bd9Sstevel@tonic-gate * We move to the "IN_PROGRESS" state.
19397c478bd9Sstevel@tonic-gate */
19407c478bd9Sstevel@tonic-gate if (lstate == ENCR_STATE_NOT_READY)
19417c478bd9Sstevel@tonic-gate lstate = ENCR_STATE_IN_PROGRESS;
19427c478bd9Sstevel@tonic-gate /*
19437c478bd9Sstevel@tonic-gate * Clear the NO_KEYID bit because we now have a valid keyid
19447c478bd9Sstevel@tonic-gate */
19457c478bd9Sstevel@tonic-gate lstate &= ~ENCR_STATE_NO_KEYID;
19467c478bd9Sstevel@tonic-gate }
19477c478bd9Sstevel@tonic-gate
19487c478bd9Sstevel@tonic-gate if (enc_debug)
19497c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19507c478bd9Sstevel@tonic-gate "\t(set_keyid_state) %s final state = %d\n",
19517c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19527c478bd9Sstevel@tonic-gate "DECRYPT"), lstate);
19537c478bd9Sstevel@tonic-gate
19547c478bd9Sstevel@tonic-gate if (dir == TELNET_DIR_ENCRYPT)
19557c478bd9Sstevel@tonic-gate encr_data.encrypt.state = lstate;
19567c478bd9Sstevel@tonic-gate else
19577c478bd9Sstevel@tonic-gate encr_data.decrypt.state = lstate;
19587c478bd9Sstevel@tonic-gate }
19597c478bd9Sstevel@tonic-gate
19607c478bd9Sstevel@tonic-gate /*
19617c478bd9Sstevel@tonic-gate * encrypt_keyid
19627c478bd9Sstevel@tonic-gate *
19637c478bd9Sstevel@tonic-gate * Set the keyid value in the key_info structure.
19647c478bd9Sstevel@tonic-gate * if necessary send a response to the sender
19657c478bd9Sstevel@tonic-gate */
19667c478bd9Sstevel@tonic-gate static void
encrypt_keyid(uchar_t * newkeyid,int * keyidlen,uchar_t * keyid,int len,int dir)19677c478bd9Sstevel@tonic-gate encrypt_keyid(uchar_t *newkeyid, int *keyidlen, uchar_t *keyid,
19687c478bd9Sstevel@tonic-gate int len, int dir)
19697c478bd9Sstevel@tonic-gate {
19707c478bd9Sstevel@tonic-gate if (len > TELNET_MAXNUMKEYS) {
19717c478bd9Sstevel@tonic-gate if (enc_debug)
19727c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19737c478bd9Sstevel@tonic-gate "\t(keyid) keylen too big (%d)\n", len);
19747c478bd9Sstevel@tonic-gate return;
19757c478bd9Sstevel@tonic-gate }
19767c478bd9Sstevel@tonic-gate
19777c478bd9Sstevel@tonic-gate if (enc_debug) {
19787c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(keyid) set KEYID for %s len = %d\n",
19797c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
19807c478bd9Sstevel@tonic-gate "DECRYPT"), len);
19817c478bd9Sstevel@tonic-gate }
19827c478bd9Sstevel@tonic-gate
19837c478bd9Sstevel@tonic-gate if (len == 0) {
19847c478bd9Sstevel@tonic-gate if (*keyidlen == 0) {
19857c478bd9Sstevel@tonic-gate if (enc_debug)
19867c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19877c478bd9Sstevel@tonic-gate "\t(keyid) Got 0 length keyid - "
19887c478bd9Sstevel@tonic-gate "failure\n");
19897c478bd9Sstevel@tonic-gate return;
19907c478bd9Sstevel@tonic-gate }
19917c478bd9Sstevel@tonic-gate *keyidlen = 0;
19927c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(newkeyid, keyidlen, dir);
19937c478bd9Sstevel@tonic-gate
19947c478bd9Sstevel@tonic-gate } else if (len != *keyidlen || memcmp(keyid, newkeyid, len)) {
19957c478bd9Sstevel@tonic-gate if (enc_debug)
19967c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
19977c478bd9Sstevel@tonic-gate "\t(keyid) Setting new key (%d bytes)\n",
19987c478bd9Sstevel@tonic-gate len);
19997c478bd9Sstevel@tonic-gate
20007c478bd9Sstevel@tonic-gate *keyidlen = len;
20017c478bd9Sstevel@tonic-gate (void) memcpy(newkeyid, keyid, len);
20027c478bd9Sstevel@tonic-gate
20037c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(newkeyid, keyidlen, dir);
20047c478bd9Sstevel@tonic-gate } else {
20057c478bd9Sstevel@tonic-gate encrypt_set_keyid_state(newkeyid, keyidlen, dir);
20067c478bd9Sstevel@tonic-gate
20077c478bd9Sstevel@tonic-gate if (enc_debug)
20087c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
20097c478bd9Sstevel@tonic-gate "\t(keyid) %s Key already in place,"
20107c478bd9Sstevel@tonic-gate "state = %d autoflag=%d\n",
20117c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" : "DECRYPT"),
20127c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? encr_data.encrypt.state:
20137c478bd9Sstevel@tonic-gate encr_data.decrypt.state),
20147c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ?
20157c478bd9Sstevel@tonic-gate encr_data.encrypt.autoflag:
20167c478bd9Sstevel@tonic-gate encr_data.decrypt.autoflag));
20177c478bd9Sstevel@tonic-gate
20187c478bd9Sstevel@tonic-gate /* key already in place */
20197c478bd9Sstevel@tonic-gate if ((encr_data.encrypt.state == ENCR_STATE_OK) &&
20207c478bd9Sstevel@tonic-gate dir == TELNET_DIR_ENCRYPT && encr_data.encrypt.autoflag) {
20217c478bd9Sstevel@tonic-gate encrypt_start_output();
20227c478bd9Sstevel@tonic-gate }
20237c478bd9Sstevel@tonic-gate return;
20247c478bd9Sstevel@tonic-gate }
20257c478bd9Sstevel@tonic-gate
20267c478bd9Sstevel@tonic-gate if (enc_debug)
20277c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\t(keyid) %s final state = %d\n",
20287c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ? "ENCRYPT" :
20297c478bd9Sstevel@tonic-gate "DECRYPT"),
20307c478bd9Sstevel@tonic-gate (dir == TELNET_DIR_ENCRYPT ?
20317c478bd9Sstevel@tonic-gate encr_data.encrypt.state :
20327c478bd9Sstevel@tonic-gate encr_data.decrypt.state));
20337c478bd9Sstevel@tonic-gate
20347c478bd9Sstevel@tonic-gate encrypt_send_keyid(dir, newkeyid, *keyidlen, 0);
20357c478bd9Sstevel@tonic-gate }
20367c478bd9Sstevel@tonic-gate
20377c478bd9Sstevel@tonic-gate /*
20387c478bd9Sstevel@tonic-gate * encrypt_enc_keyid
20397c478bd9Sstevel@tonic-gate *
20407c478bd9Sstevel@tonic-gate * We received the ENC_KEYID message from a client indicating that
20417c478bd9Sstevel@tonic-gate * the client wishes to verify that the indicated keyid maps to a
20427c478bd9Sstevel@tonic-gate * valid key.
20437c478bd9Sstevel@tonic-gate */
20447c478bd9Sstevel@tonic-gate static void
encrypt_enc_keyid(char * data,int cnt)20457c478bd9Sstevel@tonic-gate encrypt_enc_keyid(char *data, int cnt)
20467c478bd9Sstevel@tonic-gate {
20477c478bd9Sstevel@tonic-gate /*
20487c478bd9Sstevel@tonic-gate * Verify the decrypt keyid is valid
20497c478bd9Sstevel@tonic-gate */
20507c478bd9Sstevel@tonic-gate encrypt_keyid(encr_data.decrypt.keyid, &encr_data.decrypt.keyidlen,
20517c478bd9Sstevel@tonic-gate (uchar_t *)data, cnt, TELNET_DIR_DECRYPT);
20527c478bd9Sstevel@tonic-gate }
20537c478bd9Sstevel@tonic-gate
20547c478bd9Sstevel@tonic-gate /*
20557c478bd9Sstevel@tonic-gate * encrypt_dec_keyid
20567c478bd9Sstevel@tonic-gate *
20577c478bd9Sstevel@tonic-gate * We received the DEC_KEYID message from a client indicating that
20587c478bd9Sstevel@tonic-gate * the client wants to verify that the indicated keyid maps to a valid key.
20597c478bd9Sstevel@tonic-gate */
20607c478bd9Sstevel@tonic-gate static void
encrypt_dec_keyid(char * data,int cnt)20617c478bd9Sstevel@tonic-gate encrypt_dec_keyid(char *data, int cnt)
20627c478bd9Sstevel@tonic-gate {
20637c478bd9Sstevel@tonic-gate encrypt_keyid(encr_data.encrypt.keyid, &encr_data.encrypt.keyidlen,
20647c478bd9Sstevel@tonic-gate (uchar_t *)data, cnt, TELNET_DIR_ENCRYPT);
20657c478bd9Sstevel@tonic-gate }
20667c478bd9Sstevel@tonic-gate
20677c478bd9Sstevel@tonic-gate /*
20687c478bd9Sstevel@tonic-gate * encrypt_session_key
20697c478bd9Sstevel@tonic-gate *
20707c478bd9Sstevel@tonic-gate * Store the session key in the encryption data record
20717c478bd9Sstevel@tonic-gate */
20727c478bd9Sstevel@tonic-gate static void
encrypt_session_key(Session_Key * key,cipher_info_t * cinfo)20737c478bd9Sstevel@tonic-gate encrypt_session_key(Session_Key *key, cipher_info_t *cinfo)
20747c478bd9Sstevel@tonic-gate {
20757c478bd9Sstevel@tonic-gate if (key == NULL || key->type != SK_DES) {
20767c478bd9Sstevel@tonic-gate if (enc_debug)
20777c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
20787c478bd9Sstevel@tonic-gate "\t(session_key) Cannot set krb5 "
20797c478bd9Sstevel@tonic-gate "session key (unknown type = %d)\n",
20807c478bd9Sstevel@tonic-gate key ? key->type : -1);
20817c478bd9Sstevel@tonic-gate }
20827c478bd9Sstevel@tonic-gate if (enc_debug)
20837c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
20847c478bd9Sstevel@tonic-gate "\t(session_key) Settting session key "
20857c478bd9Sstevel@tonic-gate "for server\n");
20867c478bd9Sstevel@tonic-gate
20877c478bd9Sstevel@tonic-gate /* store the key in the cipher info data struct */
20887c478bd9Sstevel@tonic-gate (void) memcpy(cinfo->krbdes_key, (void *)key->data, sizeof (Block));
20897c478bd9Sstevel@tonic-gate
20907c478bd9Sstevel@tonic-gate /*
20917c478bd9Sstevel@tonic-gate * Now look to see if we still need to send the key and start
20927c478bd9Sstevel@tonic-gate * encrypting.
20937c478bd9Sstevel@tonic-gate *
20947c478bd9Sstevel@tonic-gate * If so, go ahead an call it now that we have the key.
20957c478bd9Sstevel@tonic-gate */
20967c478bd9Sstevel@tonic-gate if (cinfo->need_start) {
20977c478bd9Sstevel@tonic-gate if (encrypt_send_encrypt_is() == ENCR_STATE_OK) {
20987c478bd9Sstevel@tonic-gate cinfo->need_start = 0;
20997c478bd9Sstevel@tonic-gate }
21007c478bd9Sstevel@tonic-gate }
21017c478bd9Sstevel@tonic-gate }
21027c478bd9Sstevel@tonic-gate
21037c478bd9Sstevel@tonic-gate /*
21047c478bd9Sstevel@tonic-gate * new_env
21057c478bd9Sstevel@tonic-gate *
21067c478bd9Sstevel@tonic-gate * Used to add an environment variable and value to the
21077c478bd9Sstevel@tonic-gate * linked list structure.
21087c478bd9Sstevel@tonic-gate */
21097c478bd9Sstevel@tonic-gate static int
new_env(const char * name,const char * value)21107c478bd9Sstevel@tonic-gate new_env(const char *name, const char *value)
21117c478bd9Sstevel@tonic-gate {
21127c478bd9Sstevel@tonic-gate struct envlist *env;
21137c478bd9Sstevel@tonic-gate
21147c478bd9Sstevel@tonic-gate env = malloc(sizeof (struct envlist));
21157c478bd9Sstevel@tonic-gate if (env == NULL)
21167c478bd9Sstevel@tonic-gate return (1);
21177c478bd9Sstevel@tonic-gate if ((env->name = strdup(name)) == NULL) {
21187c478bd9Sstevel@tonic-gate free(env);
21197c478bd9Sstevel@tonic-gate return (1);
21207c478bd9Sstevel@tonic-gate }
21217c478bd9Sstevel@tonic-gate if ((env->value = strdup(value)) == NULL) {
21227c478bd9Sstevel@tonic-gate free(env->name);
21237c478bd9Sstevel@tonic-gate free(env);
21247c478bd9Sstevel@tonic-gate return (1);
21257c478bd9Sstevel@tonic-gate }
21267c478bd9Sstevel@tonic-gate env->delete = 0;
21277c478bd9Sstevel@tonic-gate env->next = envlist_head;
21287c478bd9Sstevel@tonic-gate envlist_head = env;
21297c478bd9Sstevel@tonic-gate return (0);
21307c478bd9Sstevel@tonic-gate }
21317c478bd9Sstevel@tonic-gate
21327c478bd9Sstevel@tonic-gate /*
21337c478bd9Sstevel@tonic-gate * del_env
21347c478bd9Sstevel@tonic-gate *
21357c478bd9Sstevel@tonic-gate * Used to delete an environment variable from the linked list
21367c478bd9Sstevel@tonic-gate * structure. We just set a flag because we will delete the list
21377c478bd9Sstevel@tonic-gate * anyway before we exec login.
21387c478bd9Sstevel@tonic-gate */
21397c478bd9Sstevel@tonic-gate static int
del_env(const char * name)21407c478bd9Sstevel@tonic-gate del_env(const char *name)
21417c478bd9Sstevel@tonic-gate {
21427c478bd9Sstevel@tonic-gate struct envlist *env;
21437c478bd9Sstevel@tonic-gate
21447c478bd9Sstevel@tonic-gate for (env = envlist_head; env; env = env->next) {
21457c478bd9Sstevel@tonic-gate if (strcmp(env->name, name) == 0) {
21467c478bd9Sstevel@tonic-gate env->delete = 1;
21477c478bd9Sstevel@tonic-gate break;
21487c478bd9Sstevel@tonic-gate }
21497c478bd9Sstevel@tonic-gate }
21507c478bd9Sstevel@tonic-gate return (0);
21517c478bd9Sstevel@tonic-gate }
21527c478bd9Sstevel@tonic-gate
21537c478bd9Sstevel@tonic-gate static int
issock(int fd)21547c478bd9Sstevel@tonic-gate issock(int fd)
21557c478bd9Sstevel@tonic-gate {
21567c478bd9Sstevel@tonic-gate struct stat stats;
21577c478bd9Sstevel@tonic-gate
21587c478bd9Sstevel@tonic-gate if (fstat(fd, &stats) == -1)
21597c478bd9Sstevel@tonic-gate return (0);
21607c478bd9Sstevel@tonic-gate return (S_ISSOCK(stats.st_mode));
21617c478bd9Sstevel@tonic-gate }
21627c478bd9Sstevel@tonic-gate
21637c478bd9Sstevel@tonic-gate /*
21647c478bd9Sstevel@tonic-gate * audit_telnet_settid stores the terminal id while it is still
21657c478bd9Sstevel@tonic-gate * available. Subsequent calls to adt_load_hostname() return
21667c478bd9Sstevel@tonic-gate * the id which is stored here.
21677c478bd9Sstevel@tonic-gate */
21687c478bd9Sstevel@tonic-gate static int
audit_telnet_settid(int sock)21697c478bd9Sstevel@tonic-gate audit_telnet_settid(int sock) {
21707c478bd9Sstevel@tonic-gate adt_session_data_t *ah;
21717c478bd9Sstevel@tonic-gate adt_termid_t *termid;
21727c478bd9Sstevel@tonic-gate int rc;
21737c478bd9Sstevel@tonic-gate
21747c478bd9Sstevel@tonic-gate if ((rc = adt_start_session(&ah, NULL, 0)) == 0) {
21757c478bd9Sstevel@tonic-gate if ((rc = adt_load_termid(sock, &termid)) == 0) {
21767c478bd9Sstevel@tonic-gate if ((rc = adt_set_user(ah, ADT_NO_AUDIT,
21777c478bd9Sstevel@tonic-gate ADT_NO_AUDIT, 0, ADT_NO_AUDIT,
21787c478bd9Sstevel@tonic-gate termid, ADT_SETTID)) == 0)
21797c478bd9Sstevel@tonic-gate (void) adt_set_proc(ah);
21807c478bd9Sstevel@tonic-gate free(termid);
21817c478bd9Sstevel@tonic-gate }
21827c478bd9Sstevel@tonic-gate (void) adt_end_session(ah);
21837c478bd9Sstevel@tonic-gate }
21847c478bd9Sstevel@tonic-gate return (rc);
21857c478bd9Sstevel@tonic-gate }
21867c478bd9Sstevel@tonic-gate
21877c478bd9Sstevel@tonic-gate /* ARGSUSED */
21887c478bd9Sstevel@tonic-gate int
main(int argc,char * argv[])21897c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
21907c478bd9Sstevel@tonic-gate {
21917c478bd9Sstevel@tonic-gate struct sockaddr_storage from;
21927c478bd9Sstevel@tonic-gate int on = 1;
21937c478bd9Sstevel@tonic-gate socklen_t fromlen;
21947c478bd9Sstevel@tonic-gate int issocket;
21957c478bd9Sstevel@tonic-gate #if defined(DEBUG)
21967c478bd9Sstevel@tonic-gate ushort_t porttouse = 0;
21977c478bd9Sstevel@tonic-gate boolean_t standalone = 0;
21987c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
21997c478bd9Sstevel@tonic-gate extern char *optarg;
22007c478bd9Sstevel@tonic-gate char c;
22017c478bd9Sstevel@tonic-gate int tos = -1;
22027c478bd9Sstevel@tonic-gate
22037c478bd9Sstevel@tonic-gate while ((c = getopt(argc, argv, TELNETD_OPTS DEBUG_OPTS)) != -1) {
22047c478bd9Sstevel@tonic-gate switch (c) {
22057c478bd9Sstevel@tonic-gate #if defined(DEBUG)
22067c478bd9Sstevel@tonic-gate case 'p':
22077c478bd9Sstevel@tonic-gate /*
22087c478bd9Sstevel@tonic-gate * note: alternative port number only used in
22097c478bd9Sstevel@tonic-gate * standalone mode.
22107c478bd9Sstevel@tonic-gate */
22117c478bd9Sstevel@tonic-gate porttouse = atoi(optarg);
22127c478bd9Sstevel@tonic-gate standalone = 1;
22137c478bd9Sstevel@tonic-gate break;
22147c478bd9Sstevel@tonic-gate case 'e':
22157c478bd9Sstevel@tonic-gate enc_debug = 1;
22167c478bd9Sstevel@tonic-gate break;
22177c478bd9Sstevel@tonic-gate #endif /* DEBUG */
22187c478bd9Sstevel@tonic-gate case 'a':
22197c478bd9Sstevel@tonic-gate if (strcasecmp(optarg, "none") == 0) {
22207c478bd9Sstevel@tonic-gate auth_level = 0;
22217c478bd9Sstevel@tonic-gate } else if (strcasecmp(optarg, "user") == 0) {
22227c478bd9Sstevel@tonic-gate auth_level = AUTH_USER;
22237c478bd9Sstevel@tonic-gate } else if (strcasecmp(optarg, "valid") == 0) {
22247c478bd9Sstevel@tonic-gate auth_level = AUTH_VALID;
22257c478bd9Sstevel@tonic-gate } else if (strcasecmp(optarg, "off") == 0) {
22267c478bd9Sstevel@tonic-gate auth_level = -1;
22277c478bd9Sstevel@tonic-gate negotiate_auth_krb5 = 0;
22287c478bd9Sstevel@tonic-gate } else if (strcasecmp(optarg, "debug") == 0) {
22297c478bd9Sstevel@tonic-gate auth_debug = 1;
22307c478bd9Sstevel@tonic-gate } else {
22317c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
22327c478bd9Sstevel@tonic-gate "unknown authentication level specified "
22337c478bd9Sstevel@tonic-gate "with \'-a\' option (%s)", optarg);
22347c478bd9Sstevel@tonic-gate auth_level = AUTH_USER;
22357c478bd9Sstevel@tonic-gate }
22367c478bd9Sstevel@tonic-gate break;
22377c478bd9Sstevel@tonic-gate case 'X':
22387c478bd9Sstevel@tonic-gate /* disable authentication negotiation */
22397c478bd9Sstevel@tonic-gate negotiate_auth_krb5 = 0;
22407c478bd9Sstevel@tonic-gate break;
22417c478bd9Sstevel@tonic-gate case 'R':
22427c478bd9Sstevel@tonic-gate case 'M':
22437c478bd9Sstevel@tonic-gate if (optarg != NULL) {
22447c478bd9Sstevel@tonic-gate int ret = krb5_init();
22457c478bd9Sstevel@tonic-gate if (ret) {
22467c478bd9Sstevel@tonic-gate syslog(LOG_ERR,
22477c478bd9Sstevel@tonic-gate "Unable to use Kerberos V5 as "
22487c478bd9Sstevel@tonic-gate "requested, exiting");
22497c478bd9Sstevel@tonic-gate exit(1);
22507c478bd9Sstevel@tonic-gate }
2251*0b4fd3b1SSurya Prakki (void) krb5_set_default_realm(telnet_context,
2252*0b4fd3b1SSurya Prakki optarg);
22537c478bd9Sstevel@tonic-gate syslog(LOG_NOTICE,
22547c478bd9Sstevel@tonic-gate "using %s as default KRB5 realm", optarg);
22557c478bd9Sstevel@tonic-gate }
22567c478bd9Sstevel@tonic-gate break;
22577c478bd9Sstevel@tonic-gate case 'S':
22587c478bd9Sstevel@tonic-gate telnet_srvtab = (char *)strdup(optarg);
22597c478bd9Sstevel@tonic-gate break;
22607c478bd9Sstevel@tonic-gate case 'E': /* disable automatic encryption */
22617c478bd9Sstevel@tonic-gate negotiate_encrypt = B_FALSE;
22627c478bd9Sstevel@tonic-gate break;
22637c478bd9Sstevel@tonic-gate case 'U':
22647c478bd9Sstevel@tonic-gate resolve_hostname = 1;
22657c478bd9Sstevel@tonic-gate break;
22667c478bd9Sstevel@tonic-gate case 's':
22677c478bd9Sstevel@tonic-gate if (optarg == NULL || (tos = atoi(optarg)) < 0 ||
22687c478bd9Sstevel@tonic-gate tos > 255) {
22697c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "telnetd: illegal tos value: "
22707c478bd9Sstevel@tonic-gate "%s\n", optarg);
22717c478bd9Sstevel@tonic-gate } else {
22727c478bd9Sstevel@tonic-gate if (tos < 0)
22737c478bd9Sstevel@tonic-gate tos = 020;
22747c478bd9Sstevel@tonic-gate }
22757c478bd9Sstevel@tonic-gate break;
22767c478bd9Sstevel@tonic-gate case 'h':
22777c478bd9Sstevel@tonic-gate show_hostinfo = 0;
22787c478bd9Sstevel@tonic-gate break;
22797c478bd9Sstevel@tonic-gate default:
22807c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "telnetd: illegal cmd line option %c",
22817c478bd9Sstevel@tonic-gate c);
22827c478bd9Sstevel@tonic-gate break;
22837c478bd9Sstevel@tonic-gate }
22847c478bd9Sstevel@tonic-gate }
22857c478bd9Sstevel@tonic-gate
22867c478bd9Sstevel@tonic-gate netibufsize = BUFSIZ;
22877c478bd9Sstevel@tonic-gate if (!(netibuf = (char *)malloc(netibufsize)))
22887c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "netibuf malloc failed\n");
22897c478bd9Sstevel@tonic-gate (void) memset(netibuf, 0, netibufsize);
22907c478bd9Sstevel@tonic-gate netip = netibuf;
22917c478bd9Sstevel@tonic-gate
22927c478bd9Sstevel@tonic-gate #if defined(DEBUG)
22937c478bd9Sstevel@tonic-gate if (standalone) {
22947c478bd9Sstevel@tonic-gate int s, ns, foo;
22957c478bd9Sstevel@tonic-gate struct servent *sp;
22967c478bd9Sstevel@tonic-gate static struct sockaddr_in6 sin6 = { AF_INET6 };
22977c478bd9Sstevel@tonic-gate int option = 1;
22987c478bd9Sstevel@tonic-gate
22997c478bd9Sstevel@tonic-gate if (porttouse) {
23007c478bd9Sstevel@tonic-gate sin6.sin6_port = htons(porttouse);
23017c478bd9Sstevel@tonic-gate } else {
23027c478bd9Sstevel@tonic-gate sp = getservbyname("telnet", "tcp");
23037c478bd9Sstevel@tonic-gate if (sp == 0) {
23047c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
23057c478bd9Sstevel@tonic-gate "telnetd: tcp/telnet: "
23067c478bd9Sstevel@tonic-gate "unknown service\n");
23077c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23087c478bd9Sstevel@tonic-gate }
23097c478bd9Sstevel@tonic-gate sin6.sin6_port = sp->s_port;
23107c478bd9Sstevel@tonic-gate }
23117c478bd9Sstevel@tonic-gate
23127c478bd9Sstevel@tonic-gate s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
23137c478bd9Sstevel@tonic-gate if (s < 0) {
23147c478bd9Sstevel@tonic-gate perror("telnetd: socket");
23157c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23167c478bd9Sstevel@tonic-gate }
23177c478bd9Sstevel@tonic-gate if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&option,
23187c478bd9Sstevel@tonic-gate sizeof (option)) == -1)
23197c478bd9Sstevel@tonic-gate perror("setsockopt SO_REUSEADDR");
23207c478bd9Sstevel@tonic-gate if (bind(s, (struct sockaddr *)&sin6, sizeof (sin6)) < 0) {
23217c478bd9Sstevel@tonic-gate perror("bind");
23227c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23237c478bd9Sstevel@tonic-gate }
23247c478bd9Sstevel@tonic-gate if (listen(s, 32) < 0) {
23257c478bd9Sstevel@tonic-gate perror("listen");
23267c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23277c478bd9Sstevel@tonic-gate }
23287c478bd9Sstevel@tonic-gate
23297c478bd9Sstevel@tonic-gate /* automatically reap all child processes */
23307c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN);
23317c478bd9Sstevel@tonic-gate
23327c478bd9Sstevel@tonic-gate for (;;) {
23337c478bd9Sstevel@tonic-gate pid_t pid;
23347c478bd9Sstevel@tonic-gate
23357c478bd9Sstevel@tonic-gate foo = sizeof (sin6);
23367c478bd9Sstevel@tonic-gate ns = accept(s, (struct sockaddr *)&sin6, &foo);
23377c478bd9Sstevel@tonic-gate if (ns < 0) {
23387c478bd9Sstevel@tonic-gate perror("accept");
23397c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23407c478bd9Sstevel@tonic-gate }
23417c478bd9Sstevel@tonic-gate pid = fork();
23427c478bd9Sstevel@tonic-gate if (pid == -1) {
23437c478bd9Sstevel@tonic-gate perror("fork");
23447c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23457c478bd9Sstevel@tonic-gate }
23467c478bd9Sstevel@tonic-gate if (pid == 0) {
23477c478bd9Sstevel@tonic-gate (void) dup2(ns, 0);
23487c478bd9Sstevel@tonic-gate (void) close(s);
23497c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_DFL);
23507c478bd9Sstevel@tonic-gate break;
23517c478bd9Sstevel@tonic-gate }
23527c478bd9Sstevel@tonic-gate (void) close(ns);
23537c478bd9Sstevel@tonic-gate }
23547c478bd9Sstevel@tonic-gate }
23557c478bd9Sstevel@tonic-gate #endif /* defined(DEBUG) */
23567c478bd9Sstevel@tonic-gate
23577c478bd9Sstevel@tonic-gate openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
23587c478bd9Sstevel@tonic-gate
23597c478bd9Sstevel@tonic-gate issocket = issock(0);
23607c478bd9Sstevel@tonic-gate if (!issocket)
23617c478bd9Sstevel@tonic-gate fatal(0, "stdin is not a socket file descriptor");
23627c478bd9Sstevel@tonic-gate
23637c478bd9Sstevel@tonic-gate fromlen = (socklen_t)sizeof (from);
23647c478bd9Sstevel@tonic-gate (void) memset((char *)&from, 0, sizeof (from));
23657c478bd9Sstevel@tonic-gate if (getpeername(0, (struct sockaddr *)&from, &fromlen)
23667c478bd9Sstevel@tonic-gate < 0) {
23677c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", argv[0]);
23687c478bd9Sstevel@tonic-gate perror("getpeername");
23697c478bd9Sstevel@tonic-gate _exit(EXIT_FAILURE);
23707c478bd9Sstevel@tonic-gate }
23717c478bd9Sstevel@tonic-gate
23727c478bd9Sstevel@tonic-gate if (audit_telnet_settid(0)) { /* set terminal ID */
23737c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", argv[0]);
23747c478bd9Sstevel@tonic-gate perror("audit");
23757c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
23767c478bd9Sstevel@tonic-gate }
23777c478bd9Sstevel@tonic-gate
23787c478bd9Sstevel@tonic-gate if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (const char *)&on,
23797c478bd9Sstevel@tonic-gate sizeof (on)) < 0) {
23807c478bd9Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
23817c478bd9Sstevel@tonic-gate }
23827c478bd9Sstevel@tonic-gate
23837c478bd9Sstevel@tonic-gate /*
23847c478bd9Sstevel@tonic-gate * Set the TOS value
23857c478bd9Sstevel@tonic-gate */
23867c478bd9Sstevel@tonic-gate if (tos != -1 &&
23877c478bd9Sstevel@tonic-gate setsockopt(0, IPPROTO_IP, IP_TOS,
23887c478bd9Sstevel@tonic-gate (char *)&tos, sizeof (tos)) < 0 &&
23897c478bd9Sstevel@tonic-gate errno != ENOPROTOOPT) {
23907c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "setsockopt (IP_TOS %d): %m", tos);
23917c478bd9Sstevel@tonic-gate }
23927c478bd9Sstevel@tonic-gate
23937c478bd9Sstevel@tonic-gate if (setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on,
23947c478bd9Sstevel@tonic-gate sizeof (on)) < 0) {
23957c478bd9Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (SO_OOBINLINE): %m");
23967c478bd9Sstevel@tonic-gate }
23977c478bd9Sstevel@tonic-gate
23987c478bd9Sstevel@tonic-gate /* set the default PAM service name */
23997c478bd9Sstevel@tonic-gate (void) strcpy(pam_svc_name, "telnet");
24007c478bd9Sstevel@tonic-gate
24017c478bd9Sstevel@tonic-gate doit(0, &from);
24027c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS);
24037c478bd9Sstevel@tonic-gate }
24047c478bd9Sstevel@tonic-gate
24057c478bd9Sstevel@tonic-gate static char *terminaltype = 0;
24067c478bd9Sstevel@tonic-gate
24077c478bd9Sstevel@tonic-gate /*
24087c478bd9Sstevel@tonic-gate * ttloop
24097c478bd9Sstevel@tonic-gate *
24107c478bd9Sstevel@tonic-gate * A small subroutine to flush the network output buffer, get some data
24117c478bd9Sstevel@tonic-gate * from the network, and pass it through the telnet state machine. We
24127c478bd9Sstevel@tonic-gate * also flush the pty input buffer (by dropping its data) if it becomes
24137c478bd9Sstevel@tonic-gate * too full.
24147c478bd9Sstevel@tonic-gate */
24157c478bd9Sstevel@tonic-gate static void
ttloop(void)24167c478bd9Sstevel@tonic-gate ttloop(void)
24177c478bd9Sstevel@tonic-gate {
24187c478bd9Sstevel@tonic-gate if (nfrontp-nbackp) {
24197c478bd9Sstevel@tonic-gate netflush();
24207c478bd9Sstevel@tonic-gate }
24217c478bd9Sstevel@tonic-gate read_again:
24227c478bd9Sstevel@tonic-gate ncc = read(net, netibuf, netibufsize);
24237c478bd9Sstevel@tonic-gate if (ncc < 0) {
24247c478bd9Sstevel@tonic-gate if (errno == EINTR)
24257c478bd9Sstevel@tonic-gate goto read_again;
24267c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ttloop: read: %m");
24277c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
24287c478bd9Sstevel@tonic-gate } else if (ncc == 0) {
24297c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ttloop: peer closed connection\n");
24307c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
24317c478bd9Sstevel@tonic-gate }
24327c478bd9Sstevel@tonic-gate
24337c478bd9Sstevel@tonic-gate netip = netibuf;
24347c478bd9Sstevel@tonic-gate telrcv(); /* state machine */
24357c478bd9Sstevel@tonic-gate if (ncc > 0) {
24367c478bd9Sstevel@tonic-gate pfrontp = pbackp = ptyobuf;
24377c478bd9Sstevel@tonic-gate telrcv();
24387c478bd9Sstevel@tonic-gate }
24397c478bd9Sstevel@tonic-gate }
24407c478bd9Sstevel@tonic-gate
24417c478bd9Sstevel@tonic-gate static void
send_do(int option)24427c478bd9Sstevel@tonic-gate send_do(int option)
24437c478bd9Sstevel@tonic-gate {
24447c478bd9Sstevel@tonic-gate write_data("%c%c%c", (uchar_t)IAC, (uchar_t)DO, (uchar_t)option);
24457c478bd9Sstevel@tonic-gate }
24467c478bd9Sstevel@tonic-gate
24477c478bd9Sstevel@tonic-gate static void
send_will(int option)24487c478bd9Sstevel@tonic-gate send_will(int option)
24497c478bd9Sstevel@tonic-gate {
24507c478bd9Sstevel@tonic-gate write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WILL, (uchar_t)option);
24517c478bd9Sstevel@tonic-gate }
24527c478bd9Sstevel@tonic-gate
24537c478bd9Sstevel@tonic-gate static void
send_wont(int option)24547c478bd9Sstevel@tonic-gate send_wont(int option)
24557c478bd9Sstevel@tonic-gate {
24567c478bd9Sstevel@tonic-gate write_data("%c%c%c", (uchar_t)IAC, (uchar_t)WONT, (uchar_t)option);
24577c478bd9Sstevel@tonic-gate }
24587c478bd9Sstevel@tonic-gate
24597c478bd9Sstevel@tonic-gate
24607c478bd9Sstevel@tonic-gate /*
24617c478bd9Sstevel@tonic-gate * getauthtype
24627c478bd9Sstevel@tonic-gate *
24637c478bd9Sstevel@tonic-gate * Negotiate automatic authentication, is possible.
24647c478bd9Sstevel@tonic-gate */
24657c478bd9Sstevel@tonic-gate static int
getauthtype(char * username,int * len)24667c478bd9Sstevel@tonic-gate getauthtype(char *username, int *len)
24677c478bd9Sstevel@tonic-gate {
24687c478bd9Sstevel@tonic-gate int init_status = -1;
24697c478bd9Sstevel@tonic-gate
24707c478bd9Sstevel@tonic-gate init_status = krb5_init();
24717c478bd9Sstevel@tonic-gate
24727c478bd9Sstevel@tonic-gate if (auth_level == -1 || init_status != 0) {
24737c478bd9Sstevel@tonic-gate remopts[TELOPT_AUTHENTICATION] = OPT_NO;
24747c478bd9Sstevel@tonic-gate myopts[TELOPT_AUTHENTICATION] = OPT_NO;
24757c478bd9Sstevel@tonic-gate negotiate_auth_krb5 = B_FALSE;
24767c478bd9Sstevel@tonic-gate negotiate_encrypt = B_FALSE;
24777c478bd9Sstevel@tonic-gate return (AUTH_REJECT);
24787c478bd9Sstevel@tonic-gate }
24797c478bd9Sstevel@tonic-gate
24807c478bd9Sstevel@tonic-gate if (init_status == 0 && auth_level != -1) {
24817c478bd9Sstevel@tonic-gate if (negotiate_auth_krb5) {
24827c478bd9Sstevel@tonic-gate /*
24837c478bd9Sstevel@tonic-gate * Negotiate Authentication FIRST
24847c478bd9Sstevel@tonic-gate */
24857c478bd9Sstevel@tonic-gate send_do(TELOPT_AUTHENTICATION);
24867c478bd9Sstevel@tonic-gate remopts[TELOPT_AUTHENTICATION] =
24877c478bd9Sstevel@tonic-gate OPT_YES_BUT_ALWAYS_LOOK;
24887c478bd9Sstevel@tonic-gate }
24897c478bd9Sstevel@tonic-gate while (sequenceIs(authopt, getauth))
24907c478bd9Sstevel@tonic-gate ttloop();
24917c478bd9Sstevel@tonic-gate
24927c478bd9Sstevel@tonic-gate if (remopts[TELOPT_AUTHENTICATION] == OPT_YES) {
24937c478bd9Sstevel@tonic-gate /*
24947c478bd9Sstevel@tonic-gate * Request KRB5 Mutual authentication and if that fails,
24957c478bd9Sstevel@tonic-gate * KRB5 1-way client authentication
24967c478bd9Sstevel@tonic-gate */
24977c478bd9Sstevel@tonic-gate uchar_t sbbuf[MAXOPTLEN], *p;
24987c478bd9Sstevel@tonic-gate p = sbbuf;
24997c478bd9Sstevel@tonic-gate *p++ = (uchar_t)IAC;
25007c478bd9Sstevel@tonic-gate *p++ = (uchar_t)SB;
25017c478bd9Sstevel@tonic-gate *p++ = (uchar_t)TELOPT_AUTHENTICATION;
25027c478bd9Sstevel@tonic-gate *p++ = (uchar_t)TELQUAL_SEND;
25037c478bd9Sstevel@tonic-gate if (negotiate_auth_krb5) {
25047c478bd9Sstevel@tonic-gate *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25057c478bd9Sstevel@tonic-gate *p++ = (uchar_t)(AUTH_WHO_CLIENT |
25067c478bd9Sstevel@tonic-gate AUTH_HOW_MUTUAL |
25077c478bd9Sstevel@tonic-gate AUTH_ENCRYPT_ON);
25087c478bd9Sstevel@tonic-gate *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25097c478bd9Sstevel@tonic-gate *p++ = (uchar_t)(AUTH_WHO_CLIENT |
25107c478bd9Sstevel@tonic-gate AUTH_HOW_MUTUAL);
25117c478bd9Sstevel@tonic-gate *p++ = (uchar_t)AUTHTYPE_KERBEROS_V5;
25127c478bd9Sstevel@tonic-gate *p++ = (uchar_t)(AUTH_WHO_CLIENT|
25137c478bd9Sstevel@tonic-gate AUTH_HOW_ONE_WAY);
25147c478bd9Sstevel@tonic-gate } else {
25157c478bd9Sstevel@tonic-gate *p++ = (uchar_t)AUTHTYPE_NULL;
25167c478bd9Sstevel@tonic-gate }
25177c478bd9Sstevel@tonic-gate *p++ = (uchar_t)IAC;
25187c478bd9Sstevel@tonic-gate *p++ = (uchar_t)SE;
25197c478bd9Sstevel@tonic-gate
25207c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf,
25217c478bd9Sstevel@tonic-gate (size_t)(p - sbbuf));
25227c478bd9Sstevel@tonic-gate netflush();
25237c478bd9Sstevel@tonic-gate if (auth_debug)
25247c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
25257c478bd9Sstevel@tonic-gate "SENT TELOPT_AUTHENTICATION "
25267c478bd9Sstevel@tonic-gate "[data]\n");
25277c478bd9Sstevel@tonic-gate
25287c478bd9Sstevel@tonic-gate /* auth_wait returns the authentication level */
25297c478bd9Sstevel@tonic-gate /* status = auth_wait(username, len); */
25307c478bd9Sstevel@tonic-gate while (sequenceIs(authdone, getauth))
25317c478bd9Sstevel@tonic-gate ttloop();
25327c478bd9Sstevel@tonic-gate /*
25337c478bd9Sstevel@tonic-gate * Now check to see if the user is valid or not
25347c478bd9Sstevel@tonic-gate */
25357c478bd9Sstevel@tonic-gate if (authenticated == NULL || authenticated == &NoAuth)
25367c478bd9Sstevel@tonic-gate auth_status = AUTH_REJECT;
25377c478bd9Sstevel@tonic-gate else {
25387c478bd9Sstevel@tonic-gate /*
25397c478bd9Sstevel@tonic-gate * We cant be VALID until the user status is
25407c478bd9Sstevel@tonic-gate * checked.
25417c478bd9Sstevel@tonic-gate */
25427c478bd9Sstevel@tonic-gate if (auth_status == AUTH_VALID)
25437c478bd9Sstevel@tonic-gate auth_status = AUTH_USER;
25447c478bd9Sstevel@tonic-gate
25457c478bd9Sstevel@tonic-gate if (authenticated->AuthName ==
25467c478bd9Sstevel@tonic-gate AUTHTYPE_KERBEROS_V5)
25477c478bd9Sstevel@tonic-gate auth_status = krb5_user_status(
25487c478bd9Sstevel@tonic-gate username, *len, auth_status);
25497c478bd9Sstevel@tonic-gate }
25507c478bd9Sstevel@tonic-gate }
25517c478bd9Sstevel@tonic-gate }
25527c478bd9Sstevel@tonic-gate return (auth_status);
25537c478bd9Sstevel@tonic-gate }
25547c478bd9Sstevel@tonic-gate
25557c478bd9Sstevel@tonic-gate static void
getencrtype(void)25567c478bd9Sstevel@tonic-gate getencrtype(void)
25577c478bd9Sstevel@tonic-gate {
25587c478bd9Sstevel@tonic-gate if (krb5_privacy_allowed() && negotiate_encrypt) {
25597c478bd9Sstevel@tonic-gate if (myopts[TELOPT_ENCRYPT] != OPT_YES) {
25607c478bd9Sstevel@tonic-gate if (!sent_will_encrypt) {
25617c478bd9Sstevel@tonic-gate send_will(TELOPT_ENCRYPT);
25627c478bd9Sstevel@tonic-gate sent_will_encrypt = B_TRUE;
25637c478bd9Sstevel@tonic-gate }
25647c478bd9Sstevel@tonic-gate if (enc_debug)
25657c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT WILL ENCRYPT\n");
25667c478bd9Sstevel@tonic-gate }
25677c478bd9Sstevel@tonic-gate if (remopts[TELOPT_ENCRYPT] != OPT_YES) {
25687c478bd9Sstevel@tonic-gate if (!sent_do_encrypt) {
25697c478bd9Sstevel@tonic-gate send_do(TELOPT_ENCRYPT);
25707c478bd9Sstevel@tonic-gate sent_do_encrypt = B_TRUE;
25713ff1fd9dSethindra remopts[TELOPT_ENCRYPT] =
25723ff1fd9dSethindra OPT_YES_BUT_ALWAYS_LOOK;
25737c478bd9Sstevel@tonic-gate }
25747c478bd9Sstevel@tonic-gate if (enc_debug)
25757c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "SENT DO ENCRYPT\n");
25767c478bd9Sstevel@tonic-gate }
25777c478bd9Sstevel@tonic-gate myopts[TELOPT_ENCRYPT] = OPT_YES;
25787c478bd9Sstevel@tonic-gate
25797c478bd9Sstevel@tonic-gate while (sequenceIs(encropt, getencr))
25807c478bd9Sstevel@tonic-gate ttloop();
25817c478bd9Sstevel@tonic-gate
25827c478bd9Sstevel@tonic-gate if (auth_status != AUTH_REJECT &&
25837c478bd9Sstevel@tonic-gate remopts[TELOPT_ENCRYPT] == OPT_YES &&
25847c478bd9Sstevel@tonic-gate myopts[TELOPT_ENCRYPT] == OPT_YES) {
25857c478bd9Sstevel@tonic-gate
25867c478bd9Sstevel@tonic-gate if (sent_encrypt_support == B_FALSE) {
25877c478bd9Sstevel@tonic-gate write_data("%c%c%c%c%c%c%c",
25887c478bd9Sstevel@tonic-gate (uchar_t)IAC,
25897c478bd9Sstevel@tonic-gate (uchar_t)SB,
25907c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCRYPT,
25917c478bd9Sstevel@tonic-gate (uchar_t)ENCRYPT_SUPPORT,
25927c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_ENCTYPE_DES_CFB64,
25937c478bd9Sstevel@tonic-gate (uchar_t)IAC,
25947c478bd9Sstevel@tonic-gate (uchar_t)SE);
25957c478bd9Sstevel@tonic-gate
25967c478bd9Sstevel@tonic-gate netflush();
25977c478bd9Sstevel@tonic-gate }
25987c478bd9Sstevel@tonic-gate /*
25997c478bd9Sstevel@tonic-gate * Now wait for a response to these messages before
26007c478bd9Sstevel@tonic-gate * continuing...
26017c478bd9Sstevel@tonic-gate * Look for TELOPT_ENCRYPT suboptions
26027c478bd9Sstevel@tonic-gate */
26037c478bd9Sstevel@tonic-gate while (sequenceIs(encr_support, getencr))
26047c478bd9Sstevel@tonic-gate ttloop();
26057c478bd9Sstevel@tonic-gate }
26067c478bd9Sstevel@tonic-gate } else {
26077c478bd9Sstevel@tonic-gate /* Dont need responses to these, so dont wait for them */
26087c478bd9Sstevel@tonic-gate settimer(encropt);
26097c478bd9Sstevel@tonic-gate remopts[TELOPT_ENCRYPT] = OPT_NO;
26107c478bd9Sstevel@tonic-gate myopts[TELOPT_ENCRYPT] = OPT_NO;
26117c478bd9Sstevel@tonic-gate }
26127c478bd9Sstevel@tonic-gate
26137c478bd9Sstevel@tonic-gate }
26147c478bd9Sstevel@tonic-gate
26157c478bd9Sstevel@tonic-gate /*
26167c478bd9Sstevel@tonic-gate * getterminaltype
26177c478bd9Sstevel@tonic-gate *
26187c478bd9Sstevel@tonic-gate * Ask the other end to send along its terminal type.
26197c478bd9Sstevel@tonic-gate * Output is the variable terminaltype filled in.
26207c478bd9Sstevel@tonic-gate */
26217c478bd9Sstevel@tonic-gate static void
getterminaltype(void)26227c478bd9Sstevel@tonic-gate getterminaltype(void)
26237c478bd9Sstevel@tonic-gate {
26247c478bd9Sstevel@tonic-gate /*
26257c478bd9Sstevel@tonic-gate * The remote side may have already sent this info, so
26267c478bd9Sstevel@tonic-gate * dont ask for these options if the other side already
26277c478bd9Sstevel@tonic-gate * sent the information.
26287c478bd9Sstevel@tonic-gate */
26297c478bd9Sstevel@tonic-gate if (sequenceIs(ttypeopt, getterminal)) {
26307c478bd9Sstevel@tonic-gate send_do(TELOPT_TTYPE);
26317c478bd9Sstevel@tonic-gate remopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
26327c478bd9Sstevel@tonic-gate }
26337c478bd9Sstevel@tonic-gate
26347c478bd9Sstevel@tonic-gate if (sequenceIs(nawsopt, getterminal)) {
26357c478bd9Sstevel@tonic-gate send_do(TELOPT_NAWS);
26367c478bd9Sstevel@tonic-gate remopts[TELOPT_NAWS] = OPT_YES_BUT_ALWAYS_LOOK;
26377c478bd9Sstevel@tonic-gate }
26387c478bd9Sstevel@tonic-gate
26397c478bd9Sstevel@tonic-gate if (sequenceIs(xdisplocopt, getterminal)) {
26407c478bd9Sstevel@tonic-gate send_do(TELOPT_XDISPLOC);
26417c478bd9Sstevel@tonic-gate remopts[TELOPT_XDISPLOC] = OPT_YES_BUT_ALWAYS_LOOK;
26427c478bd9Sstevel@tonic-gate }
26437c478bd9Sstevel@tonic-gate
26447c478bd9Sstevel@tonic-gate if (sequenceIs(environopt, getterminal)) {
26457c478bd9Sstevel@tonic-gate send_do(TELOPT_NEW_ENVIRON);
26467c478bd9Sstevel@tonic-gate remopts[TELOPT_NEW_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
26477c478bd9Sstevel@tonic-gate }
26487c478bd9Sstevel@tonic-gate
26497c478bd9Sstevel@tonic-gate if (sequenceIs(oenvironopt, getterminal)) {
26507c478bd9Sstevel@tonic-gate send_do(TELOPT_OLD_ENVIRON);
26517c478bd9Sstevel@tonic-gate remopts[TELOPT_OLD_ENVIRON] = OPT_YES_BUT_ALWAYS_LOOK;
26527c478bd9Sstevel@tonic-gate }
26537c478bd9Sstevel@tonic-gate
26547c478bd9Sstevel@tonic-gate /* make sure encryption is started here */
26557c478bd9Sstevel@tonic-gate while (auth_status != AUTH_REJECT &&
26567c478bd9Sstevel@tonic-gate authenticated != &NoAuth && authenticated != NULL &&
26577c478bd9Sstevel@tonic-gate remopts[TELOPT_ENCRYPT] == OPT_YES &&
26587c478bd9Sstevel@tonic-gate encr_data.encrypt.autoflag &&
26597c478bd9Sstevel@tonic-gate encr_data.encrypt.state != ENCR_STATE_OK) {
26607c478bd9Sstevel@tonic-gate if (enc_debug)
26617c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "getterminaltype() forcing encrypt\n");
26627c478bd9Sstevel@tonic-gate ttloop();
26637c478bd9Sstevel@tonic-gate }
26647c478bd9Sstevel@tonic-gate
26657c478bd9Sstevel@tonic-gate if (enc_debug) {
26667c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "getterminaltype() encryption %sstarted\n",
26677c478bd9Sstevel@tonic-gate encr_data.encrypt.state == ENCR_STATE_OK ? "" : "not ");
26687c478bd9Sstevel@tonic-gate }
26697c478bd9Sstevel@tonic-gate
26707c478bd9Sstevel@tonic-gate while (sequenceIs(ttypeopt, getterminal) ||
26717c478bd9Sstevel@tonic-gate sequenceIs(nawsopt, getterminal) ||
26727c478bd9Sstevel@tonic-gate sequenceIs(xdisplocopt, getterminal) ||
26737c478bd9Sstevel@tonic-gate sequenceIs(environopt, getterminal) ||
26747c478bd9Sstevel@tonic-gate sequenceIs(oenvironopt, getterminal)) {
26757c478bd9Sstevel@tonic-gate ttloop();
26767c478bd9Sstevel@tonic-gate }
26777c478bd9Sstevel@tonic-gate
26787c478bd9Sstevel@tonic-gate
26797c478bd9Sstevel@tonic-gate if (remopts[TELOPT_TTYPE] == OPT_YES) {
26807c478bd9Sstevel@tonic-gate static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26817c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_TTYPE, (uchar_t)TELQUAL_SEND,
26827c478bd9Sstevel@tonic-gate (uchar_t)IAC, (uchar_t)SE };
26837c478bd9Sstevel@tonic-gate
26847c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, sizeof (sbbuf));
26857c478bd9Sstevel@tonic-gate }
26867c478bd9Sstevel@tonic-gate if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
26877c478bd9Sstevel@tonic-gate static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26887c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_XDISPLOC, (uchar_t)TELQUAL_SEND,
26897c478bd9Sstevel@tonic-gate (uchar_t)IAC, (uchar_t)SE };
26907c478bd9Sstevel@tonic-gate
26917c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, sizeof (sbbuf));
26927c478bd9Sstevel@tonic-gate }
26937c478bd9Sstevel@tonic-gate if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
26947c478bd9Sstevel@tonic-gate static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
26957c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_NEW_ENVIRON, (uchar_t)TELQUAL_SEND,
26967c478bd9Sstevel@tonic-gate (uchar_t)IAC, (uchar_t)SE };
26977c478bd9Sstevel@tonic-gate
26987c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, sizeof (sbbuf));
26997c478bd9Sstevel@tonic-gate }
27007c478bd9Sstevel@tonic-gate if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
27017c478bd9Sstevel@tonic-gate static uchar_t sbbuf[] = { (uchar_t)IAC, (uchar_t)SB,
27027c478bd9Sstevel@tonic-gate (uchar_t)TELOPT_OLD_ENVIRON, (uchar_t)TELQUAL_SEND,
27037c478bd9Sstevel@tonic-gate (uchar_t)IAC, (uchar_t)SE };
27047c478bd9Sstevel@tonic-gate
27057c478bd9Sstevel@tonic-gate write_data_len((const char *)sbbuf, sizeof (sbbuf));
27067c478bd9Sstevel@tonic-gate }
27077c478bd9Sstevel@tonic-gate
27087c478bd9Sstevel@tonic-gate if (remopts[TELOPT_TTYPE] == OPT_YES) {
27097c478bd9Sstevel@tonic-gate while (sequenceIs(ttypesubopt, getterminal)) {
27107c478bd9Sstevel@tonic-gate ttloop();
27117c478bd9Sstevel@tonic-gate }
27127c478bd9Sstevel@tonic-gate }
27137c478bd9Sstevel@tonic-gate if (remopts[TELOPT_XDISPLOC] == OPT_YES) {
27147c478bd9Sstevel@tonic-gate while (sequenceIs(xdisplocsubopt, getterminal)) {
27157c478bd9Sstevel@tonic-gate ttloop();
27167c478bd9Sstevel@tonic-gate }
27177c478bd9Sstevel@tonic-gate }
27187c478bd9Sstevel@tonic-gate if (remopts[TELOPT_NEW_ENVIRON] == OPT_YES) {
27197c478bd9Sstevel@tonic-gate while (sequenceIs(environsubopt, getterminal)) {
27207c478bd9Sstevel@tonic-gate ttloop();
27217c478bd9Sstevel@tonic-gate }
27227c478bd9Sstevel@tonic-gate }
27237c478bd9Sstevel@tonic-gate if (remopts[TELOPT_OLD_ENVIRON] == OPT_YES) {
27247c478bd9Sstevel@tonic-gate while (sequenceIs(oenvironsubopt, getterminal)) {
27257c478bd9Sstevel@tonic-gate ttloop();
27267c478bd9Sstevel@tonic-gate }
27277c478bd9Sstevel@tonic-gate }
27287c478bd9Sstevel@tonic-gate init_neg_done = 1;
27297c478bd9Sstevel@tonic-gate }
27307c478bd9Sstevel@tonic-gate
27317c478bd9Sstevel@tonic-gate pid_t pid;
27327c478bd9Sstevel@tonic-gate
27337c478bd9Sstevel@tonic-gate /*
27347c478bd9Sstevel@tonic-gate * Get a pty, scan input lines.
27357c478bd9Sstevel@tonic-gate */
27367c478bd9Sstevel@tonic-gate static void
doit(int f,struct sockaddr_storage * who)27377c478bd9Sstevel@tonic-gate doit(int f, struct sockaddr_storage *who)
27387c478bd9Sstevel@tonic-gate {
27397c478bd9Sstevel@tonic-gate char *host;
27407c478bd9Sstevel@tonic-gate char host_name[MAXHOSTNAMELEN];
27417c478bd9Sstevel@tonic-gate int p, t, tt;
27427c478bd9Sstevel@tonic-gate struct sgttyb b;
27437c478bd9Sstevel@tonic-gate int ptmfd; /* fd of logindmux connected to pty */
27447c478bd9Sstevel@tonic-gate int netfd; /* fd of logindmux connected to netf */
27457c478bd9Sstevel@tonic-gate struct stat buf;
27467c478bd9Sstevel@tonic-gate struct protocol_arg telnetp;
27477c478bd9Sstevel@tonic-gate struct strioctl telnetmod;
27487c478bd9Sstevel@tonic-gate struct envlist *env, *next;
27497c478bd9Sstevel@tonic-gate int nsize = 0;
27507c478bd9Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN];
27517c478bd9Sstevel@tonic-gate struct sockaddr_in *sin;
27527c478bd9Sstevel@tonic-gate struct sockaddr_in6 *sin6;
27537c478bd9Sstevel@tonic-gate socklen_t wholen;
27547c478bd9Sstevel@tonic-gate char username[MAXUSERNAMELEN];
27557c478bd9Sstevel@tonic-gate int len;
27567c478bd9Sstevel@tonic-gate uchar_t passthru;
27577c478bd9Sstevel@tonic-gate char *slavename;
27587c478bd9Sstevel@tonic-gate
27597c478bd9Sstevel@tonic-gate if ((p = open("/dev/ptmx", O_RDWR | O_NOCTTY)) == -1) {
27607c478bd9Sstevel@tonic-gate fatalperror(f, "open /dev/ptmx", errno);
27617c478bd9Sstevel@tonic-gate }
27627c478bd9Sstevel@tonic-gate if (grantpt(p) == -1)
27637c478bd9Sstevel@tonic-gate fatal(f, "could not grant slave pty");
27647c478bd9Sstevel@tonic-gate if (unlockpt(p) == -1)
27657c478bd9Sstevel@tonic-gate fatal(f, "could not unlock slave pty");
27667c478bd9Sstevel@tonic-gate if ((slavename = ptsname(p)) == NULL)
27677c478bd9Sstevel@tonic-gate fatal(f, "could not enable slave pty");
27687c478bd9Sstevel@tonic-gate (void) dup2(f, 0);
27697c478bd9Sstevel@tonic-gate if ((t = open(slavename, O_RDWR | O_NOCTTY)) == -1)
27707c478bd9Sstevel@tonic-gate fatal(f, "could not open slave pty");
27717c478bd9Sstevel@tonic-gate if (ioctl(t, I_PUSH, "ptem") == -1)
27727c478bd9Sstevel@tonic-gate fatalperror(f, "ioctl I_PUSH ptem", errno);
27737c478bd9Sstevel@tonic-gate if (ioctl(t, I_PUSH, "ldterm") == -1)
27747c478bd9Sstevel@tonic-gate fatalperror(f, "ioctl I_PUSH ldterm", errno);
27757c478bd9Sstevel@tonic-gate if (ioctl(t, I_PUSH, "ttcompat") == -1)
27767c478bd9Sstevel@tonic-gate fatalperror(f, "ioctl I_PUSH ttcompat", errno);
27777c478bd9Sstevel@tonic-gate
27787c478bd9Sstevel@tonic-gate line = slavename;
27797c478bd9Sstevel@tonic-gate
27807c478bd9Sstevel@tonic-gate pty = t;
27817c478bd9Sstevel@tonic-gate
27827c478bd9Sstevel@tonic-gate if (ioctl(t, TIOCGETP, &b) == -1)
27837c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl TIOCGETP pty t: %m\n");
27847c478bd9Sstevel@tonic-gate b.sg_flags = O_CRMOD|O_XTABS|O_ANYP;
27857c478bd9Sstevel@tonic-gate /* XXX - ispeed and ospeed must be non-zero */
27867c478bd9Sstevel@tonic-gate b.sg_ispeed = B38400;
27877c478bd9Sstevel@tonic-gate b.sg_ospeed = B38400;
27887c478bd9Sstevel@tonic-gate if (ioctl(t, TIOCSETN, &b) == -1)
27897c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl TIOCSETN pty t: %m\n");
27907c478bd9Sstevel@tonic-gate if (ioctl(pty, TIOCGETP, &b) == -1)
27917c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl TIOCGETP pty pty: %m\n");
27927c478bd9Sstevel@tonic-gate b.sg_flags &= ~O_ECHO;
27937c478bd9Sstevel@tonic-gate if (ioctl(pty, TIOCSETN, &b) == -1)
27947c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl TIOCSETN pty pty: %m\n");
27957c478bd9Sstevel@tonic-gate
27967c478bd9Sstevel@tonic-gate if (who->ss_family == AF_INET) {
27977c478bd9Sstevel@tonic-gate char *addrbuf = NULL;
27987c478bd9Sstevel@tonic-gate char *portbuf = NULL;
27997c478bd9Sstevel@tonic-gate
28007c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *)who;
28017c478bd9Sstevel@tonic-gate wholen = sizeof (struct sockaddr_in);
28027c478bd9Sstevel@tonic-gate
28037c478bd9Sstevel@tonic-gate addrbuf = (char *)malloc(wholen);
28047c478bd9Sstevel@tonic-gate if (addrbuf == NULL)
28057c478bd9Sstevel@tonic-gate fatal(f, "Cannot alloc memory for address info\n");
28067c478bd9Sstevel@tonic-gate portbuf = (char *)malloc(sizeof (sin->sin_port));
28077c478bd9Sstevel@tonic-gate if (portbuf == NULL) {
28087c478bd9Sstevel@tonic-gate free(addrbuf);
28097c478bd9Sstevel@tonic-gate fatal(f, "Cannot alloc memory for port info\n");
28107c478bd9Sstevel@tonic-gate }
28117c478bd9Sstevel@tonic-gate
28127c478bd9Sstevel@tonic-gate (void) memcpy(addrbuf, (const void *)&sin->sin_addr, wholen);
28137c478bd9Sstevel@tonic-gate (void) memcpy(portbuf, (const void *)&sin->sin_port,
28147c478bd9Sstevel@tonic-gate sizeof (sin->sin_port));
28157c478bd9Sstevel@tonic-gate
28167c478bd9Sstevel@tonic-gate if (rsaddr.contents != NULL)
28177c478bd9Sstevel@tonic-gate free(rsaddr.contents);
28187c478bd9Sstevel@tonic-gate
28197c478bd9Sstevel@tonic-gate rsaddr.contents = (krb5_octet *)addrbuf;
28207c478bd9Sstevel@tonic-gate rsaddr.length = wholen;
28217c478bd9Sstevel@tonic-gate rsaddr.addrtype = ADDRTYPE_INET;
28227c478bd9Sstevel@tonic-gate
28237c478bd9Sstevel@tonic-gate if (rsport.contents != NULL)
28247c478bd9Sstevel@tonic-gate free(rsport.contents);
28257c478bd9Sstevel@tonic-gate
28267c478bd9Sstevel@tonic-gate rsport.contents = (krb5_octet *)portbuf;
28277c478bd9Sstevel@tonic-gate rsport.length = sizeof (sin->sin_port);
28287c478bd9Sstevel@tonic-gate rsport.addrtype = ADDRTYPE_IPPORT;
28297c478bd9Sstevel@tonic-gate } else if (who->ss_family == AF_INET6) {
28307c478bd9Sstevel@tonic-gate struct in_addr ipv4_addr;
28317c478bd9Sstevel@tonic-gate char *addrbuf = NULL;
28327c478bd9Sstevel@tonic-gate char *portbuf = NULL;
28337c478bd9Sstevel@tonic-gate
28347c478bd9Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)who;
28357c478bd9Sstevel@tonic-gate wholen = sizeof (struct sockaddr_in6);
28367c478bd9Sstevel@tonic-gate
28377c478bd9Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
28387c478bd9Sstevel@tonic-gate &ipv4_addr);
28397c478bd9Sstevel@tonic-gate
28407c478bd9Sstevel@tonic-gate addrbuf = (char *)malloc(wholen);
28417c478bd9Sstevel@tonic-gate if (addrbuf == NULL)
28427c478bd9Sstevel@tonic-gate fatal(f, "Cannot alloc memory for address info\n");
28437c478bd9Sstevel@tonic-gate
28447c478bd9Sstevel@tonic-gate portbuf = (char *)malloc(sizeof (sin6->sin6_port));
28457c478bd9Sstevel@tonic-gate if (portbuf == NULL) {
28467c478bd9Sstevel@tonic-gate free(addrbuf);
28477c478bd9Sstevel@tonic-gate fatal(f, "Cannot alloc memory for port info\n");
28487c478bd9Sstevel@tonic-gate }
28497c478bd9Sstevel@tonic-gate
28507c478bd9Sstevel@tonic-gate (void) memcpy((void *) addrbuf,
28517c478bd9Sstevel@tonic-gate (const void *)&ipv4_addr,
28527c478bd9Sstevel@tonic-gate wholen);
28537c478bd9Sstevel@tonic-gate /*
28547c478bd9Sstevel@tonic-gate * If we already used rsaddr.contents, free the previous
28557c478bd9Sstevel@tonic-gate * buffer.
28567c478bd9Sstevel@tonic-gate */
28577c478bd9Sstevel@tonic-gate if (rsaddr.contents != NULL)
28587c478bd9Sstevel@tonic-gate free(rsaddr.contents);
28597c478bd9Sstevel@tonic-gate
28607c478bd9Sstevel@tonic-gate rsaddr.contents = (krb5_octet *)addrbuf;
28617c478bd9Sstevel@tonic-gate rsaddr.length = sizeof (ipv4_addr);
28627c478bd9Sstevel@tonic-gate rsaddr.addrtype = ADDRTYPE_INET;
28637c478bd9Sstevel@tonic-gate
28647c478bd9Sstevel@tonic-gate (void) memcpy((void *) portbuf, (const void *)&sin6->sin6_port,
28657c478bd9Sstevel@tonic-gate sizeof (sin6->sin6_port));
28667c478bd9Sstevel@tonic-gate
28677c478bd9Sstevel@tonic-gate if (rsport.contents != NULL)
28687c478bd9Sstevel@tonic-gate free(rsport.contents);
28697c478bd9Sstevel@tonic-gate
28707c478bd9Sstevel@tonic-gate rsport.contents = (krb5_octet *)portbuf;
28717c478bd9Sstevel@tonic-gate rsport.length = sizeof (sin6->sin6_port);
28727c478bd9Sstevel@tonic-gate rsport.addrtype = ADDRTYPE_IPPORT;
28737c478bd9Sstevel@tonic-gate } else {
28747c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "unknown address family %d\n",
28757c478bd9Sstevel@tonic-gate who->ss_family);
28767c478bd9Sstevel@tonic-gate fatal(f, "getpeername: unknown address family\n");
28777c478bd9Sstevel@tonic-gate }
28787c478bd9Sstevel@tonic-gate
28797c478bd9Sstevel@tonic-gate if (getnameinfo((const struct sockaddr *) who, wholen, host_name,
28807c478bd9Sstevel@tonic-gate sizeof (host_name), NULL, 0, 0) == 0) {
28817c478bd9Sstevel@tonic-gate host = host_name;
28827c478bd9Sstevel@tonic-gate } else {
28837c478bd9Sstevel@tonic-gate /*
28847c478bd9Sstevel@tonic-gate * If the '-U' option was given on the cmd line, we must
28857c478bd9Sstevel@tonic-gate * be able to lookup the hostname
28867c478bd9Sstevel@tonic-gate */
28877c478bd9Sstevel@tonic-gate if (resolve_hostname) {
28887c478bd9Sstevel@tonic-gate fatal(f, "Couldn't resolve your address into a "
28897c478bd9Sstevel@tonic-gate "host name.\r\nPlease contact your net "
28907c478bd9Sstevel@tonic-gate "administrator");
28917c478bd9Sstevel@tonic-gate }
28927c478bd9Sstevel@tonic-gate
28937c478bd9Sstevel@tonic-gate if (who->ss_family == AF_INET6) {
28947c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
28957c478bd9Sstevel@tonic-gate struct in_addr ipv4_addr;
28967c478bd9Sstevel@tonic-gate
28977c478bd9Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
28987c478bd9Sstevel@tonic-gate &ipv4_addr);
28997c478bd9Sstevel@tonic-gate host = (char *)inet_ntop(AF_INET,
29007c478bd9Sstevel@tonic-gate &ipv4_addr, abuf, sizeof (abuf));
29017c478bd9Sstevel@tonic-gate } else {
29027c478bd9Sstevel@tonic-gate host = (char *)inet_ntop(AF_INET6,
29037c478bd9Sstevel@tonic-gate &sin6->sin6_addr, abuf,
29047c478bd9Sstevel@tonic-gate sizeof (abuf));
29057c478bd9Sstevel@tonic-gate }
29067c478bd9Sstevel@tonic-gate } else if (who->ss_family == AF_INET) {
29077c478bd9Sstevel@tonic-gate host = (char *)inet_ntop(AF_INET,
29087c478bd9Sstevel@tonic-gate &sin->sin_addr, abuf, sizeof (abuf));
29097c478bd9Sstevel@tonic-gate }
29107c478bd9Sstevel@tonic-gate }
29117c478bd9Sstevel@tonic-gate /*
29127c478bd9Sstevel@tonic-gate * Note that sockmod has to be removed since readstream assumes
29137c478bd9Sstevel@tonic-gate * a "raw" TPI endpoint (e.g. it uses getmsg).
29147c478bd9Sstevel@tonic-gate */
29157c478bd9Sstevel@tonic-gate if (removemod(f, "sockmod") < 0)
29167c478bd9Sstevel@tonic-gate fatalperror(f, "couldn't remove sockmod", errno);
29177c478bd9Sstevel@tonic-gate
29187c478bd9Sstevel@tonic-gate encrypt_init();
29197c478bd9Sstevel@tonic-gate
29207c478bd9Sstevel@tonic-gate /*
29217c478bd9Sstevel@tonic-gate * Push the crypto module on the stream before 'telmod' so it
29227c478bd9Sstevel@tonic-gate * can encrypt/decrypt without interfering with telmod functionality
29237c478bd9Sstevel@tonic-gate * We must push it now because many of the crypto options negotiated
29247c478bd9Sstevel@tonic-gate * initially must be saved in the crypto module (via IOCTL calls).
29257c478bd9Sstevel@tonic-gate */
29267c478bd9Sstevel@tonic-gate if (ioctl(f, I_PUSH, "cryptmod") < 0)
29277c478bd9Sstevel@tonic-gate fatalperror(f, "ioctl I_PUSH cryptmod", errno);
29287c478bd9Sstevel@tonic-gate
29297c478bd9Sstevel@tonic-gate cryptmod_fd = f;
29307c478bd9Sstevel@tonic-gate /*
29317c478bd9Sstevel@tonic-gate * gotta set the encryption clock now because it is often negotiated
29327c478bd9Sstevel@tonic-gate * immediately by the client, and if we wait till after we negotiate
29337c478bd9Sstevel@tonic-gate * auth, it will be out of whack with when the WILL/WONT ENCRYPT
29347c478bd9Sstevel@tonic-gate * option is received.
29357c478bd9Sstevel@tonic-gate */
29367c478bd9Sstevel@tonic-gate settimer(getencr);
29377c478bd9Sstevel@tonic-gate
29387c478bd9Sstevel@tonic-gate /*
29397c478bd9Sstevel@tonic-gate * get terminal type.
29407c478bd9Sstevel@tonic-gate */
29417c478bd9Sstevel@tonic-gate username[0] = '\0';
29427c478bd9Sstevel@tonic-gate len = sizeof (username);
29437c478bd9Sstevel@tonic-gate
29447c478bd9Sstevel@tonic-gate settimer(getterminal);
29457c478bd9Sstevel@tonic-gate settimer(getauth);
29467c478bd9Sstevel@tonic-gate /*
29477c478bd9Sstevel@tonic-gate * Exchange TELOPT_AUTHENTICATE options per RFC 2941/2942
29487c478bd9Sstevel@tonic-gate */
29497c478bd9Sstevel@tonic-gate auth_status = getauthtype(username, &len);
29507c478bd9Sstevel@tonic-gate /*
29517c478bd9Sstevel@tonic-gate * Exchange TELOPT_ENCRYPT options per RFC 2946
29527c478bd9Sstevel@tonic-gate */
29537c478bd9Sstevel@tonic-gate getencrtype();
29547c478bd9Sstevel@tonic-gate getterminaltype();
29557c478bd9Sstevel@tonic-gate
29567c478bd9Sstevel@tonic-gate if (ioctl(f, I_PUSH, "telmod") < 0)
29577c478bd9Sstevel@tonic-gate fatalperror(f, "ioctl I_PUSH telmod", errno);
29587c478bd9Sstevel@tonic-gate
29597c478bd9Sstevel@tonic-gate /*
29607c478bd9Sstevel@tonic-gate * Make sure telmod will pass unrecognized IOCTLs to cryptmod
29617c478bd9Sstevel@tonic-gate */
29627c478bd9Sstevel@tonic-gate passthru = 1;
29637c478bd9Sstevel@tonic-gate
29647c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = CRYPTPASSTHRU;
29657c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
29667c478bd9Sstevel@tonic-gate telnetmod.ic_len = sizeof (uchar_t);
29677c478bd9Sstevel@tonic-gate telnetmod.ic_dp = (char *)&passthru;
29687c478bd9Sstevel@tonic-gate
29697c478bd9Sstevel@tonic-gate if (ioctl(f, I_STR, &telnetmod) < 0)
29707c478bd9Sstevel@tonic-gate fatal(f, "ioctl CRPASSTHRU failed\n");
29717c478bd9Sstevel@tonic-gate
29727c478bd9Sstevel@tonic-gate if (!ncc)
29737c478bd9Sstevel@tonic-gate netip = netibuf;
29747c478bd9Sstevel@tonic-gate
29757c478bd9Sstevel@tonic-gate /*
29767c478bd9Sstevel@tonic-gate * readstream will do a getmsg till it receives M_PROTO type
29777c478bd9Sstevel@tonic-gate * T_DATA_REQ from telnetmodopen(). This signals that all data
29787c478bd9Sstevel@tonic-gate * in-flight before telmod was pushed has been received at the
29797c478bd9Sstevel@tonic-gate * stream head.
29807c478bd9Sstevel@tonic-gate */
29817c478bd9Sstevel@tonic-gate while ((nsize = readstream(f, netibuf, ncc + netip - netibuf)) > 0) {
29827c478bd9Sstevel@tonic-gate ncc += nsize;
29837c478bd9Sstevel@tonic-gate }
29847c478bd9Sstevel@tonic-gate
29857c478bd9Sstevel@tonic-gate if (nsize < 0) {
29867c478bd9Sstevel@tonic-gate fatalperror(f, "readstream failed\n", errno);
29877c478bd9Sstevel@tonic-gate }
29887c478bd9Sstevel@tonic-gate
29897c478bd9Sstevel@tonic-gate /*
29907c478bd9Sstevel@tonic-gate * open logindmux drivers and link them with network and ptm
29917c478bd9Sstevel@tonic-gate * file descriptors.
29927c478bd9Sstevel@tonic-gate */
29937c478bd9Sstevel@tonic-gate if ((ptmfd = open("/dev/logindmux", O_RDWR)) == -1) {
29947c478bd9Sstevel@tonic-gate fatalperror(f, "open /dev/logindmux", errno);
29957c478bd9Sstevel@tonic-gate }
29967c478bd9Sstevel@tonic-gate if ((netfd = open("/dev/logindmux", O_RDWR)) == -1) {
29977c478bd9Sstevel@tonic-gate fatalperror(f, "open /dev/logindmux", errno);
29987c478bd9Sstevel@tonic-gate }
29997c478bd9Sstevel@tonic-gate
30007c478bd9Sstevel@tonic-gate if (ioctl(ptmfd, I_LINK, p) < 0)
30017c478bd9Sstevel@tonic-gate fatal(f, "ioctl I_LINK of /dev/ptmx failed\n");
30027c478bd9Sstevel@tonic-gate if (ioctl(netfd, I_LINK, f) < 0)
30037c478bd9Sstevel@tonic-gate fatal(f, "ioctl I_LINK of tcp connection failed\n");
30047c478bd9Sstevel@tonic-gate
30057c478bd9Sstevel@tonic-gate /*
30067c478bd9Sstevel@tonic-gate * Figure out the device number of ptm's mux fd, and pass that
30077c478bd9Sstevel@tonic-gate * to the net's mux.
30087c478bd9Sstevel@tonic-gate */
30097c478bd9Sstevel@tonic-gate if (fstat(ptmfd, &buf) < 0) {
30107c478bd9Sstevel@tonic-gate fatalperror(f, "fstat ptmfd failed", errno);
30117c478bd9Sstevel@tonic-gate }
30127c478bd9Sstevel@tonic-gate telnetp.dev = buf.st_rdev;
30137c478bd9Sstevel@tonic-gate telnetp.flag = 0;
30147c478bd9Sstevel@tonic-gate
30157c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
30167c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
30177c478bd9Sstevel@tonic-gate telnetmod.ic_len = sizeof (struct protocol_arg);
30187c478bd9Sstevel@tonic-gate telnetmod.ic_dp = (char *)&telnetp;
30197c478bd9Sstevel@tonic-gate
30207c478bd9Sstevel@tonic-gate if (ioctl(netfd, I_STR, &telnetmod) < 0)
30217c478bd9Sstevel@tonic-gate fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of netfd failed\n");
30227c478bd9Sstevel@tonic-gate
30237c478bd9Sstevel@tonic-gate /*
30247c478bd9Sstevel@tonic-gate * Figure out the device number of the net's mux fd, and pass that
30257c478bd9Sstevel@tonic-gate * to the ptm's mux.
30267c478bd9Sstevel@tonic-gate */
30277c478bd9Sstevel@tonic-gate if (fstat(netfd, &buf) < 0) {
30287c478bd9Sstevel@tonic-gate fatalperror(f, "fstat netfd failed", errno);
30297c478bd9Sstevel@tonic-gate }
30307c478bd9Sstevel@tonic-gate telnetp.dev = buf.st_rdev;
30317c478bd9Sstevel@tonic-gate telnetp.flag = 1;
30327c478bd9Sstevel@tonic-gate
30337c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = LOGDMX_IOC_QEXCHANGE;
30347c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
30357c478bd9Sstevel@tonic-gate telnetmod.ic_len = sizeof (struct protocol_arg);
30367c478bd9Sstevel@tonic-gate telnetmod.ic_dp = (char *)&telnetp;
30377c478bd9Sstevel@tonic-gate
30387c478bd9Sstevel@tonic-gate if (ioctl(ptmfd, I_STR, &telnetmod) < 0)
30397c478bd9Sstevel@tonic-gate fatal(netfd, "ioctl LOGDMX_IOC_QEXCHANGE of ptmfd failed\n");
30407c478bd9Sstevel@tonic-gate
30417c478bd9Sstevel@tonic-gate net = netfd;
30427c478bd9Sstevel@tonic-gate master = ptmfd;
30437c478bd9Sstevel@tonic-gate cryptmod_fd = netfd;
30447c478bd9Sstevel@tonic-gate
30457c478bd9Sstevel@tonic-gate /*
30467c478bd9Sstevel@tonic-gate * Show banner that getty never gave, but
30477c478bd9Sstevel@tonic-gate * only if the user did not automatically authenticate.
30487c478bd9Sstevel@tonic-gate */
30497c478bd9Sstevel@tonic-gate if (getenv("USER") == '\0' && auth_status < AUTH_USER)
30507c478bd9Sstevel@tonic-gate showbanner();
30517c478bd9Sstevel@tonic-gate
30527c478bd9Sstevel@tonic-gate /*
30537c478bd9Sstevel@tonic-gate * If the user automatically authenticated with Kerberos
30547c478bd9Sstevel@tonic-gate * we must set the service name that PAM will use. We
30557c478bd9Sstevel@tonic-gate * need to do it BEFORE the child fork so that 'cleanup'
30567c478bd9Sstevel@tonic-gate * in the parent can call the PAM cleanup stuff with the
30577c478bd9Sstevel@tonic-gate * same PAM service that /bin/login will use to authenticate
30587c478bd9Sstevel@tonic-gate * this session.
30597c478bd9Sstevel@tonic-gate */
30607c478bd9Sstevel@tonic-gate if (auth_level >= 0 && auth_status >= AUTH_USER &&
30617c478bd9Sstevel@tonic-gate (AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) {
30627c478bd9Sstevel@tonic-gate (void) strcpy(pam_svc_name, "ktelnet");
30637c478bd9Sstevel@tonic-gate }
30647c478bd9Sstevel@tonic-gate /*
30657c478bd9Sstevel@tonic-gate * Request to do suppress go ahead.
30667c478bd9Sstevel@tonic-gate *
30677c478bd9Sstevel@tonic-gate * Send this before sending the TELOPT_ECHO stuff below because
30687c478bd9Sstevel@tonic-gate * some clients (MIT KRB5 telnet) have quirky 'kludge mode' support
30697c478bd9Sstevel@tonic-gate * that has them turn off local echo mode if SGA is not received first.
30707c478bd9Sstevel@tonic-gate * This also has the odd side-effect of causing the client to enable
30717c478bd9Sstevel@tonic-gate * encryption and then immediately disable it during the ECHO option
30727c478bd9Sstevel@tonic-gate * negotiations. Its just better to to SGA first now that we support
30737c478bd9Sstevel@tonic-gate * encryption.
30747c478bd9Sstevel@tonic-gate */
30757c478bd9Sstevel@tonic-gate if (!myopts[TELOPT_SGA]) {
30767c478bd9Sstevel@tonic-gate dooption(TELOPT_SGA);
30777c478bd9Sstevel@tonic-gate }
30787c478bd9Sstevel@tonic-gate
30797c478bd9Sstevel@tonic-gate /*
30807c478bd9Sstevel@tonic-gate * Pretend we got a DO ECHO from the client if we have not
30817c478bd9Sstevel@tonic-gate * yet negotiated the ECHO.
30827c478bd9Sstevel@tonic-gate */
30837c478bd9Sstevel@tonic-gate if (!myopts[TELOPT_ECHO]) {
30847c478bd9Sstevel@tonic-gate dooption(TELOPT_ECHO);
30857c478bd9Sstevel@tonic-gate }
30867c478bd9Sstevel@tonic-gate
30877c478bd9Sstevel@tonic-gate /*
30887c478bd9Sstevel@tonic-gate * Is the client side a 4.2 (NOT 4.3) system? We need to know this
30897c478bd9Sstevel@tonic-gate * because 4.2 clients are unable to deal with TCP urgent data.
30907c478bd9Sstevel@tonic-gate *
30917c478bd9Sstevel@tonic-gate * To find out, we send out a "DO ECHO". If the remote system
30927c478bd9Sstevel@tonic-gate * answers "WILL ECHO" it is probably a 4.2 client, and we note
30937c478bd9Sstevel@tonic-gate * that fact ("WILL ECHO" ==> that the client will echo what
30947c478bd9Sstevel@tonic-gate * WE, the server, sends it; it does NOT mean that the client will
30957c478bd9Sstevel@tonic-gate * echo the terminal input).
30967c478bd9Sstevel@tonic-gate */
30977c478bd9Sstevel@tonic-gate send_do(TELOPT_ECHO);
30987c478bd9Sstevel@tonic-gate remopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
30997c478bd9Sstevel@tonic-gate
31007c478bd9Sstevel@tonic-gate if ((pid = fork()) < 0)
31017c478bd9Sstevel@tonic-gate fatalperror(netfd, "fork", errno);
31027c478bd9Sstevel@tonic-gate if (pid)
31037c478bd9Sstevel@tonic-gate telnet(net, master);
31047c478bd9Sstevel@tonic-gate /*
31057c478bd9Sstevel@tonic-gate * The child process needs to be the session leader
31067c478bd9Sstevel@tonic-gate * and have the pty as its controlling tty. Thus we need
31077c478bd9Sstevel@tonic-gate * to re-open the slave side of the pty no without
31087c478bd9Sstevel@tonic-gate * the O_NOCTTY flag that we have been careful to
31097c478bd9Sstevel@tonic-gate * use up to this point.
31107c478bd9Sstevel@tonic-gate */
31117c478bd9Sstevel@tonic-gate (void) setsid();
31127c478bd9Sstevel@tonic-gate
31137c478bd9Sstevel@tonic-gate tt = open(line, O_RDWR);
31147c478bd9Sstevel@tonic-gate if (tt < 0)
31157c478bd9Sstevel@tonic-gate fatalperror(netfd, line, errno);
31167c478bd9Sstevel@tonic-gate (void) close(netfd);
31177c478bd9Sstevel@tonic-gate (void) close(ptmfd);
31187c478bd9Sstevel@tonic-gate (void) close(f);
31197c478bd9Sstevel@tonic-gate (void) close(p);
31207c478bd9Sstevel@tonic-gate (void) close(t);
31217c478bd9Sstevel@tonic-gate if (tt != 0)
31227c478bd9Sstevel@tonic-gate (void) dup2(tt, 0);
31237c478bd9Sstevel@tonic-gate if (tt != 1)
31247c478bd9Sstevel@tonic-gate (void) dup2(tt, 1);
31257c478bd9Sstevel@tonic-gate if (tt != 2)
31267c478bd9Sstevel@tonic-gate (void) dup2(tt, 2);
31277c478bd9Sstevel@tonic-gate if (tt > 2)
31287c478bd9Sstevel@tonic-gate (void) close(tt);
31297c478bd9Sstevel@tonic-gate
31307c478bd9Sstevel@tonic-gate if (terminaltype)
31317c478bd9Sstevel@tonic-gate (void) local_setenv("TERM", terminaltype+5, 1);
31327c478bd9Sstevel@tonic-gate /*
31337c478bd9Sstevel@tonic-gate * -h : pass on name of host.
31347c478bd9Sstevel@tonic-gate * WARNING: -h is accepted by login if and only if
31357c478bd9Sstevel@tonic-gate * getuid() == 0.
31367c478bd9Sstevel@tonic-gate * -p : don't clobber the environment (so terminal type stays set).
31377c478bd9Sstevel@tonic-gate */
31387c478bd9Sstevel@tonic-gate {
31397c478bd9Sstevel@tonic-gate /* System V login expects a utmp entry to already be there */
31407c478bd9Sstevel@tonic-gate struct utmpx ut;
31417c478bd9Sstevel@tonic-gate (void) memset((char *)&ut, 0, sizeof (ut));
31427c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_user, ".telnet", sizeof (ut.ut_user));
31437c478bd9Sstevel@tonic-gate (void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
31447c478bd9Sstevel@tonic-gate ut.ut_pid = getpid();
31457c478bd9Sstevel@tonic-gate ut.ut_id[0] = 't';
31467c478bd9Sstevel@tonic-gate ut.ut_id[1] = (char)SC_WILDC;
31477c478bd9Sstevel@tonic-gate ut.ut_id[2] = (char)SC_WILDC;
31487c478bd9Sstevel@tonic-gate ut.ut_id[3] = (char)SC_WILDC;
31497c478bd9Sstevel@tonic-gate ut.ut_type = LOGIN_PROCESS;
31507c478bd9Sstevel@tonic-gate ut.ut_exit.e_termination = 0;
31517c478bd9Sstevel@tonic-gate ut.ut_exit.e_exit = 0;
31527c478bd9Sstevel@tonic-gate (void) time(&ut.ut_tv.tv_sec);
31537c478bd9Sstevel@tonic-gate if (makeutx(&ut) == NULL)
31547c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "in.telnetd:\tmakeutx failed");
31557c478bd9Sstevel@tonic-gate }
31567c478bd9Sstevel@tonic-gate
31577c478bd9Sstevel@tonic-gate /*
31587c478bd9Sstevel@tonic-gate * Load in the cached environment variables and either
31597c478bd9Sstevel@tonic-gate * set/unset them in the environment.
31607c478bd9Sstevel@tonic-gate */
31617c478bd9Sstevel@tonic-gate for (next = envlist_head; next; ) {
31627c478bd9Sstevel@tonic-gate env = next;
31637c478bd9Sstevel@tonic-gate if (env->delete)
31647c478bd9Sstevel@tonic-gate (void) local_unsetenv(env->name);
31657c478bd9Sstevel@tonic-gate else
31667c478bd9Sstevel@tonic-gate (void) local_setenv(env->name, env->value, 1);
31677c478bd9Sstevel@tonic-gate free(env->name);
31687c478bd9Sstevel@tonic-gate free(env->value);
31697c478bd9Sstevel@tonic-gate next = env->next;
31707c478bd9Sstevel@tonic-gate free(env);
31717c478bd9Sstevel@tonic-gate }
31727c478bd9Sstevel@tonic-gate
31737c478bd9Sstevel@tonic-gate if (!username || !username[0])
31747c478bd9Sstevel@tonic-gate auth_status = AUTH_REJECT; /* we dont know who this is */
31757c478bd9Sstevel@tonic-gate
31767c478bd9Sstevel@tonic-gate /* If the current auth status is less than the required level, exit */
31777c478bd9Sstevel@tonic-gate if (auth_status < auth_level) {
31787c478bd9Sstevel@tonic-gate fatal(net, "Authentication failed\n");
31797c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
31807c478bd9Sstevel@tonic-gate }
31817c478bd9Sstevel@tonic-gate
31827c478bd9Sstevel@tonic-gate /*
31837c478bd9Sstevel@tonic-gate * If AUTH_VALID (proper authentication REQUIRED and we have
31847c478bd9Sstevel@tonic-gate * a krb5_name), exec '/bin/login', make sure it uses the
31857c478bd9Sstevel@tonic-gate * correct PAM service name (pam_svc_name). If possible,
31867c478bd9Sstevel@tonic-gate * make sure the krb5 authenticated user's name (krb5_name)
31877c478bd9Sstevel@tonic-gate * is in the PAM REPOSITORY for krb5.
31887c478bd9Sstevel@tonic-gate */
31897c478bd9Sstevel@tonic-gate if (auth_level >= 0 &&
31907c478bd9Sstevel@tonic-gate (auth_status == AUTH_VALID || auth_status == AUTH_USER) &&
31917c478bd9Sstevel@tonic-gate ((krb5_name != NULL) && strlen(krb5_name)) &&
31927c478bd9Sstevel@tonic-gate ((AuthenticatingUser != NULL) && strlen(AuthenticatingUser))) {
31937c478bd9Sstevel@tonic-gate (void) execl(LOGIN_PROGRAM, "login",
31947c478bd9Sstevel@tonic-gate "-p",
31957c478bd9Sstevel@tonic-gate "-d", slavename,
31967c478bd9Sstevel@tonic-gate "-h", host,
31977c478bd9Sstevel@tonic-gate "-u", krb5_name,
31987c478bd9Sstevel@tonic-gate "-s", pam_svc_name,
31997c478bd9Sstevel@tonic-gate "-R", KRB5_REPOSITORY_NAME,
32007c478bd9Sstevel@tonic-gate AuthenticatingUser, 0);
32017c478bd9Sstevel@tonic-gate } else if (auth_level >= 0 &&
32027c478bd9Sstevel@tonic-gate auth_status >= AUTH_USER &&
32037c478bd9Sstevel@tonic-gate (((AuthenticatingUser != NULL) && strlen(AuthenticatingUser)) ||
32047c478bd9Sstevel@tonic-gate getenv("USER"))) {
32057c478bd9Sstevel@tonic-gate /*
32067c478bd9Sstevel@tonic-gate * If we only know the name but not the principal,
32077c478bd9Sstevel@tonic-gate * login will have to authenticate further.
32087c478bd9Sstevel@tonic-gate */
32097c478bd9Sstevel@tonic-gate (void) execl(LOGIN_PROGRAM, "login",
32107c478bd9Sstevel@tonic-gate "-p",
32117c478bd9Sstevel@tonic-gate "-d", slavename,
32127c478bd9Sstevel@tonic-gate "-h", host,
3213e1c3e95aSdanmcd "-s", pam_svc_name, "--",
32147c478bd9Sstevel@tonic-gate (AuthenticatingUser != NULL ? AuthenticatingUser :
3215e1c3e95aSdanmcd getenv("USER")), 0);
32167c478bd9Sstevel@tonic-gate
32177c478bd9Sstevel@tonic-gate } else /* default, no auth. info available, login does it all */ {
32187c478bd9Sstevel@tonic-gate (void) execl(LOGIN_PROGRAM, "login",
3219e1c3e95aSdanmcd "-p", "-h", host, "-d", slavename, "--",
32207c478bd9Sstevel@tonic-gate getenv("USER"), 0);
32217c478bd9Sstevel@tonic-gate }
32227c478bd9Sstevel@tonic-gate
32237c478bd9Sstevel@tonic-gate fatalperror(netfd, LOGIN_PROGRAM, errno);
32247c478bd9Sstevel@tonic-gate /*NOTREACHED*/
32257c478bd9Sstevel@tonic-gate }
32267c478bd9Sstevel@tonic-gate
32277c478bd9Sstevel@tonic-gate static void
fatal(int f,char * msg)32287c478bd9Sstevel@tonic-gate fatal(int f, char *msg)
32297c478bd9Sstevel@tonic-gate {
32307c478bd9Sstevel@tonic-gate char buf[BUFSIZ];
32317c478bd9Sstevel@tonic-gate
32327c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "telnetd: %s.\r\n", msg);
32337c478bd9Sstevel@tonic-gate (void) write(f, buf, strlen(buf));
32347c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
32357c478bd9Sstevel@tonic-gate /*NOTREACHED*/
32367c478bd9Sstevel@tonic-gate }
32377c478bd9Sstevel@tonic-gate
32387c478bd9Sstevel@tonic-gate static void
fatalperror(int f,char * msg,int errnum)32397c478bd9Sstevel@tonic-gate fatalperror(int f, char *msg, int errnum)
32407c478bd9Sstevel@tonic-gate {
32417c478bd9Sstevel@tonic-gate char buf[BUFSIZ];
32427c478bd9Sstevel@tonic-gate
32437c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf),
32447c478bd9Sstevel@tonic-gate "%s: %s\r\n", msg, strerror(errnum));
32457c478bd9Sstevel@tonic-gate fatal(f, buf);
32467c478bd9Sstevel@tonic-gate /*NOTREACHED*/
32477c478bd9Sstevel@tonic-gate }
32487c478bd9Sstevel@tonic-gate
32497c478bd9Sstevel@tonic-gate /*
32507c478bd9Sstevel@tonic-gate * Main loop. Select from pty and network, and
32517c478bd9Sstevel@tonic-gate * hand data to telnet receiver finite state machine
32527c478bd9Sstevel@tonic-gate * when it receives telnet protocol. Regular data
32537c478bd9Sstevel@tonic-gate * flow between pty and network takes place through
32547c478bd9Sstevel@tonic-gate * inkernel telnet streams module (telmod).
32557c478bd9Sstevel@tonic-gate */
32567c478bd9Sstevel@tonic-gate static void
telnet(int net,int master)32577c478bd9Sstevel@tonic-gate telnet(int net, int master)
32587c478bd9Sstevel@tonic-gate {
32597c478bd9Sstevel@tonic-gate int on = 1;
32607c478bd9Sstevel@tonic-gate char mode;
32617c478bd9Sstevel@tonic-gate struct strioctl telnetmod;
32627c478bd9Sstevel@tonic-gate int nsize = 0;
32637c478bd9Sstevel@tonic-gate char binary_in = 0;
32647c478bd9Sstevel@tonic-gate char binary_out = 0;
32657c478bd9Sstevel@tonic-gate
32667c478bd9Sstevel@tonic-gate if (ioctl(net, FIONBIO, &on) == -1)
32677c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl FIONBIO net: %m\n");
32687c478bd9Sstevel@tonic-gate if (ioctl(master, FIONBIO, &on) == -1)
32697c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl FIONBIO pty p: %m\n");
32707c478bd9Sstevel@tonic-gate (void) signal(SIGTSTP, SIG_IGN);
32717c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, (void (*)())cleanup);
32727c478bd9Sstevel@tonic-gate (void) setpgrp();
32737c478bd9Sstevel@tonic-gate
32747c478bd9Sstevel@tonic-gate /*
32757c478bd9Sstevel@tonic-gate * Call telrcv() once to pick up anything received during
32767c478bd9Sstevel@tonic-gate * terminal type negotiation.
32777c478bd9Sstevel@tonic-gate */
32787c478bd9Sstevel@tonic-gate telrcv();
32797c478bd9Sstevel@tonic-gate
32807c478bd9Sstevel@tonic-gate netflush();
32817c478bd9Sstevel@tonic-gate ptyflush();
32827c478bd9Sstevel@tonic-gate
32837c478bd9Sstevel@tonic-gate for (;;) {
32847c478bd9Sstevel@tonic-gate fd_set ibits, obits, xbits;
32857c478bd9Sstevel@tonic-gate int c;
32867c478bd9Sstevel@tonic-gate
32877c478bd9Sstevel@tonic-gate if (ncc < 0)
32887c478bd9Sstevel@tonic-gate break;
32897c478bd9Sstevel@tonic-gate
32907c478bd9Sstevel@tonic-gate FD_ZERO(&ibits);
32917c478bd9Sstevel@tonic-gate FD_ZERO(&obits);
32927c478bd9Sstevel@tonic-gate FD_ZERO(&xbits);
32937c478bd9Sstevel@tonic-gate
32947c478bd9Sstevel@tonic-gate /*
32957c478bd9Sstevel@tonic-gate * If we couldn't flush all our output to the network,
32967c478bd9Sstevel@tonic-gate * keep checking for when we can.
32977c478bd9Sstevel@tonic-gate */
32987c478bd9Sstevel@tonic-gate if (nfrontp - nbackp)
32997c478bd9Sstevel@tonic-gate FD_SET(net, &obits);
33007c478bd9Sstevel@tonic-gate /*
33017c478bd9Sstevel@tonic-gate * Never look for input if there's still
33027c478bd9Sstevel@tonic-gate * stuff in the corresponding output buffer
33037c478bd9Sstevel@tonic-gate */
33047c478bd9Sstevel@tonic-gate if (pfrontp - pbackp) {
33057c478bd9Sstevel@tonic-gate FD_SET(master, &obits);
33067c478bd9Sstevel@tonic-gate } else {
33077c478bd9Sstevel@tonic-gate FD_SET(net, &ibits);
33087c478bd9Sstevel@tonic-gate }
33097c478bd9Sstevel@tonic-gate if (!SYNCHing) {
33107c478bd9Sstevel@tonic-gate FD_SET(net, &xbits);
33117c478bd9Sstevel@tonic-gate }
33127c478bd9Sstevel@tonic-gate
33137c478bd9Sstevel@tonic-gate #define max(x, y) (((x) < (y)) ? (y) : (x))
33147c478bd9Sstevel@tonic-gate
33157c478bd9Sstevel@tonic-gate /*
33167c478bd9Sstevel@tonic-gate * make an ioctl to telnet module (net side) to send
33177c478bd9Sstevel@tonic-gate * binary mode of telnet daemon. binary_in and
33187c478bd9Sstevel@tonic-gate * binary_out are 0 if not in binary mode.
33197c478bd9Sstevel@tonic-gate */
33207c478bd9Sstevel@tonic-gate if (binary_in != myopts[TELOPT_BINARY] ||
33217c478bd9Sstevel@tonic-gate binary_out != remopts[TELOPT_BINARY]) {
33227c478bd9Sstevel@tonic-gate
33237c478bd9Sstevel@tonic-gate mode = 0;
33247c478bd9Sstevel@tonic-gate if (myopts[TELOPT_BINARY] != OPT_NO)
33257c478bd9Sstevel@tonic-gate mode |= TEL_BINARY_IN;
33267c478bd9Sstevel@tonic-gate
33277c478bd9Sstevel@tonic-gate if (remopts[TELOPT_BINARY] != OPT_NO)
33287c478bd9Sstevel@tonic-gate mode |= TEL_BINARY_OUT;
33297c478bd9Sstevel@tonic-gate
33307c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = TEL_IOC_MODE;
33317c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
33327c478bd9Sstevel@tonic-gate telnetmod.ic_len = 1;
33337c478bd9Sstevel@tonic-gate telnetmod.ic_dp = &mode;
33347c478bd9Sstevel@tonic-gate
33357c478bd9Sstevel@tonic-gate syslog(LOG_DEBUG, "TEL_IOC_MODE binary has changed\n");
33367c478bd9Sstevel@tonic-gate
33377c478bd9Sstevel@tonic-gate if (ioctl(net, I_STR, &telnetmod) < 0)
33387c478bd9Sstevel@tonic-gate fatal(net, "ioctl TEL_IOC_MODE failed\n");
33397c478bd9Sstevel@tonic-gate binary_in = myopts[TELOPT_BINARY];
33407c478bd9Sstevel@tonic-gate binary_out = remopts[TELOPT_BINARY];
33417c478bd9Sstevel@tonic-gate }
33427c478bd9Sstevel@tonic-gate if (state == TS_DATA) {
33437c478bd9Sstevel@tonic-gate if ((nfrontp == nbackp) &&
33447c478bd9Sstevel@tonic-gate (pfrontp == pbackp)) {
33457c478bd9Sstevel@tonic-gate if (ioctl(net, I_NREAD, &nsize) < 0)
33467c478bd9Sstevel@tonic-gate fatalperror(net,
33477c478bd9Sstevel@tonic-gate "ioctl I_NREAD failed\n", errno);
33487c478bd9Sstevel@tonic-gate if (nsize)
33497c478bd9Sstevel@tonic-gate drainstream(nsize);
33507c478bd9Sstevel@tonic-gate
33517c478bd9Sstevel@tonic-gate /*
33527c478bd9Sstevel@tonic-gate * make an ioctl to reinsert remaining data at
33537c478bd9Sstevel@tonic-gate * streamhead. After this, ioctl reenables the
33547c478bd9Sstevel@tonic-gate * telnet lower put queue. This queue was
33557c478bd9Sstevel@tonic-gate * noenabled by telnet module after sending
33567c478bd9Sstevel@tonic-gate * protocol/urgent data to telnetd.
33577c478bd9Sstevel@tonic-gate */
33587c478bd9Sstevel@tonic-gate
33597c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = TEL_IOC_ENABLE;
33607c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
33617c478bd9Sstevel@tonic-gate if (ncc || nsize) {
33627c478bd9Sstevel@tonic-gate telnetmod.ic_len = ncc + nsize;
33637c478bd9Sstevel@tonic-gate telnetmod.ic_dp = netip;
33647c478bd9Sstevel@tonic-gate } else {
33657c478bd9Sstevel@tonic-gate telnetmod.ic_len = 0;
33667c478bd9Sstevel@tonic-gate telnetmod.ic_dp = NULL;
33677c478bd9Sstevel@tonic-gate }
33687c478bd9Sstevel@tonic-gate if (ioctl(net, I_STR, &telnetmod) < 0)
33697c478bd9Sstevel@tonic-gate fatal(net, "ioctl TEL_IOC_ENABLE \
33707c478bd9Sstevel@tonic-gate failed\n");
33717c478bd9Sstevel@tonic-gate
33727c478bd9Sstevel@tonic-gate telmod_init_done = B_TRUE;
33737c478bd9Sstevel@tonic-gate
33747c478bd9Sstevel@tonic-gate netip = netibuf;
33757c478bd9Sstevel@tonic-gate (void) memset(netibuf, 0, netibufsize);
33767c478bd9Sstevel@tonic-gate
33777c478bd9Sstevel@tonic-gate ncc = 0;
33787c478bd9Sstevel@tonic-gate }
33797c478bd9Sstevel@tonic-gate } else {
33807c478bd9Sstevel@tonic-gate /*
33817c478bd9Sstevel@tonic-gate * state not changed to TS_DATA and hence, more to read
33827c478bd9Sstevel@tonic-gate * send ioctl to get one more message block.
33837c478bd9Sstevel@tonic-gate */
33847c478bd9Sstevel@tonic-gate telnetmod.ic_cmd = TEL_IOC_GETBLK;
33857c478bd9Sstevel@tonic-gate telnetmod.ic_timout = -1;
33867c478bd9Sstevel@tonic-gate telnetmod.ic_len = 0;
33877c478bd9Sstevel@tonic-gate telnetmod.ic_dp = NULL;
33887c478bd9Sstevel@tonic-gate
33897c478bd9Sstevel@tonic-gate if (ioctl(net, I_STR, &telnetmod) < 0)
33907c478bd9Sstevel@tonic-gate fatal(net, "ioctl TEL_IOC_GETBLK failed\n");
33917c478bd9Sstevel@tonic-gate }
33927c478bd9Sstevel@tonic-gate
33937c478bd9Sstevel@tonic-gate if ((c = select(max(net, master) + 1, &ibits, &obits, &xbits,
33947c478bd9Sstevel@tonic-gate (struct timeval *)0)) < 1) {
33957c478bd9Sstevel@tonic-gate if (c == -1) {
33967c478bd9Sstevel@tonic-gate if (errno == EINTR) {
33977c478bd9Sstevel@tonic-gate continue;
33987c478bd9Sstevel@tonic-gate }
33997c478bd9Sstevel@tonic-gate }
34007c478bd9Sstevel@tonic-gate (void) sleep(5);
34017c478bd9Sstevel@tonic-gate continue;
34027c478bd9Sstevel@tonic-gate }
34037c478bd9Sstevel@tonic-gate
34047c478bd9Sstevel@tonic-gate /*
34057c478bd9Sstevel@tonic-gate * Any urgent data?
34067c478bd9Sstevel@tonic-gate */
34077c478bd9Sstevel@tonic-gate if (FD_ISSET(net, &xbits)) {
34087c478bd9Sstevel@tonic-gate SYNCHing = 1;
34097c478bd9Sstevel@tonic-gate }
34107c478bd9Sstevel@tonic-gate
34117c478bd9Sstevel@tonic-gate /*
34127c478bd9Sstevel@tonic-gate * Something to read from the network...
34137c478bd9Sstevel@tonic-gate */
34147c478bd9Sstevel@tonic-gate if (FD_ISSET(net, &ibits)) {
34157c478bd9Sstevel@tonic-gate ncc = read(net, netibuf, netibufsize);
34167c478bd9Sstevel@tonic-gate if (ncc < 0 && errno == EWOULDBLOCK)
34177c478bd9Sstevel@tonic-gate ncc = 0;
34187c478bd9Sstevel@tonic-gate else {
34197c478bd9Sstevel@tonic-gate if (ncc <= 0) {
34207c478bd9Sstevel@tonic-gate break;
34217c478bd9Sstevel@tonic-gate }
34227c478bd9Sstevel@tonic-gate netip = netibuf;
34237c478bd9Sstevel@tonic-gate }
34247c478bd9Sstevel@tonic-gate }
34257c478bd9Sstevel@tonic-gate
34267c478bd9Sstevel@tonic-gate if (FD_ISSET(net, &obits) && (nfrontp - nbackp) > 0)
34277c478bd9Sstevel@tonic-gate netflush();
34287c478bd9Sstevel@tonic-gate if (ncc > 0)
34297c478bd9Sstevel@tonic-gate telrcv();
34307c478bd9Sstevel@tonic-gate if (FD_ISSET(master, &obits) && (pfrontp - pbackp) > 0)
34317c478bd9Sstevel@tonic-gate ptyflush();
34327c478bd9Sstevel@tonic-gate }
34337c478bd9Sstevel@tonic-gate cleanup(0);
34347c478bd9Sstevel@tonic-gate }
34357c478bd9Sstevel@tonic-gate
34367c478bd9Sstevel@tonic-gate static void
telrcv(void)34377c478bd9Sstevel@tonic-gate telrcv(void)
34387c478bd9Sstevel@tonic-gate {
34397c478bd9Sstevel@tonic-gate int c;
34407c478bd9Sstevel@tonic-gate
34417c478bd9Sstevel@tonic-gate while (ncc > 0) {
34427c478bd9Sstevel@tonic-gate if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
34437c478bd9Sstevel@tonic-gate return;
34447c478bd9Sstevel@tonic-gate c = *netip & 0377;
34457c478bd9Sstevel@tonic-gate /*
34467c478bd9Sstevel@tonic-gate * Once we hit data, we want to transition back to
34477c478bd9Sstevel@tonic-gate * in-kernel processing. However, this code is shared
34487c478bd9Sstevel@tonic-gate * by getterminaltype()/ttloop() which run before the
34497c478bd9Sstevel@tonic-gate * in-kernel plumbing is available. So if we are still
34507c478bd9Sstevel@tonic-gate * processing the initial option negotiation, even TS_DATA
34517c478bd9Sstevel@tonic-gate * must be processed here.
34527c478bd9Sstevel@tonic-gate */
34537c478bd9Sstevel@tonic-gate if (c != IAC && state == TS_DATA && init_neg_done) {
34547c478bd9Sstevel@tonic-gate break;
34557c478bd9Sstevel@tonic-gate }
34567c478bd9Sstevel@tonic-gate netip++;
34577c478bd9Sstevel@tonic-gate ncc--;
34587c478bd9Sstevel@tonic-gate switch (state) {
34597c478bd9Sstevel@tonic-gate
34607c478bd9Sstevel@tonic-gate case TS_CR:
34617c478bd9Sstevel@tonic-gate state = TS_DATA;
34627c478bd9Sstevel@tonic-gate /* Strip off \n or \0 after a \r */
34637c478bd9Sstevel@tonic-gate if ((c == 0) || (c == '\n')) {
34647c478bd9Sstevel@tonic-gate break;
34657c478bd9Sstevel@tonic-gate }
34667c478bd9Sstevel@tonic-gate /* FALLTHRU */
34677c478bd9Sstevel@tonic-gate
34687c478bd9Sstevel@tonic-gate case TS_DATA:
34697c478bd9Sstevel@tonic-gate if (c == IAC) {
34707c478bd9Sstevel@tonic-gate state = TS_IAC;
34717c478bd9Sstevel@tonic-gate break;
34727c478bd9Sstevel@tonic-gate }
34737c478bd9Sstevel@tonic-gate if (inter > 0)
34747c478bd9Sstevel@tonic-gate break;
34757c478bd9Sstevel@tonic-gate /*
34767c478bd9Sstevel@tonic-gate * We map \r\n ==> \r, since
34777c478bd9Sstevel@tonic-gate * We now map \r\n ==> \r for pragmatic reasons.
34787c478bd9Sstevel@tonic-gate * Many client implementations send \r\n when
34797c478bd9Sstevel@tonic-gate * the user hits the CarriageReturn key.
34807c478bd9Sstevel@tonic-gate *
34817c478bd9Sstevel@tonic-gate * We USED to map \r\n ==> \n, since \r\n says
34827c478bd9Sstevel@tonic-gate * that we want to be in column 1 of the next
34837c478bd9Sstevel@tonic-gate * line.
34847c478bd9Sstevel@tonic-gate */
34857c478bd9Sstevel@tonic-gate if (c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
34867c478bd9Sstevel@tonic-gate state = TS_CR;
34877c478bd9Sstevel@tonic-gate }
34887c478bd9Sstevel@tonic-gate *pfrontp++ = c;
34897c478bd9Sstevel@tonic-gate break;
34907c478bd9Sstevel@tonic-gate
34917c478bd9Sstevel@tonic-gate case TS_IAC:
34927c478bd9Sstevel@tonic-gate switch (c) {
34937c478bd9Sstevel@tonic-gate
34947c478bd9Sstevel@tonic-gate /*
34957c478bd9Sstevel@tonic-gate * Send the process on the pty side an
34967c478bd9Sstevel@tonic-gate * interrupt. Do this with a NULL or
34977c478bd9Sstevel@tonic-gate * interrupt char; depending on the tty mode.
34987c478bd9Sstevel@tonic-gate */
34997c478bd9Sstevel@tonic-gate case IP:
35007c478bd9Sstevel@tonic-gate interrupt();
35017c478bd9Sstevel@tonic-gate break;
35027c478bd9Sstevel@tonic-gate
35037c478bd9Sstevel@tonic-gate case BREAK:
35047c478bd9Sstevel@tonic-gate sendbrk();
35057c478bd9Sstevel@tonic-gate break;
35067c478bd9Sstevel@tonic-gate
35077c478bd9Sstevel@tonic-gate /*
35087c478bd9Sstevel@tonic-gate * Are You There?
35097c478bd9Sstevel@tonic-gate */
35107c478bd9Sstevel@tonic-gate case AYT:
35117c478bd9Sstevel@tonic-gate write_data_len("\r\n[Yes]\r\n", 9);
35127c478bd9Sstevel@tonic-gate break;
35137c478bd9Sstevel@tonic-gate
35147c478bd9Sstevel@tonic-gate /*
35157c478bd9Sstevel@tonic-gate * Abort Output
35167c478bd9Sstevel@tonic-gate */
35177c478bd9Sstevel@tonic-gate case AO: {
35187c478bd9Sstevel@tonic-gate struct ltchars tmpltc;
35197c478bd9Sstevel@tonic-gate
35207c478bd9Sstevel@tonic-gate ptyflush(); /* half-hearted */
35217c478bd9Sstevel@tonic-gate if (ioctl(pty, TIOCGLTC, &tmpltc) == -1)
35227c478bd9Sstevel@tonic-gate syslog(LOG_INFO,
35237c478bd9Sstevel@tonic-gate "ioctl TIOCGLTC: %m\n");
35247c478bd9Sstevel@tonic-gate if (tmpltc.t_flushc != '\377') {
35257c478bd9Sstevel@tonic-gate *pfrontp++ = tmpltc.t_flushc;
35267c478bd9Sstevel@tonic-gate }
35277c478bd9Sstevel@tonic-gate netclear(); /* clear buffer back */
35287c478bd9Sstevel@tonic-gate write_data("%c%c", (uchar_t)IAC,
35297c478bd9Sstevel@tonic-gate (uchar_t)DM);
35307c478bd9Sstevel@tonic-gate
35317c478bd9Sstevel@tonic-gate neturg = nfrontp-1; /* off by one XXX */
35327c478bd9Sstevel@tonic-gate netflush();
35337c478bd9Sstevel@tonic-gate netflush(); /* XXX.sparker */
35347c478bd9Sstevel@tonic-gate break;
35357c478bd9Sstevel@tonic-gate }
35367c478bd9Sstevel@tonic-gate
35377c478bd9Sstevel@tonic-gate /*
35387c478bd9Sstevel@tonic-gate * Erase Character and
35397c478bd9Sstevel@tonic-gate * Erase Line
35407c478bd9Sstevel@tonic-gate */
35417c478bd9Sstevel@tonic-gate case EC:
35427c478bd9Sstevel@tonic-gate case EL: {
35437c478bd9Sstevel@tonic-gate struct sgttyb b;
35447c478bd9Sstevel@tonic-gate char ch;
35457c478bd9Sstevel@tonic-gate
35467c478bd9Sstevel@tonic-gate ptyflush(); /* half-hearted */
35477c478bd9Sstevel@tonic-gate if (ioctl(pty, TIOCGETP, &b) == -1)
35487c478bd9Sstevel@tonic-gate syslog(LOG_INFO,
35497c478bd9Sstevel@tonic-gate "ioctl TIOCGETP: %m\n");
35507c478bd9Sstevel@tonic-gate ch = (c == EC) ?
35517c478bd9Sstevel@tonic-gate b.sg_erase : b.sg_kill;
35527c478bd9Sstevel@tonic-gate if (ch != '\377') {
35537c478bd9Sstevel@tonic-gate *pfrontp++ = ch;
35547c478bd9Sstevel@tonic-gate }
35557c478bd9Sstevel@tonic-gate break;
35567c478bd9Sstevel@tonic-gate }
35577c478bd9Sstevel@tonic-gate
35587c478bd9Sstevel@tonic-gate /*
35597c478bd9Sstevel@tonic-gate * Check for urgent data...
35607c478bd9Sstevel@tonic-gate */
35617c478bd9Sstevel@tonic-gate case DM:
35627c478bd9Sstevel@tonic-gate break;
35637c478bd9Sstevel@tonic-gate
35647c478bd9Sstevel@tonic-gate /*
35657c478bd9Sstevel@tonic-gate * Begin option subnegotiation...
35667c478bd9Sstevel@tonic-gate */
35677c478bd9Sstevel@tonic-gate case SB:
35687c478bd9Sstevel@tonic-gate state = TS_SB;
35697c478bd9Sstevel@tonic-gate SB_CLEAR();
35707c478bd9Sstevel@tonic-gate continue;
35717c478bd9Sstevel@tonic-gate
35727c478bd9Sstevel@tonic-gate case WILL:
35737c478bd9Sstevel@tonic-gate state = TS_WILL;
35747c478bd9Sstevel@tonic-gate continue;
35757c478bd9Sstevel@tonic-gate
35767c478bd9Sstevel@tonic-gate case WONT:
35777c478bd9Sstevel@tonic-gate state = TS_WONT;
35787c478bd9Sstevel@tonic-gate continue;
35797c478bd9Sstevel@tonic-gate
35807c478bd9Sstevel@tonic-gate case DO:
35817c478bd9Sstevel@tonic-gate state = TS_DO;
35827c478bd9Sstevel@tonic-gate continue;
35837c478bd9Sstevel@tonic-gate
35847c478bd9Sstevel@tonic-gate case DONT:
35857c478bd9Sstevel@tonic-gate state = TS_DONT;
35867c478bd9Sstevel@tonic-gate continue;
35877c478bd9Sstevel@tonic-gate
35887c478bd9Sstevel@tonic-gate case IAC:
35897c478bd9Sstevel@tonic-gate *pfrontp++ = c;
35907c478bd9Sstevel@tonic-gate break;
35917c478bd9Sstevel@tonic-gate }
35927c478bd9Sstevel@tonic-gate state = TS_DATA;
35937c478bd9Sstevel@tonic-gate break;
35947c478bd9Sstevel@tonic-gate case TS_SB:
35957c478bd9Sstevel@tonic-gate if (c == IAC) {
35967c478bd9Sstevel@tonic-gate state = TS_SE;
35977c478bd9Sstevel@tonic-gate } else {
35987c478bd9Sstevel@tonic-gate SB_ACCUM(c);
35997c478bd9Sstevel@tonic-gate }
36007c478bd9Sstevel@tonic-gate break;
36017c478bd9Sstevel@tonic-gate case TS_SE:
36027c478bd9Sstevel@tonic-gate if (c != SE) {
36037c478bd9Sstevel@tonic-gate if (c != IAC) {
36047c478bd9Sstevel@tonic-gate SB_ACCUM((uchar_t)IAC);
36057c478bd9Sstevel@tonic-gate }
36067c478bd9Sstevel@tonic-gate SB_ACCUM(c);
36077c478bd9Sstevel@tonic-gate state = TS_SB;
36087c478bd9Sstevel@tonic-gate
36097c478bd9Sstevel@tonic-gate } else {
36107c478bd9Sstevel@tonic-gate SB_TERM();
36117c478bd9Sstevel@tonic-gate suboption(); /* handle sub-option */
36127c478bd9Sstevel@tonic-gate state = TS_DATA;
36137c478bd9Sstevel@tonic-gate }
36147c478bd9Sstevel@tonic-gate break;
36157c478bd9Sstevel@tonic-gate
36167c478bd9Sstevel@tonic-gate case TS_WILL:
36177c478bd9Sstevel@tonic-gate if (remopts[c] != OPT_YES)
36187c478bd9Sstevel@tonic-gate willoption(c);
36197c478bd9Sstevel@tonic-gate state = TS_DATA;
36207c478bd9Sstevel@tonic-gate continue;
36217c478bd9Sstevel@tonic-gate
36227c478bd9Sstevel@tonic-gate case TS_WONT:
36237c478bd9Sstevel@tonic-gate if (remopts[c] != OPT_NO)
36247c478bd9Sstevel@tonic-gate wontoption(c);
36257c478bd9Sstevel@tonic-gate state = TS_DATA;
36267c478bd9Sstevel@tonic-gate continue;
36277c478bd9Sstevel@tonic-gate
36287c478bd9Sstevel@tonic-gate case TS_DO:
36297c478bd9Sstevel@tonic-gate if (myopts[c] != OPT_YES)
36307c478bd9Sstevel@tonic-gate dooption(c);
36317c478bd9Sstevel@tonic-gate state = TS_DATA;
36327c478bd9Sstevel@tonic-gate continue;
36337c478bd9Sstevel@tonic-gate
36347c478bd9Sstevel@tonic-gate case TS_DONT:
36357c478bd9Sstevel@tonic-gate if (myopts[c] != OPT_NO) {
36367c478bd9Sstevel@tonic-gate dontoption(c);
36377c478bd9Sstevel@tonic-gate }
36387c478bd9Sstevel@tonic-gate state = TS_DATA;
36397c478bd9Sstevel@tonic-gate continue;
36407c478bd9Sstevel@tonic-gate
36417c478bd9Sstevel@tonic-gate default:
36427c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
36437c478bd9Sstevel@tonic-gate (void) printf("telnetd: panic state=%d\n", state);
36447c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
36457c478bd9Sstevel@tonic-gate }
36467c478bd9Sstevel@tonic-gate }
36477c478bd9Sstevel@tonic-gate }
36487c478bd9Sstevel@tonic-gate
36497c478bd9Sstevel@tonic-gate static void
willoption(int option)36507c478bd9Sstevel@tonic-gate willoption(int option)
36517c478bd9Sstevel@tonic-gate {
36527c478bd9Sstevel@tonic-gate uchar_t *fmt;
36537c478bd9Sstevel@tonic-gate boolean_t send_reply = B_TRUE;
36547c478bd9Sstevel@tonic-gate
36557c478bd9Sstevel@tonic-gate switch (option) {
36567c478bd9Sstevel@tonic-gate case TELOPT_BINARY:
36577c478bd9Sstevel@tonic-gate mode(O_RAW, 0);
36587c478bd9Sstevel@tonic-gate fmt = doopt;
36597c478bd9Sstevel@tonic-gate break;
36607c478bd9Sstevel@tonic-gate
36617c478bd9Sstevel@tonic-gate case TELOPT_ECHO:
36627c478bd9Sstevel@tonic-gate not42 = 0; /* looks like a 4.2 system */
36637c478bd9Sstevel@tonic-gate /*
36647c478bd9Sstevel@tonic-gate * Now, in a 4.2 system, to break them out of ECHOing
36657c478bd9Sstevel@tonic-gate * (to the terminal) mode, we need to send a "WILL ECHO".
36667c478bd9Sstevel@tonic-gate * Kludge upon kludge!
36677c478bd9Sstevel@tonic-gate */
36687c478bd9Sstevel@tonic-gate if (myopts[TELOPT_ECHO] == OPT_YES) {
36697c478bd9Sstevel@tonic-gate dooption(TELOPT_ECHO);
36707c478bd9Sstevel@tonic-gate }
36717c478bd9Sstevel@tonic-gate fmt = dont;
36727c478bd9Sstevel@tonic-gate break;
36737c478bd9Sstevel@tonic-gate case TELOPT_TTYPE:
36747c478bd9Sstevel@tonic-gate settimer(ttypeopt);
36757c478bd9Sstevel@tonic-gate goto common;
36767c478bd9Sstevel@tonic-gate
36777c478bd9Sstevel@tonic-gate case TELOPT_NAWS:
36787c478bd9Sstevel@tonic-gate settimer(nawsopt);
36797c478bd9Sstevel@tonic-gate goto common;
36807c478bd9Sstevel@tonic-gate
36817c478bd9Sstevel@tonic-gate case TELOPT_XDISPLOC:
36827c478bd9Sstevel@tonic-gate settimer(xdisplocopt);
36837c478bd9Sstevel@tonic-gate goto common;
36847c478bd9Sstevel@tonic-gate
36857c478bd9Sstevel@tonic-gate case TELOPT_NEW_ENVIRON:
36867c478bd9Sstevel@tonic-gate settimer(environopt);
36877c478bd9Sstevel@tonic-gate goto common;
36887c478bd9Sstevel@tonic-gate
36897c478bd9Sstevel@tonic-gate case TELOPT_AUTHENTICATION:
36907c478bd9Sstevel@tonic-gate settimer(authopt);
36917c478bd9Sstevel@tonic-gate if (remopts[option] == OPT_NO ||
36927c478bd9Sstevel@tonic-gate negotiate_auth_krb5 == 0)
36937c478bd9Sstevel@tonic-gate fmt = dont;
36947c478bd9Sstevel@tonic-gate else
36957c478bd9Sstevel@tonic-gate fmt = doopt;
36967c478bd9Sstevel@tonic-gate break;
36977c478bd9Sstevel@tonic-gate
36987c478bd9Sstevel@tonic-gate case TELOPT_OLD_ENVIRON:
36997c478bd9Sstevel@tonic-gate settimer(oenvironopt);
37007c478bd9Sstevel@tonic-gate goto common;
37017c478bd9Sstevel@tonic-gate common:
37027c478bd9Sstevel@tonic-gate if (remopts[option] == OPT_YES_BUT_ALWAYS_LOOK) {
37037c478bd9Sstevel@tonic-gate remopts[option] = OPT_YES;
37047c478bd9Sstevel@tonic-gate return;
37057c478bd9Sstevel@tonic-gate }
37067c478bd9Sstevel@tonic-gate /*FALLTHRU*/
37077c478bd9Sstevel@tonic-gate case TELOPT_SGA:
37087c478bd9Sstevel@tonic-gate fmt = doopt;
37097c478bd9Sstevel@tonic-gate break;
37107c478bd9Sstevel@tonic-gate
37117c478bd9Sstevel@tonic-gate case TELOPT_TM:
37127c478bd9Sstevel@tonic-gate fmt = dont;
37137c478bd9Sstevel@tonic-gate break;
37147c478bd9Sstevel@tonic-gate
37157c478bd9Sstevel@tonic-gate case TELOPT_ENCRYPT:
37167c478bd9Sstevel@tonic-gate settimer(encropt); /* got response to do/dont */
37177c478bd9Sstevel@tonic-gate if (enc_debug)
37187c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
37197c478bd9Sstevel@tonic-gate "RCVD IAC WILL TELOPT_ENCRYPT\n");
37207c478bd9Sstevel@tonic-gate if (krb5_privacy_allowed()) {
37217c478bd9Sstevel@tonic-gate fmt = doopt;
37227c478bd9Sstevel@tonic-gate if (sent_do_encrypt)
37237c478bd9Sstevel@tonic-gate send_reply = B_FALSE;
37247c478bd9Sstevel@tonic-gate else
37257c478bd9Sstevel@tonic-gate sent_do_encrypt = B_TRUE;
37267c478bd9Sstevel@tonic-gate } else {
37277c478bd9Sstevel@tonic-gate fmt = dont;
37287c478bd9Sstevel@tonic-gate }
37297c478bd9Sstevel@tonic-gate break;
37307c478bd9Sstevel@tonic-gate
37317c478bd9Sstevel@tonic-gate default:
37327c478bd9Sstevel@tonic-gate fmt = dont;
37337c478bd9Sstevel@tonic-gate break;
37347c478bd9Sstevel@tonic-gate }
37357c478bd9Sstevel@tonic-gate if (fmt == doopt) {
37367c478bd9Sstevel@tonic-gate remopts[option] = OPT_YES;
37377c478bd9Sstevel@tonic-gate } else {
37387c478bd9Sstevel@tonic-gate remopts[option] = OPT_NO;
37397c478bd9Sstevel@tonic-gate }
37407c478bd9Sstevel@tonic-gate if (send_reply) {
37417c478bd9Sstevel@tonic-gate write_data((const char *)fmt, option);
37427c478bd9Sstevel@tonic-gate netflush();
37437c478bd9Sstevel@tonic-gate }
37447c478bd9Sstevel@tonic-gate }
37457c478bd9Sstevel@tonic-gate
37467c478bd9Sstevel@tonic-gate static void
wontoption(int option)37477c478bd9Sstevel@tonic-gate wontoption(int option)
37487c478bd9Sstevel@tonic-gate {
37497c478bd9Sstevel@tonic-gate uchar_t *fmt;
37507c478bd9Sstevel@tonic-gate int send_reply = 1;
37517c478bd9Sstevel@tonic-gate
37527c478bd9Sstevel@tonic-gate switch (option) {
37537c478bd9Sstevel@tonic-gate case TELOPT_ECHO:
37547c478bd9Sstevel@tonic-gate not42 = 1; /* doesn't seem to be a 4.2 system */
37557c478bd9Sstevel@tonic-gate break;
37567c478bd9Sstevel@tonic-gate
37577c478bd9Sstevel@tonic-gate case TELOPT_BINARY:
37587c478bd9Sstevel@tonic-gate mode(0, O_RAW);
37597c478bd9Sstevel@tonic-gate break;
37607c478bd9Sstevel@tonic-gate
37617c478bd9Sstevel@tonic-gate case TELOPT_TTYPE:
37627c478bd9Sstevel@tonic-gate settimer(ttypeopt);
37637c478bd9Sstevel@tonic-gate break;
37647c478bd9Sstevel@tonic-gate
37657c478bd9Sstevel@tonic-gate case TELOPT_NAWS:
37667c478bd9Sstevel@tonic-gate settimer(nawsopt);
37677c478bd9Sstevel@tonic-gate break;
37687c478bd9Sstevel@tonic-gate
37697c478bd9Sstevel@tonic-gate case TELOPT_XDISPLOC:
37707c478bd9Sstevel@tonic-gate settimer(xdisplocopt);
37717c478bd9Sstevel@tonic-gate break;
37727c478bd9Sstevel@tonic-gate
37737c478bd9Sstevel@tonic-gate case TELOPT_NEW_ENVIRON:
37747c478bd9Sstevel@tonic-gate settimer(environopt);
37757c478bd9Sstevel@tonic-gate break;
37767c478bd9Sstevel@tonic-gate
37777c478bd9Sstevel@tonic-gate case TELOPT_OLD_ENVIRON:
37787c478bd9Sstevel@tonic-gate settimer(oenvironopt);
37797c478bd9Sstevel@tonic-gate break;
37807c478bd9Sstevel@tonic-gate
37817c478bd9Sstevel@tonic-gate case TELOPT_AUTHENTICATION:
37827c478bd9Sstevel@tonic-gate settimer(authopt);
37837c478bd9Sstevel@tonic-gate auth_finished(0, AUTH_REJECT);
37847c478bd9Sstevel@tonic-gate if (auth_debug)
37857c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
37867c478bd9Sstevel@tonic-gate "RCVD WONT TELOPT_AUTHENTICATE\n");
37877c478bd9Sstevel@tonic-gate
37887c478bd9Sstevel@tonic-gate remopts[option] = OPT_NO;
37897c478bd9Sstevel@tonic-gate send_reply = 0;
37907c478bd9Sstevel@tonic-gate break;
37917c478bd9Sstevel@tonic-gate
37927c478bd9Sstevel@tonic-gate case TELOPT_ENCRYPT:
37937c478bd9Sstevel@tonic-gate if (enc_debug)
37947c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
37957c478bd9Sstevel@tonic-gate "RCVD IAC WONT TELOPT_ENCRYPT\n");
37967c478bd9Sstevel@tonic-gate settimer(encropt); /* got response to will/wont */
37977c478bd9Sstevel@tonic-gate /*
37987c478bd9Sstevel@tonic-gate * Remote side cannot send encryption. No reply necessary
37997c478bd9Sstevel@tonic-gate * Treat this as if "IAC SB ENCRYPT END IAC SE" were
38007c478bd9Sstevel@tonic-gate * received (RFC 2946) and disable crypto.
38017c478bd9Sstevel@tonic-gate */
38027c478bd9Sstevel@tonic-gate encrypt_end(TELNET_DIR_DECRYPT);
38037c478bd9Sstevel@tonic-gate send_reply = 0;
38047c478bd9Sstevel@tonic-gate break;
38057c478bd9Sstevel@tonic-gate }
38067c478bd9Sstevel@tonic-gate
38077c478bd9Sstevel@tonic-gate fmt = dont;
38087c478bd9Sstevel@tonic-gate remopts[option] = OPT_NO;
38097c478bd9Sstevel@tonic-gate if (send_reply) {
38107c478bd9Sstevel@tonic-gate write_data((const char *)fmt, option);
38117c478bd9Sstevel@tonic-gate }
38127c478bd9Sstevel@tonic-gate }
38137c478bd9Sstevel@tonic-gate
38147c478bd9Sstevel@tonic-gate /*
38157c478bd9Sstevel@tonic-gate * We received an "IAC DO ..." message from the client, change our state
38167c478bd9Sstevel@tonic-gate * to OPT_YES.
38177c478bd9Sstevel@tonic-gate */
38187c478bd9Sstevel@tonic-gate static void
dooption(int option)38197c478bd9Sstevel@tonic-gate dooption(int option)
38207c478bd9Sstevel@tonic-gate {
38217c478bd9Sstevel@tonic-gate uchar_t *fmt;
38227c478bd9Sstevel@tonic-gate boolean_t send_reply = B_TRUE;
38237c478bd9Sstevel@tonic-gate
38247c478bd9Sstevel@tonic-gate switch (option) {
38257c478bd9Sstevel@tonic-gate
38267c478bd9Sstevel@tonic-gate case TELOPT_TM:
38277c478bd9Sstevel@tonic-gate fmt = wont;
38287c478bd9Sstevel@tonic-gate break;
38297c478bd9Sstevel@tonic-gate
38307c478bd9Sstevel@tonic-gate case TELOPT_ECHO:
38317c478bd9Sstevel@tonic-gate mode(O_ECHO|O_CRMOD, 0);
38327c478bd9Sstevel@tonic-gate fmt = will;
38337c478bd9Sstevel@tonic-gate break;
38347c478bd9Sstevel@tonic-gate
38357c478bd9Sstevel@tonic-gate case TELOPT_BINARY:
38367c478bd9Sstevel@tonic-gate mode(O_RAW, 0);
38377c478bd9Sstevel@tonic-gate fmt = will;
38387c478bd9Sstevel@tonic-gate break;
38397c478bd9Sstevel@tonic-gate
38407c478bd9Sstevel@tonic-gate case TELOPT_SGA:
38417c478bd9Sstevel@tonic-gate fmt = will;
38427c478bd9Sstevel@tonic-gate break;
38437c478bd9Sstevel@tonic-gate
38447c478bd9Sstevel@tonic-gate case TELOPT_LOGOUT:
38457c478bd9Sstevel@tonic-gate /*
38467c478bd9Sstevel@tonic-gate * Options don't get much easier. Acknowledge the option,
38477c478bd9Sstevel@tonic-gate * and then clean up and exit.
38487c478bd9Sstevel@tonic-gate */
38497c478bd9Sstevel@tonic-gate write_data((const char *)will, option);
38507c478bd9Sstevel@tonic-gate netflush();
38517c478bd9Sstevel@tonic-gate cleanup(0);
38527c478bd9Sstevel@tonic-gate /*NOTREACHED*/
38537c478bd9Sstevel@tonic-gate
38547c478bd9Sstevel@tonic-gate case TELOPT_ENCRYPT:
38557c478bd9Sstevel@tonic-gate if (enc_debug)
38567c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "RCVD DO TELOPT_ENCRYPT\n");
38577c478bd9Sstevel@tonic-gate settimer(encropt);
38587c478bd9Sstevel@tonic-gate /*
38597c478bd9Sstevel@tonic-gate * We received a "DO". This indicates that the other side
38607c478bd9Sstevel@tonic-gate * wants us to encrypt our data (pending negotiatoin).
38617c478bd9Sstevel@tonic-gate * reply with "IAC WILL ENCRYPT" if we are able to send
38627c478bd9Sstevel@tonic-gate * encrypted data.
38637c478bd9Sstevel@tonic-gate */
38647c478bd9Sstevel@tonic-gate if (krb5_privacy_allowed() && negotiate_encrypt) {
38657c478bd9Sstevel@tonic-gate fmt = will;
38667c478bd9Sstevel@tonic-gate if (sent_will_encrypt)
38677c478bd9Sstevel@tonic-gate send_reply = B_FALSE;
38687c478bd9Sstevel@tonic-gate else
38697c478bd9Sstevel@tonic-gate sent_will_encrypt = B_TRUE;
38707c478bd9Sstevel@tonic-gate /* return if we already sent "WILL ENCRYPT" */
38717c478bd9Sstevel@tonic-gate if (myopts[option] == OPT_YES)
38727c478bd9Sstevel@tonic-gate return;
38737c478bd9Sstevel@tonic-gate } else {
38747c478bd9Sstevel@tonic-gate fmt = wont;
38757c478bd9Sstevel@tonic-gate }
38767c478bd9Sstevel@tonic-gate break;
38777c478bd9Sstevel@tonic-gate
38787c478bd9Sstevel@tonic-gate case TELOPT_AUTHENTICATION:
38797c478bd9Sstevel@tonic-gate if (auth_debug) {
38807c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
38817c478bd9Sstevel@tonic-gate "RCVD DO TELOPT_AUTHENTICATION\n");
38827c478bd9Sstevel@tonic-gate }
38837c478bd9Sstevel@tonic-gate /*
38847c478bd9Sstevel@tonic-gate * RFC 2941 - only the server can send
38857c478bd9Sstevel@tonic-gate * "DO TELOPT_AUTHENTICATION".
38867c478bd9Sstevel@tonic-gate * if a server receives this, it must respond with WONT...
38877c478bd9Sstevel@tonic-gate */
38887c478bd9Sstevel@tonic-gate fmt = wont;
38897c478bd9Sstevel@tonic-gate break;
38907c478bd9Sstevel@tonic-gate
38917c478bd9Sstevel@tonic-gate default:
38927c478bd9Sstevel@tonic-gate fmt = wont;
38937c478bd9Sstevel@tonic-gate break;
38947c478bd9Sstevel@tonic-gate }
38957c478bd9Sstevel@tonic-gate if (fmt == will) {
38967c478bd9Sstevel@tonic-gate myopts[option] = OPT_YES;
38977c478bd9Sstevel@tonic-gate } else {
38987c478bd9Sstevel@tonic-gate myopts[option] = OPT_NO;
38997c478bd9Sstevel@tonic-gate }
39007c478bd9Sstevel@tonic-gate if (send_reply) {
39017c478bd9Sstevel@tonic-gate write_data((const char *)fmt, option);
39027c478bd9Sstevel@tonic-gate netflush();
39037c478bd9Sstevel@tonic-gate }
39047c478bd9Sstevel@tonic-gate }
39057c478bd9Sstevel@tonic-gate
39067c478bd9Sstevel@tonic-gate /*
39077c478bd9Sstevel@tonic-gate * We received an "IAC DONT ..." message from client.
39087c478bd9Sstevel@tonic-gate * Client does not agree with the option so act accordingly.
39097c478bd9Sstevel@tonic-gate */
39107c478bd9Sstevel@tonic-gate static void
dontoption(int option)39117c478bd9Sstevel@tonic-gate dontoption(int option)
39127c478bd9Sstevel@tonic-gate {
39137c478bd9Sstevel@tonic-gate int send_reply = 1;
39147c478bd9Sstevel@tonic-gate switch (option) {
39157c478bd9Sstevel@tonic-gate case TELOPT_ECHO:
39167c478bd9Sstevel@tonic-gate /*
39177c478bd9Sstevel@tonic-gate * we should stop echoing, since the client side will be doing
39187c478bd9Sstevel@tonic-gate * it, but keep mapping CR since CR-LF will be mapped to it.
39197c478bd9Sstevel@tonic-gate */
39207c478bd9Sstevel@tonic-gate mode(0, O_ECHO);
39217c478bd9Sstevel@tonic-gate break;
39227c478bd9Sstevel@tonic-gate
39237c478bd9Sstevel@tonic-gate case TELOPT_ENCRYPT:
39247c478bd9Sstevel@tonic-gate if (enc_debug)
39257c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "RCVD IAC DONT ENCRYPT\n");
39267c478bd9Sstevel@tonic-gate settimer(encropt);
39277c478bd9Sstevel@tonic-gate /*
39287c478bd9Sstevel@tonic-gate * Remote side cannot receive any encrypted data,
39297c478bd9Sstevel@tonic-gate * so dont send any. No reply necessary.
39307c478bd9Sstevel@tonic-gate */
39317c478bd9Sstevel@tonic-gate send_reply = 0;
39327c478bd9Sstevel@tonic-gate break;
39337c478bd9Sstevel@tonic-gate
39347c478bd9Sstevel@tonic-gate default:
39357c478bd9Sstevel@tonic-gate break;
39367c478bd9Sstevel@tonic-gate }
39377c478bd9Sstevel@tonic-gate
39387c478bd9Sstevel@tonic-gate myopts[option] = OPT_NO;
39397c478bd9Sstevel@tonic-gate
39407c478bd9Sstevel@tonic-gate if (send_reply) {
39417c478bd9Sstevel@tonic-gate write_data((const char *)wont, option);
39427c478bd9Sstevel@tonic-gate }
39437c478bd9Sstevel@tonic-gate }
39447c478bd9Sstevel@tonic-gate
39457c478bd9Sstevel@tonic-gate /*
39467c478bd9Sstevel@tonic-gate * suboption()
39477c478bd9Sstevel@tonic-gate *
39487c478bd9Sstevel@tonic-gate * Look at the sub-option buffer, and try to be helpful to the other
39497c478bd9Sstevel@tonic-gate * side.
39507c478bd9Sstevel@tonic-gate *
39517c478bd9Sstevel@tonic-gate */
39527c478bd9Sstevel@tonic-gate static void
suboption(void)39537c478bd9Sstevel@tonic-gate suboption(void)
39547c478bd9Sstevel@tonic-gate {
39557c478bd9Sstevel@tonic-gate int subchar;
39567c478bd9Sstevel@tonic-gate
39577c478bd9Sstevel@tonic-gate switch (subchar = SB_GET()) {
39587c478bd9Sstevel@tonic-gate case TELOPT_TTYPE: { /* Yaaaay! */
39597c478bd9Sstevel@tonic-gate static char terminalname[5+41] = "TERM=";
39607c478bd9Sstevel@tonic-gate
39617c478bd9Sstevel@tonic-gate settimer(ttypesubopt);
39627c478bd9Sstevel@tonic-gate
39637c478bd9Sstevel@tonic-gate if (SB_GET() != TELQUAL_IS) {
39647c478bd9Sstevel@tonic-gate return; /* ??? XXX but, this is the most robust */
39657c478bd9Sstevel@tonic-gate }
39667c478bd9Sstevel@tonic-gate
39677c478bd9Sstevel@tonic-gate terminaltype = terminalname+strlen(terminalname);
39687c478bd9Sstevel@tonic-gate
39697c478bd9Sstevel@tonic-gate while (terminaltype < (terminalname + sizeof (terminalname) -
39707c478bd9Sstevel@tonic-gate 1) && !SB_EOF()) {
39717c478bd9Sstevel@tonic-gate int c;
39727c478bd9Sstevel@tonic-gate
39737c478bd9Sstevel@tonic-gate c = SB_GET();
39747c478bd9Sstevel@tonic-gate if (isupper(c)) {
39757c478bd9Sstevel@tonic-gate c = tolower(c);
39767c478bd9Sstevel@tonic-gate }
39777c478bd9Sstevel@tonic-gate *terminaltype++ = c; /* accumulate name */
39787c478bd9Sstevel@tonic-gate }
39797c478bd9Sstevel@tonic-gate *terminaltype = 0;
39807c478bd9Sstevel@tonic-gate terminaltype = terminalname;
39817c478bd9Sstevel@tonic-gate break;
39827c478bd9Sstevel@tonic-gate }
39837c478bd9Sstevel@tonic-gate
39847c478bd9Sstevel@tonic-gate case TELOPT_NAWS: {
39857c478bd9Sstevel@tonic-gate struct winsize ws;
39867c478bd9Sstevel@tonic-gate
39877c478bd9Sstevel@tonic-gate if (SB_EOF()) {
39887c478bd9Sstevel@tonic-gate return;
39897c478bd9Sstevel@tonic-gate }
39907c478bd9Sstevel@tonic-gate ws.ws_col = SB_GET() << 8;
39917c478bd9Sstevel@tonic-gate if (SB_EOF()) {
39927c478bd9Sstevel@tonic-gate return;
39937c478bd9Sstevel@tonic-gate }
39947c478bd9Sstevel@tonic-gate ws.ws_col |= SB_GET();
39957c478bd9Sstevel@tonic-gate if (SB_EOF()) {
39967c478bd9Sstevel@tonic-gate return;
39977c478bd9Sstevel@tonic-gate }
39987c478bd9Sstevel@tonic-gate ws.ws_row = SB_GET() << 8;
39997c478bd9Sstevel@tonic-gate if (SB_EOF()) {
40007c478bd9Sstevel@tonic-gate return;
40017c478bd9Sstevel@tonic-gate }
40027c478bd9Sstevel@tonic-gate ws.ws_row |= SB_GET();
40037c478bd9Sstevel@tonic-gate ws.ws_xpixel = 0; ws.ws_ypixel = 0;
40047c478bd9Sstevel@tonic-gate (void) ioctl(pty, TIOCSWINSZ, &ws);
40057c478bd9Sstevel@tonic-gate settimer(nawsopt);
40067c478bd9Sstevel@tonic-gate break;
40077c478bd9Sstevel@tonic-gate }
40087c478bd9Sstevel@tonic-gate
40097c478bd9Sstevel@tonic-gate case TELOPT_XDISPLOC: {
40107c478bd9Sstevel@tonic-gate if (SB_EOF() || SB_GET() != TELQUAL_IS) {
40117c478bd9Sstevel@tonic-gate return;
40127c478bd9Sstevel@tonic-gate }
40137c478bd9Sstevel@tonic-gate settimer(xdisplocsubopt);
40147c478bd9Sstevel@tonic-gate subpointer[SB_LEN()] = '\0';
40157c478bd9Sstevel@tonic-gate if ((new_env("DISPLAY", subpointer)) == 1)
40167c478bd9Sstevel@tonic-gate perror("malloc");
40177c478bd9Sstevel@tonic-gate break;
40187c478bd9Sstevel@tonic-gate }
40197c478bd9Sstevel@tonic-gate
40207c478bd9Sstevel@tonic-gate case TELOPT_NEW_ENVIRON:
40217c478bd9Sstevel@tonic-gate case TELOPT_OLD_ENVIRON: {
40227c478bd9Sstevel@tonic-gate int c;
40237c478bd9Sstevel@tonic-gate char *cp, *varp, *valp;
40247c478bd9Sstevel@tonic-gate
40257c478bd9Sstevel@tonic-gate if (SB_EOF())
40267c478bd9Sstevel@tonic-gate return;
40277c478bd9Sstevel@tonic-gate c = SB_GET();
40287c478bd9Sstevel@tonic-gate if (c == TELQUAL_IS) {
40297c478bd9Sstevel@tonic-gate if (subchar == TELOPT_OLD_ENVIRON)
40307c478bd9Sstevel@tonic-gate settimer(oenvironsubopt);
40317c478bd9Sstevel@tonic-gate else
40327c478bd9Sstevel@tonic-gate settimer(environsubopt);
40337c478bd9Sstevel@tonic-gate } else if (c != TELQUAL_INFO) {
40347c478bd9Sstevel@tonic-gate return;
40357c478bd9Sstevel@tonic-gate }
40367c478bd9Sstevel@tonic-gate
40377c478bd9Sstevel@tonic-gate if (subchar == TELOPT_NEW_ENVIRON) {
40387c478bd9Sstevel@tonic-gate while (!SB_EOF()) {
40397c478bd9Sstevel@tonic-gate c = SB_GET();
40407c478bd9Sstevel@tonic-gate if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
40417c478bd9Sstevel@tonic-gate break;
40427c478bd9Sstevel@tonic-gate }
40437c478bd9Sstevel@tonic-gate } else
40447c478bd9Sstevel@tonic-gate {
40457c478bd9Sstevel@tonic-gate while (!SB_EOF()) {
40467c478bd9Sstevel@tonic-gate c = SB_GET();
40477c478bd9Sstevel@tonic-gate if ((c == env_ovar) || (c == ENV_USERVAR))
40487c478bd9Sstevel@tonic-gate break;
40497c478bd9Sstevel@tonic-gate }
40507c478bd9Sstevel@tonic-gate }
40517c478bd9Sstevel@tonic-gate
40527c478bd9Sstevel@tonic-gate if (SB_EOF())
40537c478bd9Sstevel@tonic-gate return;
40547c478bd9Sstevel@tonic-gate
40557c478bd9Sstevel@tonic-gate cp = varp = (char *)subpointer;
40567c478bd9Sstevel@tonic-gate valp = 0;
40577c478bd9Sstevel@tonic-gate
40587c478bd9Sstevel@tonic-gate while (!SB_EOF()) {
40597c478bd9Sstevel@tonic-gate c = SB_GET();
40607c478bd9Sstevel@tonic-gate if (subchar == TELOPT_OLD_ENVIRON) {
40617c478bd9Sstevel@tonic-gate if (c == env_ovar)
40627c478bd9Sstevel@tonic-gate c = NEW_ENV_VAR;
40637c478bd9Sstevel@tonic-gate else if (c == env_ovalue)
40647c478bd9Sstevel@tonic-gate c = NEW_ENV_VALUE;
40657c478bd9Sstevel@tonic-gate }
40667c478bd9Sstevel@tonic-gate switch (c) {
40677c478bd9Sstevel@tonic-gate
40687c478bd9Sstevel@tonic-gate case NEW_ENV_VALUE:
40697c478bd9Sstevel@tonic-gate *cp = '\0';
40707c478bd9Sstevel@tonic-gate cp = valp = (char *)subpointer;
40717c478bd9Sstevel@tonic-gate break;
40727c478bd9Sstevel@tonic-gate
40737c478bd9Sstevel@tonic-gate case NEW_ENV_VAR:
40747c478bd9Sstevel@tonic-gate case ENV_USERVAR:
40757c478bd9Sstevel@tonic-gate *cp = '\0';
40767c478bd9Sstevel@tonic-gate if (valp) {
40777c478bd9Sstevel@tonic-gate if ((new_env(varp, valp)) == 1) {
40787c478bd9Sstevel@tonic-gate perror("malloc");
40797c478bd9Sstevel@tonic-gate }
40807c478bd9Sstevel@tonic-gate } else {
40817c478bd9Sstevel@tonic-gate (void) del_env(varp);
40827c478bd9Sstevel@tonic-gate }
40837c478bd9Sstevel@tonic-gate cp = varp = (char *)subpointer;
40847c478bd9Sstevel@tonic-gate valp = 0;
40857c478bd9Sstevel@tonic-gate break;
40867c478bd9Sstevel@tonic-gate
40877c478bd9Sstevel@tonic-gate case ENV_ESC:
40887c478bd9Sstevel@tonic-gate if (SB_EOF())
40897c478bd9Sstevel@tonic-gate break;
40907c478bd9Sstevel@tonic-gate c = SB_GET();
40917c478bd9Sstevel@tonic-gate /* FALL THROUGH */
40927c478bd9Sstevel@tonic-gate default:
40937c478bd9Sstevel@tonic-gate *cp++ = c;
40947c478bd9Sstevel@tonic-gate break;
40957c478bd9Sstevel@tonic-gate }
40967c478bd9Sstevel@tonic-gate }
40977c478bd9Sstevel@tonic-gate *cp = '\0';
40987c478bd9Sstevel@tonic-gate if (valp) {
40997c478bd9Sstevel@tonic-gate if ((new_env(varp, valp)) == 1) {
41007c478bd9Sstevel@tonic-gate perror("malloc");
41017c478bd9Sstevel@tonic-gate }
41027c478bd9Sstevel@tonic-gate } else {
41037c478bd9Sstevel@tonic-gate (void) del_env(varp);
41047c478bd9Sstevel@tonic-gate }
41057c478bd9Sstevel@tonic-gate break;
41067c478bd9Sstevel@tonic-gate } /* end of case TELOPT_NEW_ENVIRON */
41077c478bd9Sstevel@tonic-gate
41087c478bd9Sstevel@tonic-gate case TELOPT_AUTHENTICATION:
41097c478bd9Sstevel@tonic-gate if (SB_EOF())
41107c478bd9Sstevel@tonic-gate break;
41117c478bd9Sstevel@tonic-gate switch (SB_GET()) {
41127c478bd9Sstevel@tonic-gate case TELQUAL_SEND:
41137c478bd9Sstevel@tonic-gate case TELQUAL_REPLY:
41147c478bd9Sstevel@tonic-gate /*
41157c478bd9Sstevel@tonic-gate * These are sent server only and cannot be sent by the
41167c478bd9Sstevel@tonic-gate * client.
41177c478bd9Sstevel@tonic-gate */
41187c478bd9Sstevel@tonic-gate break;
41197c478bd9Sstevel@tonic-gate case TELQUAL_IS:
41207c478bd9Sstevel@tonic-gate if (auth_debug)
41217c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
41227c478bd9Sstevel@tonic-gate "RCVD AUTHENTICATION IS "
41237c478bd9Sstevel@tonic-gate "(%d bytes)\n",
41247c478bd9Sstevel@tonic-gate SB_LEN());
41257c478bd9Sstevel@tonic-gate if (!auth_negotiated)
41267c478bd9Sstevel@tonic-gate auth_is((uchar_t *)subpointer, SB_LEN());
41277c478bd9Sstevel@tonic-gate break;
41287c478bd9Sstevel@tonic-gate case TELQUAL_NAME:
41297c478bd9Sstevel@tonic-gate if (auth_debug)
41307c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
41317c478bd9Sstevel@tonic-gate "RCVD AUTHENTICATION NAME "
41327c478bd9Sstevel@tonic-gate "(%d bytes)\n",
41337c478bd9Sstevel@tonic-gate SB_LEN());
41347c478bd9Sstevel@tonic-gate if (!auth_negotiated)
41357c478bd9Sstevel@tonic-gate auth_name((uchar_t *)subpointer, SB_LEN());
41367c478bd9Sstevel@tonic-gate break;
41377c478bd9Sstevel@tonic-gate }
41387c478bd9Sstevel@tonic-gate break;
41397c478bd9Sstevel@tonic-gate
41407c478bd9Sstevel@tonic-gate case TELOPT_ENCRYPT: {
41417c478bd9Sstevel@tonic-gate int c;
41427c478bd9Sstevel@tonic-gate if (SB_EOF())
41437c478bd9Sstevel@tonic-gate break;
41447c478bd9Sstevel@tonic-gate c = SB_GET();
41457c478bd9Sstevel@tonic-gate #ifdef ENCRYPT_NAMES
41467c478bd9Sstevel@tonic-gate if (enc_debug)
41477c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "RCVD ENCRYPT %s\n",
41487c478bd9Sstevel@tonic-gate ENCRYPT_NAME(c));
41497c478bd9Sstevel@tonic-gate #endif /* ENCRYPT_NAMES */
41507c478bd9Sstevel@tonic-gate switch (c) {
41517c478bd9Sstevel@tonic-gate case ENCRYPT_SUPPORT:
41527c478bd9Sstevel@tonic-gate encrypt_support(subpointer, SB_LEN());
41537c478bd9Sstevel@tonic-gate break;
41547c478bd9Sstevel@tonic-gate case ENCRYPT_IS:
41557c478bd9Sstevel@tonic-gate encrypt_is((uchar_t *)subpointer, SB_LEN());
41567c478bd9Sstevel@tonic-gate break;
41577c478bd9Sstevel@tonic-gate case ENCRYPT_REPLY:
41587c478bd9Sstevel@tonic-gate (void) encrypt_reply(subpointer, SB_LEN());
41597c478bd9Sstevel@tonic-gate break;
41607c478bd9Sstevel@tonic-gate case ENCRYPT_START:
41617c478bd9Sstevel@tonic-gate encrypt_start();
41627c478bd9Sstevel@tonic-gate break;
41637c478bd9Sstevel@tonic-gate case ENCRYPT_END:
41647c478bd9Sstevel@tonic-gate encrypt_end(TELNET_DIR_DECRYPT);
41657c478bd9Sstevel@tonic-gate break;
41667c478bd9Sstevel@tonic-gate case ENCRYPT_REQSTART:
41677c478bd9Sstevel@tonic-gate encrypt_request_start();
41687c478bd9Sstevel@tonic-gate break;
41697c478bd9Sstevel@tonic-gate case ENCRYPT_REQEND:
41707c478bd9Sstevel@tonic-gate /*
41717c478bd9Sstevel@tonic-gate * We can always send an REQEND so that we cannot
41727c478bd9Sstevel@tonic-gate * get stuck encrypting. We should only get this
41737c478bd9Sstevel@tonic-gate * if we have been able to get in the correct mode
41747c478bd9Sstevel@tonic-gate * anyhow.
41757c478bd9Sstevel@tonic-gate */
41767c478bd9Sstevel@tonic-gate encrypt_request_end();
41777c478bd9Sstevel@tonic-gate break;
41787c478bd9Sstevel@tonic-gate case ENCRYPT_ENC_KEYID:
41797c478bd9Sstevel@tonic-gate encrypt_enc_keyid(subpointer, SB_LEN());
41807c478bd9Sstevel@tonic-gate break;
41817c478bd9Sstevel@tonic-gate case ENCRYPT_DEC_KEYID:
41827c478bd9Sstevel@tonic-gate encrypt_dec_keyid(subpointer, SB_LEN());
41837c478bd9Sstevel@tonic-gate break;
41847c478bd9Sstevel@tonic-gate default:
41857c478bd9Sstevel@tonic-gate break;
41867c478bd9Sstevel@tonic-gate }
41877c478bd9Sstevel@tonic-gate }
41887c478bd9Sstevel@tonic-gate break;
41897c478bd9Sstevel@tonic-gate
41907c478bd9Sstevel@tonic-gate default:
41917c478bd9Sstevel@tonic-gate break;
41927c478bd9Sstevel@tonic-gate }
41937c478bd9Sstevel@tonic-gate }
41947c478bd9Sstevel@tonic-gate
41957c478bd9Sstevel@tonic-gate static void
mode(int on,int off)41967c478bd9Sstevel@tonic-gate mode(int on, int off)
41977c478bd9Sstevel@tonic-gate {
41987c478bd9Sstevel@tonic-gate struct termios tios;
41997c478bd9Sstevel@tonic-gate
42007c478bd9Sstevel@tonic-gate ptyflush();
42017c478bd9Sstevel@tonic-gate if (tcgetattr(pty, &tios) < 0)
42027c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "tcgetattr: %m\n");
42037c478bd9Sstevel@tonic-gate
42047c478bd9Sstevel@tonic-gate if (on & O_RAW) {
42057c478bd9Sstevel@tonic-gate tios.c_cflag |= CS8;
42067c478bd9Sstevel@tonic-gate tios.c_iflag &= ~IUCLC;
42077c478bd9Sstevel@tonic-gate tios.c_lflag &= ~(XCASE|IEXTEN);
42087c478bd9Sstevel@tonic-gate }
42097c478bd9Sstevel@tonic-gate if (off & O_RAW) {
42107c478bd9Sstevel@tonic-gate if ((tios.c_cflag & PARENB) != 0)
42117c478bd9Sstevel@tonic-gate tios.c_cflag &= ~CS8;
42127c478bd9Sstevel@tonic-gate tios.c_lflag |= IEXTEN;
42137c478bd9Sstevel@tonic-gate }
42147c478bd9Sstevel@tonic-gate
42157c478bd9Sstevel@tonic-gate if (on & O_ECHO)
42167c478bd9Sstevel@tonic-gate tios.c_lflag |= ECHO;
42177c478bd9Sstevel@tonic-gate if (off & O_ECHO)
42187c478bd9Sstevel@tonic-gate tios.c_lflag &= ~ECHO;
42197c478bd9Sstevel@tonic-gate
42207c478bd9Sstevel@tonic-gate if (on & O_CRMOD) {
42217c478bd9Sstevel@tonic-gate tios.c_iflag |= ICRNL;
42227c478bd9Sstevel@tonic-gate tios.c_oflag |= ONLCR;
42237c478bd9Sstevel@tonic-gate }
42247c478bd9Sstevel@tonic-gate /*
42257c478bd9Sstevel@tonic-gate * Because "O_CRMOD" will never be set in "off" we don't have to
42267c478bd9Sstevel@tonic-gate * handle this case here.
42277c478bd9Sstevel@tonic-gate */
42287c478bd9Sstevel@tonic-gate
42297c478bd9Sstevel@tonic-gate if (tcsetattr(pty, TCSANOW, &tios) < 0)
42307c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "tcsetattr: %m\n");
42317c478bd9Sstevel@tonic-gate }
42327c478bd9Sstevel@tonic-gate
42337c478bd9Sstevel@tonic-gate /*
42347c478bd9Sstevel@tonic-gate * Send interrupt to process on other side of pty.
42357c478bd9Sstevel@tonic-gate * If it is in raw mode, just write NULL;
42367c478bd9Sstevel@tonic-gate * otherwise, write intr char.
42377c478bd9Sstevel@tonic-gate */
42387c478bd9Sstevel@tonic-gate static void
interrupt(void)42397c478bd9Sstevel@tonic-gate interrupt(void)
42407c478bd9Sstevel@tonic-gate {
42417c478bd9Sstevel@tonic-gate struct sgttyb b;
42427c478bd9Sstevel@tonic-gate struct tchars tchars;
42437c478bd9Sstevel@tonic-gate
42447c478bd9Sstevel@tonic-gate ptyflush(); /* half-hearted */
42457c478bd9Sstevel@tonic-gate if (ioctl(pty, TIOCGETP, &b) == -1)
42467c478bd9Sstevel@tonic-gate syslog(LOG_INFO, "ioctl TIOCGETP: %m\n");
42477c478bd9Sstevel@tonic-gate if (b.sg_flags & O_RAW) {
42487c478bd9Sstevel@tonic-gate *pfrontp++ = '\0';
42497c478bd9Sstevel@tonic-gate return;
42507c478bd9Sstevel@tonic-gate }
42517c478bd9Sstevel@tonic-gate *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
42527c478bd9Sstevel@tonic-gate '\177' : tchars.t_intrc;
42537c478bd9Sstevel@tonic-gate }
42547c478bd9Sstevel@tonic-gate
42557c478bd9Sstevel@tonic-gate /*
42567c478bd9Sstevel@tonic-gate * Send quit to process on other side of pty.
42577c478bd9Sstevel@tonic-gate * If it is in raw mode, just write NULL;
42587c478bd9Sstevel@tonic-gate * otherwise, write quit char.
42597c478bd9Sstevel@tonic-gate */
42607c478bd9Sstevel@tonic-gate static void
sendbrk(void)42617c478bd9Sstevel@tonic-gate sendbrk(void)
42627c478bd9Sstevel@tonic-gate {
42637c478bd9Sstevel@tonic-gate struct sgttyb b;
42647c478bd9Sstevel@tonic-gate struct tchars tchars;
42657c478bd9Sstevel@tonic-gate
42667c478bd9Sstevel@tonic-gate ptyflush(); /* half-hearted */
42677c478bd9Sstevel@tonic-gate (void) ioctl(pty, TIOCGETP, &b);
42687c478bd9Sstevel@tonic-gate if (b.sg_flags & O_RAW) {
42697c478bd9Sstevel@tonic-gate *pfrontp++ = '\0';
42707c478bd9Sstevel@tonic-gate return;
42717c478bd9Sstevel@tonic-gate }
42727c478bd9Sstevel@tonic-gate *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
42737c478bd9Sstevel@tonic-gate '\034' : tchars.t_quitc;
42747c478bd9Sstevel@tonic-gate }
42757c478bd9Sstevel@tonic-gate
42767c478bd9Sstevel@tonic-gate static void
ptyflush(void)42777c478bd9Sstevel@tonic-gate ptyflush(void)
42787c478bd9Sstevel@tonic-gate {
42797c478bd9Sstevel@tonic-gate int n;
42807c478bd9Sstevel@tonic-gate
42817c478bd9Sstevel@tonic-gate if ((n = pfrontp - pbackp) > 0)
42827c478bd9Sstevel@tonic-gate n = write(master, pbackp, n);
42837c478bd9Sstevel@tonic-gate if (n < 0)
42847c478bd9Sstevel@tonic-gate return;
42857c478bd9Sstevel@tonic-gate pbackp += n;
42867c478bd9Sstevel@tonic-gate if (pbackp == pfrontp)
42877c478bd9Sstevel@tonic-gate pbackp = pfrontp = ptyobuf;
42887c478bd9Sstevel@tonic-gate }
42897c478bd9Sstevel@tonic-gate
42907c478bd9Sstevel@tonic-gate /*
42917c478bd9Sstevel@tonic-gate * nextitem()
42927c478bd9Sstevel@tonic-gate *
42937c478bd9Sstevel@tonic-gate * Return the address of the next "item" in the TELNET data
42947c478bd9Sstevel@tonic-gate * stream. This will be the address of the next character if
42957c478bd9Sstevel@tonic-gate * the current address is a user data character, or it will
42967c478bd9Sstevel@tonic-gate * be the address of the character following the TELNET command
42977c478bd9Sstevel@tonic-gate * if the current address is a TELNET IAC ("I Am a Command")
42987c478bd9Sstevel@tonic-gate * character.
42997c478bd9Sstevel@tonic-gate */
43007c478bd9Sstevel@tonic-gate
43017c478bd9Sstevel@tonic-gate static char *
nextitem(char * current)43027c478bd9Sstevel@tonic-gate nextitem(char *current)
43037c478bd9Sstevel@tonic-gate {
43047c478bd9Sstevel@tonic-gate if ((*current&0xff) != IAC) {
43057c478bd9Sstevel@tonic-gate return (current+1);
43067c478bd9Sstevel@tonic-gate }
43077c478bd9Sstevel@tonic-gate switch (*(current+1)&0xff) {
43087c478bd9Sstevel@tonic-gate case DO:
43097c478bd9Sstevel@tonic-gate case DONT:
43107c478bd9Sstevel@tonic-gate case WILL:
43117c478bd9Sstevel@tonic-gate case WONT:
43127c478bd9Sstevel@tonic-gate return (current+3);
43137c478bd9Sstevel@tonic-gate case SB: /* loop forever looking for the SE */
43147c478bd9Sstevel@tonic-gate {
43157c478bd9Sstevel@tonic-gate char *look = current+2;
43167c478bd9Sstevel@tonic-gate
43177c478bd9Sstevel@tonic-gate for (;;) {
43187c478bd9Sstevel@tonic-gate if ((*look++&0xff) == IAC) {
43197c478bd9Sstevel@tonic-gate if ((*look++&0xff) == SE) {
43207c478bd9Sstevel@tonic-gate return (look);
43217c478bd9Sstevel@tonic-gate }
43227c478bd9Sstevel@tonic-gate }
43237c478bd9Sstevel@tonic-gate }
43247c478bd9Sstevel@tonic-gate }
43257c478bd9Sstevel@tonic-gate default:
43267c478bd9Sstevel@tonic-gate return (current+2);
43277c478bd9Sstevel@tonic-gate }
43287c478bd9Sstevel@tonic-gate }
43297c478bd9Sstevel@tonic-gate
43307c478bd9Sstevel@tonic-gate
43317c478bd9Sstevel@tonic-gate /*
43327c478bd9Sstevel@tonic-gate * netclear()
43337c478bd9Sstevel@tonic-gate *
43347c478bd9Sstevel@tonic-gate * We are about to do a TELNET SYNCH operation. Clear
43357c478bd9Sstevel@tonic-gate * the path to the network.
43367c478bd9Sstevel@tonic-gate *
43377c478bd9Sstevel@tonic-gate * Things are a bit tricky since we may have sent the first
43387c478bd9Sstevel@tonic-gate * byte or so of a previous TELNET command into the network.
43397c478bd9Sstevel@tonic-gate * So, we have to scan the network buffer from the beginning
43407c478bd9Sstevel@tonic-gate * until we are up to where we want to be.
43417c478bd9Sstevel@tonic-gate *
43427c478bd9Sstevel@tonic-gate * A side effect of what we do, just to keep things
43437c478bd9Sstevel@tonic-gate * simple, is to clear the urgent data pointer. The principal
43447c478bd9Sstevel@tonic-gate * caller should be setting the urgent data pointer AFTER calling
43457c478bd9Sstevel@tonic-gate * us in any case.
43467c478bd9Sstevel@tonic-gate */
43477c478bd9Sstevel@tonic-gate static void
netclear(void)43487c478bd9Sstevel@tonic-gate netclear(void)
43497c478bd9Sstevel@tonic-gate {
43507c478bd9Sstevel@tonic-gate char *thisitem, *next;
43517c478bd9Sstevel@tonic-gate char *good;
43527c478bd9Sstevel@tonic-gate #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
43537c478bd9Sstevel@tonic-gate ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
43547c478bd9Sstevel@tonic-gate
43557c478bd9Sstevel@tonic-gate thisitem = netobuf;
43567c478bd9Sstevel@tonic-gate
43577c478bd9Sstevel@tonic-gate while ((next = nextitem(thisitem)) <= nbackp) {
43587c478bd9Sstevel@tonic-gate thisitem = next;
43597c478bd9Sstevel@tonic-gate }
43607c478bd9Sstevel@tonic-gate
43617c478bd9Sstevel@tonic-gate /* Now, thisitem is first before/at boundary. */
43627c478bd9Sstevel@tonic-gate
43637c478bd9Sstevel@tonic-gate good = netobuf; /* where the good bytes go */
43647c478bd9Sstevel@tonic-gate
43657c478bd9Sstevel@tonic-gate while (nfrontp > thisitem) {
43667c478bd9Sstevel@tonic-gate if (wewant(thisitem)) {
43677c478bd9Sstevel@tonic-gate int length;
43687c478bd9Sstevel@tonic-gate
43697c478bd9Sstevel@tonic-gate next = thisitem;
43707c478bd9Sstevel@tonic-gate do {
43717c478bd9Sstevel@tonic-gate next = nextitem(next);
43727c478bd9Sstevel@tonic-gate } while (wewant(next) && (nfrontp > next));
43737c478bd9Sstevel@tonic-gate length = next-thisitem;
43747c478bd9Sstevel@tonic-gate (void) memmove(good, thisitem, length);
43757c478bd9Sstevel@tonic-gate good += length;
43767c478bd9Sstevel@tonic-gate thisitem = next;
43777c478bd9Sstevel@tonic-gate } else {
43787c478bd9Sstevel@tonic-gate thisitem = nextitem(thisitem);
43797c478bd9Sstevel@tonic-gate }
43807c478bd9Sstevel@tonic-gate }
43817c478bd9Sstevel@tonic-gate
43827c478bd9Sstevel@tonic-gate nbackp = netobuf;
43837c478bd9Sstevel@tonic-gate nfrontp = good; /* next byte to be sent */
43847c478bd9Sstevel@tonic-gate neturg = 0;
43857c478bd9Sstevel@tonic-gate }
43867c478bd9Sstevel@tonic-gate
43877c478bd9Sstevel@tonic-gate
43887c478bd9Sstevel@tonic-gate /*
43897c478bd9Sstevel@tonic-gate * netflush
43907c478bd9Sstevel@tonic-gate * Send as much data as possible to the network,
43917c478bd9Sstevel@tonic-gate * handling requests for urgent data.
43927c478bd9Sstevel@tonic-gate */
43937c478bd9Sstevel@tonic-gate static void
netflush(void)43947c478bd9Sstevel@tonic-gate netflush(void)
43957c478bd9Sstevel@tonic-gate {
43967c478bd9Sstevel@tonic-gate int n;
43977c478bd9Sstevel@tonic-gate
43987c478bd9Sstevel@tonic-gate if ((n = nfrontp - nbackp) > 0) {
43997c478bd9Sstevel@tonic-gate /*
44007c478bd9Sstevel@tonic-gate * if no urgent data, or if the other side appears to be an
44017c478bd9Sstevel@tonic-gate * old 4.2 client (and thus unable to survive TCP urgent data),
44027c478bd9Sstevel@tonic-gate * write the entire buffer in non-OOB mode.
44037c478bd9Sstevel@tonic-gate */
44047c478bd9Sstevel@tonic-gate if ((neturg == 0) || (not42 == 0)) {
44057c478bd9Sstevel@tonic-gate n = write(net, nbackp, n); /* normal write */
44067c478bd9Sstevel@tonic-gate } else {
44077c478bd9Sstevel@tonic-gate n = neturg - nbackp;
44087c478bd9Sstevel@tonic-gate /*
44097c478bd9Sstevel@tonic-gate * In 4.2 (and 4.3) systems, there is some question
44107c478bd9Sstevel@tonic-gate * about what byte in a sendOOB operation is the "OOB"
44117c478bd9Sstevel@tonic-gate * data. To make ourselves compatible, we only send ONE
44127c478bd9Sstevel@tonic-gate * byte out of band, the one WE THINK should be OOB
44137c478bd9Sstevel@tonic-gate * (though we really have more the TCP philosophy of
44147c478bd9Sstevel@tonic-gate * urgent data rather than the Unix philosophy of OOB
44157c478bd9Sstevel@tonic-gate * data).
44167c478bd9Sstevel@tonic-gate */
44177c478bd9Sstevel@tonic-gate if (n > 1) {
44187c478bd9Sstevel@tonic-gate /* send URGENT all by itself */
44197c478bd9Sstevel@tonic-gate n = write(net, nbackp, n-1);
44207c478bd9Sstevel@tonic-gate } else {
44217c478bd9Sstevel@tonic-gate /* URGENT data */
44227c478bd9Sstevel@tonic-gate n = send_oob(net, nbackp, n);
44237c478bd9Sstevel@tonic-gate }
44247c478bd9Sstevel@tonic-gate }
44257c478bd9Sstevel@tonic-gate }
44267c478bd9Sstevel@tonic-gate if (n < 0) {
44277c478bd9Sstevel@tonic-gate if (errno == EWOULDBLOCK)
44287c478bd9Sstevel@tonic-gate return;
44297c478bd9Sstevel@tonic-gate /* should blow this guy away... */
44307c478bd9Sstevel@tonic-gate return;
44317c478bd9Sstevel@tonic-gate }
44327c478bd9Sstevel@tonic-gate
44337c478bd9Sstevel@tonic-gate nbackp += n;
44347c478bd9Sstevel@tonic-gate
44357c478bd9Sstevel@tonic-gate if (nbackp >= neturg) {
44367c478bd9Sstevel@tonic-gate neturg = 0;
44377c478bd9Sstevel@tonic-gate }
44387c478bd9Sstevel@tonic-gate if (nbackp == nfrontp) {
44397c478bd9Sstevel@tonic-gate nbackp = nfrontp = netobuf;
44407c478bd9Sstevel@tonic-gate }
44417c478bd9Sstevel@tonic-gate }
44427c478bd9Sstevel@tonic-gate
44437c478bd9Sstevel@tonic-gate /* ARGSUSED */
44447c478bd9Sstevel@tonic-gate static void
cleanup(int signum)44457c478bd9Sstevel@tonic-gate cleanup(int signum)
44467c478bd9Sstevel@tonic-gate {
44477c478bd9Sstevel@tonic-gate /*
44487c478bd9Sstevel@tonic-gate * If the TEL_IOC_ENABLE ioctl hasn't completed, then we need to
44497c478bd9Sstevel@tonic-gate * handle closing differently. We close "net" first and then
44507c478bd9Sstevel@tonic-gate * "master" in that order. We do close(net) first because
44517c478bd9Sstevel@tonic-gate * we have no other way to disconnect forwarding between the network
44527c478bd9Sstevel@tonic-gate * and master. So by issuing the close()'s we ensure that no further
44537c478bd9Sstevel@tonic-gate * data rises from TCP. A more complex fix would be adding proper
44547c478bd9Sstevel@tonic-gate * support for throwing a "stop" switch for forwarding data between
44557c478bd9Sstevel@tonic-gate * logindmux peers. It's possible to block in the close of the tty
44567c478bd9Sstevel@tonic-gate * while the network still receives data and the telmod module is
44577c478bd9Sstevel@tonic-gate * TEL_STOPPED. A denial-of-service attack generates this case,
44587c478bd9Sstevel@tonic-gate * see 4102102.
44597c478bd9Sstevel@tonic-gate */
44607c478bd9Sstevel@tonic-gate
44617c478bd9Sstevel@tonic-gate if (!telmod_init_done) {
44627c478bd9Sstevel@tonic-gate (void) close(net);
44637c478bd9Sstevel@tonic-gate (void) close(master);
44647c478bd9Sstevel@tonic-gate }
44657c478bd9Sstevel@tonic-gate rmut();
44667c478bd9Sstevel@tonic-gate
44677c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE);
44687c478bd9Sstevel@tonic-gate }
44697c478bd9Sstevel@tonic-gate
44707c478bd9Sstevel@tonic-gate static void
rmut(void)44717c478bd9Sstevel@tonic-gate rmut(void)
44727c478bd9Sstevel@tonic-gate {
44737c478bd9Sstevel@tonic-gate pam_handle_t *pamh;
44747c478bd9Sstevel@tonic-gate struct utmpx *up;
44757c478bd9Sstevel@tonic-gate char user[sizeof (up->ut_user) + 1];
44767c478bd9Sstevel@tonic-gate char ttyn[sizeof (up->ut_line) + 1];
44777c478bd9Sstevel@tonic-gate char rhost[sizeof (up->ut_host) + 1];
44787c478bd9Sstevel@tonic-gate
44797c478bd9Sstevel@tonic-gate /* while cleaning up don't allow disruption */
44807c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN);
44817c478bd9Sstevel@tonic-gate
44827c478bd9Sstevel@tonic-gate setutxent();
44837c478bd9Sstevel@tonic-gate while (up = getutxent()) {
44847c478bd9Sstevel@tonic-gate if (up->ut_pid == pid) {
44857c478bd9Sstevel@tonic-gate if (up->ut_type == DEAD_PROCESS) {
44867c478bd9Sstevel@tonic-gate /*
44877c478bd9Sstevel@tonic-gate * Cleaned up elsewhere.
44887c478bd9Sstevel@tonic-gate */
44897c478bd9Sstevel@tonic-gate break;
44907c478bd9Sstevel@tonic-gate }
44917c478bd9Sstevel@tonic-gate
44927c478bd9Sstevel@tonic-gate /*
44937c478bd9Sstevel@tonic-gate * call pam_close_session if login changed
44947c478bd9Sstevel@tonic-gate * the utmpx user entry from type LOGIN_PROCESS
44957c478bd9Sstevel@tonic-gate * to type USER_PROCESS, which happens
44967c478bd9Sstevel@tonic-gate * after pam_open_session is called.
44977c478bd9Sstevel@tonic-gate */
44987c478bd9Sstevel@tonic-gate if (up->ut_type == USER_PROCESS) {
44997c478bd9Sstevel@tonic-gate (void) strlcpy(user, up->ut_user,
45007c478bd9Sstevel@tonic-gate sizeof (user));
45017c478bd9Sstevel@tonic-gate (void) strlcpy(ttyn, up->ut_line,
45027c478bd9Sstevel@tonic-gate sizeof (ttyn));
45037c478bd9Sstevel@tonic-gate (void) strlcpy(rhost, up->ut_host,
45047c478bd9Sstevel@tonic-gate sizeof (rhost));
45057c478bd9Sstevel@tonic-gate if ((pam_start("telnet", user, NULL, &pamh)) ==
45067c478bd9Sstevel@tonic-gate PAM_SUCCESS) {
45077c478bd9Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_TTY,
45087c478bd9Sstevel@tonic-gate ttyn);
45097c478bd9Sstevel@tonic-gate (void) pam_set_item(pamh, PAM_RHOST,
45107c478bd9Sstevel@tonic-gate rhost);
45117c478bd9Sstevel@tonic-gate (void) pam_close_session(pamh, 0);
45127c478bd9Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS);
45137c478bd9Sstevel@tonic-gate }
45147c478bd9Sstevel@tonic-gate }
45157c478bd9Sstevel@tonic-gate
45167c478bd9Sstevel@tonic-gate up->ut_type = DEAD_PROCESS;
45177c478bd9Sstevel@tonic-gate up->ut_exit.e_termination = WTERMSIG(0);
45187c478bd9Sstevel@tonic-gate up->ut_exit.e_exit = WEXITSTATUS(0);
45197c478bd9Sstevel@tonic-gate (void) time(&up->ut_tv.tv_sec);
45207c478bd9Sstevel@tonic-gate
45217c478bd9Sstevel@tonic-gate if (modutx(up) == NULL) {
45227c478bd9Sstevel@tonic-gate /*
45237c478bd9Sstevel@tonic-gate * Since modutx failed we'll
45247c478bd9Sstevel@tonic-gate * write out the new entry
45257c478bd9Sstevel@tonic-gate * ourselves.
45267c478bd9Sstevel@tonic-gate */
45277c478bd9Sstevel@tonic-gate (void) pututxline(up);
45287c478bd9Sstevel@tonic-gate updwtmpx("wtmpx", up);
45297c478bd9Sstevel@tonic-gate }
45307c478bd9Sstevel@tonic-gate break;
45317c478bd9Sstevel@tonic-gate }
45327c478bd9Sstevel@tonic-gate }
45337c478bd9Sstevel@tonic-gate
45347c478bd9Sstevel@tonic-gate endutxent();
45357c478bd9Sstevel@tonic-gate
45367c478bd9Sstevel@tonic-gate (void) signal(SIGCHLD, (void (*)())cleanup);
45377c478bd9Sstevel@tonic-gate }
45387c478bd9Sstevel@tonic-gate
45397c478bd9Sstevel@tonic-gate static int
readstream(int fd,char * buf,int offset)45407c478bd9Sstevel@tonic-gate readstream(int fd, char *buf, int offset)
45417c478bd9Sstevel@tonic-gate {
45427c478bd9Sstevel@tonic-gate struct strbuf ctlbuf, datbuf;
45437c478bd9Sstevel@tonic-gate union T_primitives tpi;
45447c478bd9Sstevel@tonic-gate int ret = 0;
45457c478bd9Sstevel@tonic-gate int flags = 0;
45467c478bd9Sstevel@tonic-gate int bytes_avail, count;
45477c478bd9Sstevel@tonic-gate
45487c478bd9Sstevel@tonic-gate (void) memset((char *)&ctlbuf, 0, sizeof (ctlbuf));
45497c478bd9Sstevel@tonic-gate (void) memset((char *)&datbuf, 0, sizeof (datbuf));
45507c478bd9Sstevel@tonic-gate
45517c478bd9Sstevel@tonic-gate ctlbuf.buf = (char *)&tpi;
45527c478bd9Sstevel@tonic-gate ctlbuf.maxlen = sizeof (tpi);
45537c478bd9Sstevel@tonic-gate
45547c478bd9Sstevel@tonic-gate if (ioctl(fd, I_NREAD, &bytes_avail) < 0) {
45557c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "I_NREAD returned error %m");
45567c478bd9Sstevel@tonic-gate return (-1);
45577c478bd9Sstevel@tonic-gate }
45587c478bd9Sstevel@tonic-gate if (bytes_avail > netibufsize - offset) {
45597c478bd9Sstevel@tonic-gate count = netip - netibuf;
45607c478bd9Sstevel@tonic-gate netibuf = (char *)realloc(netibuf,
45617c478bd9Sstevel@tonic-gate (unsigned)netibufsize + bytes_avail);
45627c478bd9Sstevel@tonic-gate if (netibuf == NULL) {
45637c478bd9Sstevel@tonic-gate fatal(net, "netibuf realloc failed\n");
45647c478bd9Sstevel@tonic-gate }
45657c478bd9Sstevel@tonic-gate netibufsize += bytes_avail;
45667c478bd9Sstevel@tonic-gate netip = netibuf + count;
45677c478bd9Sstevel@tonic-gate buf = netibuf;
45687c478bd9Sstevel@tonic-gate }
45697c478bd9Sstevel@tonic-gate datbuf.buf = buf + offset;
45707c478bd9Sstevel@tonic-gate datbuf.maxlen = netibufsize;
45717c478bd9Sstevel@tonic-gate ret = getmsg(fd, &ctlbuf, &datbuf, &flags);
45727c478bd9Sstevel@tonic-gate if (ret < 0) {
45737c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "getmsg returned -1, errno %d\n",
45747c478bd9Sstevel@tonic-gate errno);
45757c478bd9Sstevel@tonic-gate return (-1);
45767c478bd9Sstevel@tonic-gate }
45777c478bd9Sstevel@tonic-gate if (ctlbuf.len <= 0) {
45787c478bd9Sstevel@tonic-gate return (datbuf.len);
45797c478bd9Sstevel@tonic-gate }
45807c478bd9Sstevel@tonic-gate
45817c478bd9Sstevel@tonic-gate if (tpi.type == T_DATA_REQ) {
45827c478bd9Sstevel@tonic-gate return (0);
45837c478bd9Sstevel@tonic-gate }
45847c478bd9Sstevel@tonic-gate
45857c478bd9Sstevel@tonic-gate if ((tpi.type == T_ORDREL_IND) || (tpi.type == T_DISCON_IND))
45867c478bd9Sstevel@tonic-gate cleanup(0);
45877c478bd9Sstevel@tonic-gate fatal(fd, "no data or protocol element recognized");
4588740638c8Sbw return (0);
45897c478bd9Sstevel@tonic-gate }
45907c478bd9Sstevel@tonic-gate
45917c478bd9Sstevel@tonic-gate static void
drainstream(int size)45927c478bd9Sstevel@tonic-gate drainstream(int size)
45937c478bd9Sstevel@tonic-gate {
45947c478bd9Sstevel@tonic-gate int nbytes;
45957c478bd9Sstevel@tonic-gate int tsize;
45967c478bd9Sstevel@tonic-gate
45977c478bd9Sstevel@tonic-gate tsize = netip - netibuf;
45987c478bd9Sstevel@tonic-gate
45997c478bd9Sstevel@tonic-gate if ((tsize + ncc + size) > netibufsize) {
46007c478bd9Sstevel@tonic-gate if (!(netibuf = (char *)realloc(netibuf,
46017c478bd9Sstevel@tonic-gate (unsigned)tsize + ncc + size)))
46027c478bd9Sstevel@tonic-gate fatalperror(net, "netibuf realloc failed\n", errno);
46037c478bd9Sstevel@tonic-gate netibufsize = tsize + ncc + size;
46047c478bd9Sstevel@tonic-gate
46057c478bd9Sstevel@tonic-gate netip = netibuf + tsize;
46067c478bd9Sstevel@tonic-gate }
46077c478bd9Sstevel@tonic-gate
46087c478bd9Sstevel@tonic-gate if ((nbytes = read(net, (char *)netip + ncc, size)) != size)
46097c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "read %d bytes\n", nbytes);
46107c478bd9Sstevel@tonic-gate }
46117c478bd9Sstevel@tonic-gate
46127c478bd9Sstevel@tonic-gate /*
46137c478bd9Sstevel@tonic-gate * TPI style replacement for socket send() primitive, so we don't require
46147c478bd9Sstevel@tonic-gate * sockmod to be on the stream.
46157c478bd9Sstevel@tonic-gate */
46167c478bd9Sstevel@tonic-gate static int
send_oob(int fd,char * ptr,int count)46177c478bd9Sstevel@tonic-gate send_oob(int fd, char *ptr, int count)
46187c478bd9Sstevel@tonic-gate {
46197c478bd9Sstevel@tonic-gate struct T_exdata_req exd_req;
46207c478bd9Sstevel@tonic-gate struct strbuf hdr, dat;
46217c478bd9Sstevel@tonic-gate int ret;
46227c478bd9Sstevel@tonic-gate
46237c478bd9Sstevel@tonic-gate exd_req.PRIM_type = T_EXDATA_REQ;
46247c478bd9Sstevel@tonic-gate exd_req.MORE_flag = 0;
46257c478bd9Sstevel@tonic-gate
46267c478bd9Sstevel@tonic-gate hdr.buf = (char *)&exd_req;
46277c478bd9Sstevel@tonic-gate hdr.len = sizeof (exd_req);
46287c478bd9Sstevel@tonic-gate
46297c478bd9Sstevel@tonic-gate dat.buf = ptr;
46307c478bd9Sstevel@tonic-gate dat.len = count;
46317c478bd9Sstevel@tonic-gate
46327c478bd9Sstevel@tonic-gate ret = putmsg(fd, &hdr, &dat, 0);
46337c478bd9Sstevel@tonic-gate if (ret == 0) {
46347c478bd9Sstevel@tonic-gate ret = count;
46357c478bd9Sstevel@tonic-gate }
46367c478bd9Sstevel@tonic-gate return (ret);
46377c478bd9Sstevel@tonic-gate }
46387c478bd9Sstevel@tonic-gate
46397c478bd9Sstevel@tonic-gate
46407c478bd9Sstevel@tonic-gate /*
46417c478bd9Sstevel@tonic-gate * local_setenv --
46427c478bd9Sstevel@tonic-gate * Set the value of the environmental variable "name" to be
46437c478bd9Sstevel@tonic-gate * "value". If rewrite is set, replace any current value.
46447c478bd9Sstevel@tonic-gate */
46457c478bd9Sstevel@tonic-gate static int
local_setenv(const char * name,const char * value,int rewrite)46467c478bd9Sstevel@tonic-gate local_setenv(const char *name, const char *value, int rewrite)
46477c478bd9Sstevel@tonic-gate {
46487c478bd9Sstevel@tonic-gate static int alloced; /* if allocated space before */
46497c478bd9Sstevel@tonic-gate char *c;
46507c478bd9Sstevel@tonic-gate int l_value, offset;
46517c478bd9Sstevel@tonic-gate
46527c478bd9Sstevel@tonic-gate /*
46537c478bd9Sstevel@tonic-gate * Do not allow environment variables which begin with LD_ to be
46547c478bd9Sstevel@tonic-gate * inserted into the environment. While normally the dynamic linker
46557c478bd9Sstevel@tonic-gate * protects the login program, that is based on the assumption hostile
46567c478bd9Sstevel@tonic-gate * invocation of login are from non-root users. However, since telnetd
46577c478bd9Sstevel@tonic-gate * runs as root, this cannot be utilized. So instead we simply
46587c478bd9Sstevel@tonic-gate * prevent LD_* from being inserted into the environment.
46597c478bd9Sstevel@tonic-gate * This also applies to other environment variables that
46607c478bd9Sstevel@tonic-gate * are to be ignored in setugid apps.
46617c478bd9Sstevel@tonic-gate * Note that at this point name can contain '='!
46627c478bd9Sstevel@tonic-gate * Also, do not allow TTYPROMPT to be passed along here.
46637c478bd9Sstevel@tonic-gate */
46647c478bd9Sstevel@tonic-gate if (strncmp(name, "LD_", 3) == 0 ||
46657c478bd9Sstevel@tonic-gate strncmp(name, "NLSPATH", 7) == 0 ||
46667c478bd9Sstevel@tonic-gate (strncmp(name, "TTYPROMPT", 9) == 0 &&
46677c478bd9Sstevel@tonic-gate (name[9] == '\0' || name[9] == '='))) {
46687c478bd9Sstevel@tonic-gate return (-1);
46697c478bd9Sstevel@tonic-gate }
46707c478bd9Sstevel@tonic-gate if (*value == '=') /* no `=' in value */
46717c478bd9Sstevel@tonic-gate ++value;
46727c478bd9Sstevel@tonic-gate l_value = strlen(value);
46737c478bd9Sstevel@tonic-gate if ((c = __findenv(name, &offset))) { /* find if already exists */
46747c478bd9Sstevel@tonic-gate if (!rewrite)
46757c478bd9Sstevel@tonic-gate return (0);
46767c478bd9Sstevel@tonic-gate if ((int)strlen(c) >= l_value) { /* old larger; copy over */
4677*0b4fd3b1SSurya Prakki while (*c++ = *value++)
4678*0b4fd3b1SSurya Prakki ;
46797c478bd9Sstevel@tonic-gate return (0);
46807c478bd9Sstevel@tonic-gate }
46817c478bd9Sstevel@tonic-gate } else { /* create new slot */
46827c478bd9Sstevel@tonic-gate int cnt;
46837c478bd9Sstevel@tonic-gate char **p;
46847c478bd9Sstevel@tonic-gate
4685*0b4fd3b1SSurya Prakki for (p = environ, cnt = 0; *p; ++p, ++cnt)
4686*0b4fd3b1SSurya Prakki ;
46877c478bd9Sstevel@tonic-gate if (alloced) { /* just increase size */
46887c478bd9Sstevel@tonic-gate environ = (char **)realloc((char *)environ,
46897c478bd9Sstevel@tonic-gate (size_t)(sizeof (char *) * (cnt + 2)));
46907c478bd9Sstevel@tonic-gate if (!environ)
46917c478bd9Sstevel@tonic-gate return (-1);
46927c478bd9Sstevel@tonic-gate } else { /* get new space */
46937c478bd9Sstevel@tonic-gate alloced = 1; /* copy old entries into it */
46947c478bd9Sstevel@tonic-gate p = (char **)malloc((size_t)(sizeof (char *)*
46957c478bd9Sstevel@tonic-gate (cnt + 2)));
46967c478bd9Sstevel@tonic-gate if (!p)
46977c478bd9Sstevel@tonic-gate return (-1);
46987c478bd9Sstevel@tonic-gate (void) memcpy(p, environ, cnt * sizeof (char *));
46997c478bd9Sstevel@tonic-gate environ = p;
47007c478bd9Sstevel@tonic-gate }
47017c478bd9Sstevel@tonic-gate environ[cnt + 1] = NULL;
47027c478bd9Sstevel@tonic-gate offset = cnt;
47037c478bd9Sstevel@tonic-gate }
4704*0b4fd3b1SSurya Prakki for (c = (char *)name; *c && *c != '='; ++c) /* no `=' in name */
4705*0b4fd3b1SSurya Prakki ;
47067c478bd9Sstevel@tonic-gate if (!(environ[offset] = /* name + `=' + value */
47077c478bd9Sstevel@tonic-gate malloc((size_t)((int)(c - name) + l_value + 2))))
47087c478bd9Sstevel@tonic-gate return (-1);
4709*0b4fd3b1SSurya Prakki for (c = environ[offset]; ((*c = *name++) != 0) && (*c != '='); ++c)
4710*0b4fd3b1SSurya Prakki ;
4711*0b4fd3b1SSurya Prakki for (*c++ = '='; *c++ = *value++; )
4712*0b4fd3b1SSurya Prakki ;
47137c478bd9Sstevel@tonic-gate return (0);
47147c478bd9Sstevel@tonic-gate }
47157c478bd9Sstevel@tonic-gate
47167c478bd9Sstevel@tonic-gate /*
47177c478bd9Sstevel@tonic-gate * local_unsetenv(name) --
47187c478bd9Sstevel@tonic-gate * Delete environmental variable "name".
47197c478bd9Sstevel@tonic-gate */
47207c478bd9Sstevel@tonic-gate static void
local_unsetenv(const char * name)47217c478bd9Sstevel@tonic-gate local_unsetenv(const char *name)
47227c478bd9Sstevel@tonic-gate {
47237c478bd9Sstevel@tonic-gate char **p;
47247c478bd9Sstevel@tonic-gate int offset;
47257c478bd9Sstevel@tonic-gate
47267c478bd9Sstevel@tonic-gate while (__findenv(name, &offset)) /* if set multiple times */
47277c478bd9Sstevel@tonic-gate for (p = &environ[offset]; ; ++p)
47287c478bd9Sstevel@tonic-gate if ((*p = *(p + 1)) == 0)
47297c478bd9Sstevel@tonic-gate break;
47307c478bd9Sstevel@tonic-gate }
47317c478bd9Sstevel@tonic-gate
47327c478bd9Sstevel@tonic-gate /*
47337c478bd9Sstevel@tonic-gate * __findenv --
47347c478bd9Sstevel@tonic-gate * Returns pointer to value associated with name, if any, else NULL.
47357c478bd9Sstevel@tonic-gate * Sets offset to be the offset of the name/value combination in the
47367c478bd9Sstevel@tonic-gate * environmental array, for use by local_setenv() and local_unsetenv().
47377c478bd9Sstevel@tonic-gate * Explicitly removes '=' in argument name.
47387c478bd9Sstevel@tonic-gate */
47397c478bd9Sstevel@tonic-gate static char *
__findenv(const char * name,int * offset)47407c478bd9Sstevel@tonic-gate __findenv(const char *name, int *offset)
47417c478bd9Sstevel@tonic-gate {
47427c478bd9Sstevel@tonic-gate extern char **environ;
47437c478bd9Sstevel@tonic-gate int len;
47447c478bd9Sstevel@tonic-gate const char *np;
47457c478bd9Sstevel@tonic-gate char **p, *c;
47467c478bd9Sstevel@tonic-gate
47477c478bd9Sstevel@tonic-gate if (name == NULL || environ == NULL)
47487c478bd9Sstevel@tonic-gate return (NULL);
47497c478bd9Sstevel@tonic-gate for (np = name; *np && *np != '='; ++np)
47507c478bd9Sstevel@tonic-gate continue;
47517c478bd9Sstevel@tonic-gate len = np - name;
47527c478bd9Sstevel@tonic-gate for (p = environ; (c = *p) != NULL; ++p)
47537c478bd9Sstevel@tonic-gate if (strncmp(c, name, len) == 0 && c[len] == '=') {
47547c478bd9Sstevel@tonic-gate *offset = p - environ;
47557c478bd9Sstevel@tonic-gate return (c + len + 1);
47567c478bd9Sstevel@tonic-gate }
47577c478bd9Sstevel@tonic-gate return (NULL);
47587c478bd9Sstevel@tonic-gate }
47597c478bd9Sstevel@tonic-gate
47607c478bd9Sstevel@tonic-gate static void
showbanner(void)47617c478bd9Sstevel@tonic-gate showbanner(void)
47627c478bd9Sstevel@tonic-gate {
47637c478bd9Sstevel@tonic-gate char *cp;
47647c478bd9Sstevel@tonic-gate char evalbuf[BUFSIZ];
47657c478bd9Sstevel@tonic-gate
47667c478bd9Sstevel@tonic-gate if (defopen(defaultfile) == 0) {
47677c478bd9Sstevel@tonic-gate int flags;
47687c478bd9Sstevel@tonic-gate
47697c478bd9Sstevel@tonic-gate /* ignore case */
47707c478bd9Sstevel@tonic-gate flags = defcntl(DC_GETFLAGS, 0);
47717c478bd9Sstevel@tonic-gate TURNOFF(flags, DC_CASE);
477206e1a714Sraf (void) defcntl(DC_SETFLAGS, flags);
47737c478bd9Sstevel@tonic-gate if (cp = defread(bannervar)) {
47747c478bd9Sstevel@tonic-gate FILE *fp;
47757c478bd9Sstevel@tonic-gate
47767c478bd9Sstevel@tonic-gate if (strlen(cp) + strlen("eval echo '") + strlen("'\n")
47777c478bd9Sstevel@tonic-gate + 1 < sizeof (evalbuf)) {
47787c478bd9Sstevel@tonic-gate (void) strlcpy(evalbuf, "eval echo '",
47797c478bd9Sstevel@tonic-gate sizeof (evalbuf));
47807c478bd9Sstevel@tonic-gate (void) strlcat(evalbuf, cp, sizeof (evalbuf));
47817c478bd9Sstevel@tonic-gate (void) strlcat(evalbuf, "'\n",
47827c478bd9Sstevel@tonic-gate sizeof (evalbuf));
47837c478bd9Sstevel@tonic-gate
47847c478bd9Sstevel@tonic-gate if (fp = popen(evalbuf, "r")) {
47857c478bd9Sstevel@tonic-gate char buf[BUFSIZ];
47867c478bd9Sstevel@tonic-gate size_t size;
47877c478bd9Sstevel@tonic-gate
47887c478bd9Sstevel@tonic-gate /*
47897c478bd9Sstevel@tonic-gate * Pipe I/O atomicity guarantees we
47907c478bd9Sstevel@tonic-gate * need only one read.
47917c478bd9Sstevel@tonic-gate */
47927c478bd9Sstevel@tonic-gate if ((size = fread(buf, 1,
47937c478bd9Sstevel@tonic-gate sizeof (buf) - 1,
47947c478bd9Sstevel@tonic-gate fp)) != 0) {
47957c478bd9Sstevel@tonic-gate char *p;
47967c478bd9Sstevel@tonic-gate buf[size] = '\0';
47977c478bd9Sstevel@tonic-gate p = strrchr(buf, '\n');
47987c478bd9Sstevel@tonic-gate if (p != NULL)
47997c478bd9Sstevel@tonic-gate *p = '\0';
48007c478bd9Sstevel@tonic-gate if (strlen(buf)) {
48017c478bd9Sstevel@tonic-gate map_banner(buf);
48027c478bd9Sstevel@tonic-gate netflush();
48037c478bd9Sstevel@tonic-gate }
48047c478bd9Sstevel@tonic-gate }
48057c478bd9Sstevel@tonic-gate (void) pclose(fp);
48067c478bd9Sstevel@tonic-gate /* close default file */
48077c478bd9Sstevel@tonic-gate (void) defopen(NULL);
48087c478bd9Sstevel@tonic-gate return;
48097c478bd9Sstevel@tonic-gate }
48107c478bd9Sstevel@tonic-gate }
48117c478bd9Sstevel@tonic-gate }
48127c478bd9Sstevel@tonic-gate (void) defopen(NULL); /* close default file */
48137c478bd9Sstevel@tonic-gate }
48147c478bd9Sstevel@tonic-gate
48157c478bd9Sstevel@tonic-gate defbanner();
48167c478bd9Sstevel@tonic-gate netflush();
48177c478bd9Sstevel@tonic-gate }
48187c478bd9Sstevel@tonic-gate
48197c478bd9Sstevel@tonic-gate static void
map_banner(char * p)48207c478bd9Sstevel@tonic-gate map_banner(char *p)
48217c478bd9Sstevel@tonic-gate {
48227c478bd9Sstevel@tonic-gate char *q;
48237c478bd9Sstevel@tonic-gate
48247c478bd9Sstevel@tonic-gate /*
48257c478bd9Sstevel@tonic-gate * Map the banner: "\n" -> "\r\n" and "\r" -> "\r\0"
48267c478bd9Sstevel@tonic-gate */
48277c478bd9Sstevel@tonic-gate for (q = nfrontp; p && *p && q < nfrontp + sizeof (netobuf) - 1; )
48287c478bd9Sstevel@tonic-gate if (*p == '\n') {
48297c478bd9Sstevel@tonic-gate *q++ = '\r';
48307c478bd9Sstevel@tonic-gate *q++ = '\n';
48317c478bd9Sstevel@tonic-gate p++;
48327c478bd9Sstevel@tonic-gate } else if (*p == '\r') {
48337c478bd9Sstevel@tonic-gate *q++ = '\r';
48347c478bd9Sstevel@tonic-gate *q++ = '\0';
48357c478bd9Sstevel@tonic-gate p++;
48367c478bd9Sstevel@tonic-gate } else
48377c478bd9Sstevel@tonic-gate *q++ = *p++;
48387c478bd9Sstevel@tonic-gate
48397c478bd9Sstevel@tonic-gate nfrontp += q - netobuf;
48407c478bd9Sstevel@tonic-gate }
48417c478bd9Sstevel@tonic-gate
48427c478bd9Sstevel@tonic-gate /*
48437c478bd9Sstevel@tonic-gate * Show banner that getty never gave. By default, this is `uname -sr`.
48447c478bd9Sstevel@tonic-gate *
48457c478bd9Sstevel@tonic-gate * The banner includes some null's (for TELNET CR disambiguation),
48467c478bd9Sstevel@tonic-gate * so we have to be somewhat complicated.
48477c478bd9Sstevel@tonic-gate */
48487c478bd9Sstevel@tonic-gate static void
defbanner(void)48497c478bd9Sstevel@tonic-gate defbanner(void)
48507c478bd9Sstevel@tonic-gate {
48517c478bd9Sstevel@tonic-gate struct utsname u;
48527c478bd9Sstevel@tonic-gate
48537c478bd9Sstevel@tonic-gate /*
48547c478bd9Sstevel@tonic-gate * Dont show this if the '-h' option was present
48557c478bd9Sstevel@tonic-gate */
48567c478bd9Sstevel@tonic-gate if (!show_hostinfo)
48577c478bd9Sstevel@tonic-gate return;
48587c478bd9Sstevel@tonic-gate
48597c478bd9Sstevel@tonic-gate if (uname(&u) == -1)
48607c478bd9Sstevel@tonic-gate return;
48617c478bd9Sstevel@tonic-gate
48627c478bd9Sstevel@tonic-gate write_data_len((const char *) BANNER1, sizeof (BANNER1) - 1);
48637c478bd9Sstevel@tonic-gate write_data_len(u.sysname, strlen(u.sysname));
48647c478bd9Sstevel@tonic-gate write_data_len(" ", 1);
48657c478bd9Sstevel@tonic-gate write_data_len(u.release, strlen(u.release));
48667c478bd9Sstevel@tonic-gate write_data_len((const char *)BANNER2, sizeof (BANNER2) - 1);
48677c478bd9Sstevel@tonic-gate }
48687c478bd9Sstevel@tonic-gate
48697c478bd9Sstevel@tonic-gate /*
48707c478bd9Sstevel@tonic-gate * Verify that the named module is at the top of the stream
48717c478bd9Sstevel@tonic-gate * and then pop it off.
48727c478bd9Sstevel@tonic-gate */
48737c478bd9Sstevel@tonic-gate static int
removemod(int f,char * modname)48747c478bd9Sstevel@tonic-gate removemod(int f, char *modname)
48757c478bd9Sstevel@tonic-gate {
48767c478bd9Sstevel@tonic-gate char topmodname[BUFSIZ];
48777c478bd9Sstevel@tonic-gate
48787c478bd9Sstevel@tonic-gate if (ioctl(f, I_LOOK, topmodname) < 0)
48797c478bd9Sstevel@tonic-gate return (-1);
48807c478bd9Sstevel@tonic-gate if (strcmp(modname, topmodname) != 0) {
48817c478bd9Sstevel@tonic-gate errno = ENXIO;
48827c478bd9Sstevel@tonic-gate return (-1);
48837c478bd9Sstevel@tonic-gate }
48847c478bd9Sstevel@tonic-gate if (ioctl(f, I_POP, 0) < 0)
48857c478bd9Sstevel@tonic-gate return (-1);
48867c478bd9Sstevel@tonic-gate return (0);
48877c478bd9Sstevel@tonic-gate }
48887c478bd9Sstevel@tonic-gate
48897c478bd9Sstevel@tonic-gate static void
write_data(const char * format,...)48907c478bd9Sstevel@tonic-gate write_data(const char *format, ...)
48917c478bd9Sstevel@tonic-gate {
48927c478bd9Sstevel@tonic-gate va_list args;
48937c478bd9Sstevel@tonic-gate int len;
48947c478bd9Sstevel@tonic-gate char argp[BUFSIZ];
48957c478bd9Sstevel@tonic-gate
48967c478bd9Sstevel@tonic-gate va_start(args, format);
48977c478bd9Sstevel@tonic-gate
48987c478bd9Sstevel@tonic-gate if ((len = vsnprintf(argp, sizeof (argp), format, args)) == -1)
48997c478bd9Sstevel@tonic-gate return;
49007c478bd9Sstevel@tonic-gate
49017c478bd9Sstevel@tonic-gate write_data_len(argp, len);
49027c478bd9Sstevel@tonic-gate va_end(args);
49037c478bd9Sstevel@tonic-gate }
49047c478bd9Sstevel@tonic-gate
49057c478bd9Sstevel@tonic-gate static void
write_data_len(const char * buf,int len)49067c478bd9Sstevel@tonic-gate write_data_len(const char *buf, int len)
49077c478bd9Sstevel@tonic-gate {
49087c478bd9Sstevel@tonic-gate int remaining, copied;
49097c478bd9Sstevel@tonic-gate
49107c478bd9Sstevel@tonic-gate remaining = BUFSIZ - (nfrontp - netobuf);
49117c478bd9Sstevel@tonic-gate while (len > 0) {
49127c478bd9Sstevel@tonic-gate /*
49137c478bd9Sstevel@tonic-gate * If there's not enough space in netobuf then
49147c478bd9Sstevel@tonic-gate * try to make some.
49157c478bd9Sstevel@tonic-gate */
49167c478bd9Sstevel@tonic-gate if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
49177c478bd9Sstevel@tonic-gate netflush();
49187c478bd9Sstevel@tonic-gate remaining = BUFSIZ - (nfrontp - netobuf);
49197c478bd9Sstevel@tonic-gate }
49207c478bd9Sstevel@tonic-gate /* Copy as much as we can */
49217c478bd9Sstevel@tonic-gate copied = remaining > len ? len : remaining;
49227c478bd9Sstevel@tonic-gate (void) memmove(nfrontp, buf, copied);
49237c478bd9Sstevel@tonic-gate nfrontp += copied;
49247c478bd9Sstevel@tonic-gate len -= copied;
49257c478bd9Sstevel@tonic-gate remaining -= copied;
49267c478bd9Sstevel@tonic-gate buf += copied;
49277c478bd9Sstevel@tonic-gate }
49287c478bd9Sstevel@tonic-gate }
4929