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