📄 rlogind.c
字号:
/* Copyright (C) 1998,2001, 2002 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Inetutils; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <signal.h>#ifdef HAVE_SYS_FILIO_H# include <sys/filio.h>#endif#include <termios.h>#ifdef TIME_WITH_SYS_TIME# include <sys/time.h># include <time.h>#else# ifdef HAVE_SYS_TIME_H# include <sys/time.h># else# include <time.h># endif#endif#ifdef HAVE_SYS_STREAM_H# include <sys/stream.h>#endif#ifdef HAVE_SYS_TTY_H# include <sys/tty.h>#endif#ifdef HAVE_SYS_PTYVAR_H# include <sys/ptyvar.h>#endif#include <sys/wait.h>#include <sys/socket.h>#include <netinet/in.h>#ifdef HAVE_NETINET_IN_SYSTM_H# include <netinet/in_systm.h>#endif#ifdef HAVE_NETINET_IP_H# include <netinet/ip.h>#endif#include <arpa/inet.h>#include <netdb.h>#include <pwd.h>#include <syslog.h>#include <errno.h>#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <stdlib.h>#include <string.h>#include <getopt.h>#ifdef HAVE_SYS_SELECT_H# include <sys/select.h>#endif#include <sys/ioctl.h>#include <sys/stat.h> /* Needed for chmod() *//* The TIOCPKT_* macros may not be implemented in the pty driver. Defining them here allows the program to be compiled. */#ifndef TIOCPKT# define TIOCPKT _IOW('t', 112, int)# define TIOCPKT_FLUSHWRITE 0x02# define TIOCPKT_NOSTOP 0x10# define TIOCPKT_DOSTOP 0x20#endif /*TIOCPKT*/#ifndef TIOCPKT_WINDOW# define TIOCPKT_WINDOW 0x80#endif/* `defaults' for tty settings. */#ifndef TTYDEF_IFLAG#define TTYDEF_IFLAG (BRKINT | ISTRIP | ICRNL | IMAXBEL | IXON | IXANY)#endif#ifndef TTYDEF_OFLAG#ifndef OXTABS#define OXTABS 0#endif#define TTYDEF_OFLAG (OPOST | ONLCR | OXTABS)#endif#ifndef TTYDEF_LFLAG#define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)#endif#define AUTH_KERBEROS_4 4#define AUTH_KERBEROS_5 5#ifdef KERBEROS# define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n"# ifdef KERBEROS_IV# include <kerberosIV/des.h># include <kerberosIV/krb.h># define kerberos_error_string(c) krb_err_txt[c]# define AUTH_KERBEROS_DEFAULT AUTH_KERBEROS_4# elif defined(KERBEROS_V)# include <krb5.h># include <kerberosIV/krb.h># define kerberos_error_string(c) error_message (c)# define AUTH_KERBEROS_DEFAULT AUTH_KERBEROS_5# endif#endif /* KERBEROS */#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */#ifndef DEFMAXCHILDREN# define DEFMAXCHILDREN 10 /* Default maximum number of children */#endif#ifndef DEFPORT# define DEFPORT 513#endifextern int __check_rhosts_file;struct auth_data{ struct sockaddr_in from; char *hostname; char *lusername; char *rusername; char *term; char *env[2];#ifdef KERBEROS#ifdef KERBEROS_V int kerberos_version; krb5_principal client; krb5_context context; krb5_ccache ccache; krb5_keytab keytab;#endif#endif};static const char *short_options = "aD::d::hk::L:lnp:orxV";static struct option long_options[] ={ {"allow-root", no_argument, 0, 'o'}, {"verify-hostname", no_argument, 0, 'a'}, {"daemon", optional_argument, 0, 'd'}, {"no-rhosts", no_argument, 0, 'l'}, {"no-keepalive", no_argument, 0, 'n'}, {"local-domain", required_argument, 0, 'L'}, {"kerberos", optional_argument, 0, 'k'}, {"encrypt", no_argument, 0, 'x'}, {"debug", optional_argument, 0, 'D'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"port", required_argument, 0, 'p'}, {"reverse-required", no_argument, 0, 'r'}, {0, 0, 0, 0}};int allow_root = 0;int verify_hostname = 0;int keepalive = 1;#ifdef KERBEROSint kerberos = 0;#ifdef ENCRYPTIONint encrypt_io = 0;#endif /* ENCRYPTION */#endif /* KERBEROS */int reverse_required = 0;int debug_level = 0;int numchildren;int netf;char line[1024]; /* FIXME */int confirmed;const char *path_login = PATH_LOGIN;char *local_domain_name;int local_dot_count;struct winsize win = {0, 0, 0, 0};void usage __P ((void));void rlogin_daemon __P ((int maxchildren, int port));int rlogind_auth __P ((int fd, struct auth_data *ap));void setup_tty __P ((int fd, struct auth_data *ap));void exec_login __P ((int authenticated, struct auth_data *ap));int rlogind_mainloop __P ((int infd, int outfd));int do_rlogin __P ((int infd, struct auth_data *ap));int do_krb_login __P ((int infd, struct auth_data *ap, const char **msg));void getstr __P ((int infd, char **ptr, const char *prefix));void protocol __P ((int f, int p));int control __P ((int pty, char *cp, size_t n));RETSIGTYPE cleanup __P ((int signo));void fatal __P ((int f, const char *msg, int syserr));int in_local_domain __P ((char *hostname));char *topdomain __P ((char *name, int max_dots));RETSIGTYPErlogind_sigchld (int sig){ pid_t pid; int status; while ((pid = waitpid (-1, &status, WNOHANG)) > 0) --numchildren; signal (sig, rlogind_sigchld);}#define MODE_INETD 0#define MODE_DAEMON 1#if defined(KERBEROS) && defined(ENCRYPTION)# define ENCRYPT_IO encrypt_io# define IF_ENCRYPT(stmt) if (encrypt_io) stmt# define IF_NOT_ENCRYPT(stmt) if (!encrypt_io) stmt# define ENC_READ(c, fd, buf, size) \ if (encrypt_io) \ c = des_read(fd, buf, size); \ else \ c = read(fd, buf, size);# define ENC_WRITE(c, fd, buf, size) \ if (encrypt_io) \ c = des_write(fd, buf, size); \ else \ c = write(fd, buf, size);#else# define ENCRYPT_IO 0# define IF_ENCRYPT(stmt)# define IF_NOT_ENCRYPT(stmt) stmt# define ENC_READ(c, fd, buf, size) c = read (fd, buf, size)# define ENC_WRITE(c, fd, buf, size) c = write (fd, buf, size)#endifintmain (int argc, char *argv[]){ int port = 0; int maxchildren = DEFMAXCHILDREN; int mode = MODE_INETD; int c; while ((c = getopt_long (argc, argv, short_options, long_options, NULL)) != EOF) { switch (c) { case 'a': verify_hostname = 1; break; case 'D': if (optarg) debug_level = strtoul (optarg, NULL, 10); break; case 'd': mode = MODE_DAEMON; if (optarg) maxchildren = strtoul (optarg, NULL, 10); if (maxchildren == 0) maxchildren = DEFMAXCHILDREN; break; case 'l': __check_rhosts_file = 0; /* FIXME: extern var? */ break; case 'L': local_domain_name = optarg; break; case 'n': keepalive = 0; break;#ifdef KERBEROS case 'k': if (optarg) { if (*optarg == '4') kerberos = AUTH_KERBEROS_4; else if (*optarg == '5') kerberos = AUTH_KERBEROS_5; } else kerberos = AUTH_KERBEROS_DEFAULT; break;# ifdef ENCRYPTION case 'x': encrypt_io = 1; break;# endif /* ENCRYPTION */#endif /* KERBEROS */ case 'o': allow_root = 1; break; case 'p': port = strtoul (optarg, NULL, 10); break; case 'r': reverse_required = 1; break; case 'V': printf ("rlogind (%s %s)\n", PACKAGE_NAME, PACKAGE_VERSION); exit (0); case 'h': default: usage (); exit (0); } } openlog ("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); argc -= optind; if (argc > 0) { syslog (LOG_ERR, "%d extra arguments", argc); exit (1); } signal (SIGHUP, SIG_IGN); if (!local_domain_name) { char *p = localhost (); if (!p) { syslog (LOG_ERR, "can't determine local hostname"); exit (1); } local_dot_count = 2; local_domain_name = topdomain (p, local_dot_count); } else { char *p; local_dot_count = 0; for (p = local_domain_name; *p; p++) if (*p == '.') local_dot_count++; } if (mode == MODE_DAEMON) rlogin_daemon (maxchildren, port); else exit (rlogind_mainloop (fileno (stdin), fileno (stdout))); /* To pacify lint */ return 0;}voidrlogin_daemon (int maxchildren, int port){ pid_t pid; size_t size; struct sockaddr_in saddr; int listenfd, fd; if (port == 0) { struct servent *svp; svp = getservbyname ("login", "tcp"); if (svp != NULL) port = ntohs (svp->s_port); else port = DEFPORT; } /* Become a daemon. Take care to close inherited fds and to hold first three one, lest master/slave ptys clash with standard in,out,err */ if (daemon (0, 0) < 0) { syslog (LOG_ERR, "failed to become a daemon %s", strerror (errno)); fatal (fileno (stderr), "fork failed, exiting", 0); } signal (SIGCHLD, rlogind_sigchld); listenfd = socket (AF_INET, SOCK_STREAM, 0); if (listenfd == -1) { syslog (LOG_ERR, "socket: %s", strerror (errno)); exit (1); } { int on = 1; setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on); } size = sizeof saddr; memset (&saddr, 0, size); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl (INADDR_ANY); saddr.sin_port = htons (port); size = sizeof saddr; if (bind (listenfd, (struct sockaddr *)&saddr, size) == -1) { syslog (LOG_ERR, "bind: %s", strerror (errno)); exit (1); } if (listen (listenfd, 128) == -1) { syslog (LOG_ERR, "listen: %s", strerror (errno)); exit (1); } while (1) { if (numchildren > maxchildren) { syslog (LOG_ERR, "too many children (%d)", numchildren); pause (); continue; } size = sizeof saddr; fd = accept (listenfd, (struct sockaddr *)&saddr, &size); if (fd == -1) { if (errno == EINTR) continue; syslog (LOG_ERR, "accept: %s", strerror (errno)); exit (1); } pid = fork (); if (pid == -1) syslog (LOG_ERR, "fork: %s", strerror (errno)); else if (pid == 0) /* child */ { close (listenfd); exit (rlogind_mainloop (fd, fd)); } else numchildren++; close (fd); }}intrlogind_auth (int fd, struct auth_data *ap){ struct hostent *hp; char *hostname; int authenticated = 0; confirmed = 0; /* Check the remote host name */ hp = gethostbyaddr ((char *) &ap->from.sin_addr, sizeof (struct in_addr), ap->from.sin_family); if (hp) hostname = hp->h_name; else if (reverse_required) { syslog (LOG_CRIT, "can't resolve remote IP address"); exit (1); } else hostname = inet_ntoa (ap->from.sin_addr); ap->hostname = strdup (hostname); if (verify_hostname || in_local_domain (ap->hostname)) { int match = 0; for (hp = gethostbyname (ap->hostname); hp && !match; hp->h_addr_list++) { if (hp->h_addr_list[0] == NULL) break; match = memcmp (hp->h_addr_list[0], &ap->from.sin_addr, sizeof (ap->from.sin_addr)) == 0; } if (!match) { syslog (LOG_ERR | LOG_AUTH, "cannot find matching IP for %s (%s)", ap->hostname, inet_ntoa (ap->from.sin_addr)); fatal (fd, "Permission denied", 0); } }#ifdef KERBEROS if (kerberos) { const char *err_msg; int c = 0; if (do_krb_login (fd, ap, &err_msg) == 0) authenticated++; else fatal (fd, err_msg, 0); write (fd, &c, 1); confirmed = 1; /* we sent the null! */ } else#endif { int port = ntohs(ap->from.sin_port); if (ap->from.sin_family != AF_INET || port >= IPPORT_RESERVED || port < IPPORT_RESERVED / 2) { syslog (LOG_NOTICE, "Connection from %s on illegal port %d", inet_ntoa (ap->from.sin_addr), port); fatal (fd, "Permission denied", 0); }#ifdef IP_OPTIONS { u_char optbuf[BUFSIZ / 3], *cp; char lbuf[BUFSIZ], *lp; int optsize = sizeof (optbuf), ipproto; struct protoent *ip; if ((ip = getprotobyname ("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; if (getsockopt (0, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) == 0 && optsize != 0) { lp = lbuf; for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) sprintf (lp, " %2.2x", *cp); syslog (LOG_NOTICE, "Ignoring IP options: %s", lbuf); if (setsockopt (0, ipproto, IP_OPTIONS, (char *) NULL, optsize)) { syslog (LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); exit (1); } } }#endif /* IP_OPTIONS */ if (do_rlogin (fd, ap) == 0) authenticated++; } if (confirmed == 0) { write (fd, "", 1); confirmed = 1; /* we sent the null! */ } IF_ENCRYPT (des_write (fd, SECURE_MESSAGE, sizeof (SECURE_MESSAGE) - 1)); return authenticated;}voidsetup_tty (int fd, struct auth_data *ap){ register char *cp = strchr (ap->term + ENVSIZE, '/'); char *speed; struct termios tt; tcgetattr (fd, &tt); if (cp) { *cp++ = '\0'; speed = cp; cp = strchr (speed, '/'); if (cp) *cp++ = '\0';#ifdef HAVE_CFSETSPEED cfsetspeed (&tt, strtoul (speed, NULL, 10));#else cfsetispeed (&tt, strtoul (speed, NULL, 10)); cfsetospeed (&tt, strtoul (speed, NULL, 10));#endif } tt.c_iflag = TTYDEF_IFLAG; tt.c_oflag = TTYDEF_OFLAG; tt.c_lflag = TTYDEF_LFLAG; tcsetattr (fd, TCSAFLUSH, &tt); ap->env[0] = ap->term; ap->env[1] = 0;}#ifdef UTMPXchar *utmp_ptsid (); /*FIXME*/void utmp_init ();voidsetup_utmp (char *line){ char *ut_id = utmp_ptsid (line, "rl"); utmp_init (line + sizeof ("/dev/") - 1, ".rlogin", ut_id);}#else# define setup_utmp(line)#endifvoidexec_login(int authenticated, struct auth_data *ap){ if (authenticated) {#ifdef SOLARIS execle (path_login, "login", "-p", "-h", ap->hostname, ap->term, "-f", "--", ap->lusername, NULL, ap->env);#else execle (path_login, "login", "-p", "-h", ap->hostname, "-f", ap->lusername, NULL, ap->env);#endif } else {#ifdef SOLARIS execle (path_login, "login", "-p", "-h", ap->hostname, ap->term, "--", ap->lusername, NULL, ap->env);#else execle (path_login, "login", "-p", "-h", ap->hostname, ap->lusername, NULL, ap->env);#endif } syslog(LOG_ERR, "can't exec login: %m");}intrlogind_mainloop (int infd, int outfd){ size_t size; struct auth_data auth_data; int true; char c; int authenticated; pid_t pid; int master; memset (&auth_data, 0, sizeof auth_data); size = sizeof auth_data.from; if (getpeername (infd, (struct sockaddr *) &auth_data.from, &size) < 0) { syslog (LOG_ERR, "Can't get peer name of remote host: %m"); fatal (outfd, "Can't get peer name of remote host", 1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -