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