Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:lafenghu
rsh
netkit-rsh-0.17.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File netkit-rsh-0.17.diff of Package rsh
--- configure +++ configure @@ -62,7 +62,7 @@ BINDIR="$EXECPREFIX/bin" SBINDIR="$EXECPREFIX/sbin" -MANDIR="$PREFIX/man" +MANDIR="$PREFIX/share/man" echo "Directories: $BINDIR $SBINDIR $MANDIR " --- rcp/rcp.c +++ rcp/rcp.c @@ -262,9 +262,9 @@ nospace(); (void)snprintf(bp, len, "%s -t %s", cmd, targ); host = thost; - rem = rcmd(&host, port, pwd->pw_name, + rem = rcmd_af(&host, port, pwd->pw_name, tuser ? tuser : pwd->pw_name, - bp, 0); + bp, 0, PF_UNSPEC); if (rem < 0) exit(1); #ifdef IP_TOS @@ -325,7 +325,8 @@ if (!(bp = malloc(len))) nospace(); (void)snprintf(bp, len, "%s -f %s", cmd, src); - rem = rcmd(&host, port, pwd->pw_name, suser, bp, 0); + rem = rcmd_af(&host, port, pwd->pw_name, suser, bp, 0, + PF_UNSPEC); (void)free(bp); if (rem < 0) { ++errs; --- rexec/rexec.1 +++ rexec/rexec.1 @@ -30,14 +30,17 @@ .\" SUCH DAMAGE. .\" .\" -.TH REXEC 1 "August 15, 1999" +.TH REXEC 1 "February 14, 1997" .SH NAME -rexec -- remote execution client for an exec server +rexec \-\- remote execution client for an exec server .SH SYNOPSIS .B rexec [ -.B \-a \-c \-d \-h \-n \-s -] [--] host command +.B \-abcdhns \-l +username +.B \-p +password +] host command .SH DESCRIPTION .B Rexec calls the @@ -54,87 +57,123 @@ for details of the protocol. .SH OPTIONS .B Rexec -accepts the following options: +accepts several options, but only three are likely to be very useful: .\" .LP -\fB\-a\fP +\fB\-l username\fP .IP +Set the log-in name on the remote host to username. +.\" +.LP +\fB\-p password\fP +.IP +Provide the password for the remote account. The command line argument +will be blanked after being parsed, to prevent it from being +seen with +.B ps(1). +However, it is still not very secure to type the password on the +command line. In particular, be sure that the shell's history file +is protected. +.TP +\fB\-n\fP +Explicitly prompt for name and password, even if provided in +the environment, in the $HOME/.netrc file, or in the environmental +variables REXEC_USER and REXEC_PASS. +.PP +Other options that might be useful with non-standard remote exec +daemons, or to debug connections: +.TP +\fB\-a\fP Do not set up an auxiliary channel for standard error from command; the remote standard error and output are then both returned on the local standard output. By default, .B rexec asks that a separate channel be set up for diagnostic output from the remote command. -.\" -.LP +.TP +\fB\-b\fP +Use signal handling as in BSD rsh(1). Only the signals +SIGINT, SIGQUIT, and SIGTERM are echoed to the remote process. +They do not remain raised locally, so rexec waits for the +remote command to shutdown its side of the socket. Also, CNTRL-Z will +only suspend execution locally--the remote command may continue to run. +.TP \fB\-c\fP -.IP Do not close remote standard input when local standard input closes. Normally the standard input to the remote command is closed when the local standard input is closed. -.\" -.LP +.TP \fB\-d\fP -.IP Turn on debugging information. In particular the command sent to the remote host will be echoed. -.\" -.LP +.TP \fB\-h\fP -.IP Print a usage message. -.\" -.LP -\fB\-n\fP -.IP -Explicitly prompt for name and password. Otherwise, -$HOME/.netrc will be scanned for login information. -.\" -.LP +.TP \fB\-s\fP -.IP Do not echo signals received by the rexec onto the remote process. Normally, signals which can be trapped are passed on to the remote process; then, when you type CNTRL-C, the remote process terminates as well. -.\" -.LP -\fB\--\fP +.SH USERNAME AND PASSWORD +.B Rexec(1) +searches for the username and password in the following order: .IP -Signals end of options to -.B rexec -to allow option switches in ``command.'' -.PP -The only option that is very useful is -n; -even then, if you have not -set up a password in $HOME/.netrc, you should still be prompted. +1. If -n is given on the command line, the user will always be +prompted for both, even if they are also given on the command line. +.IP +2. The command line will be parsed +.IP +3. If the environmental variables REXEC_USER or REXEC_PASS are +defined, they will define the username or password. +.IP +4. The $HOME/.netrc file will be searched. See +.B ftp(1) +for a description of this file's format. +.IP +5. Finally, the user will be prompted if either the username or password +remains undefined. + +.SH SECURITY +Users of this command should be aware that +.B rexec(3) +transmits their password to the remote host clear text, not +encrypted. If the network is not secure to the remote host, the +password can be comprimised. + +.SH SIGNALS +Without the -b option, +all signals which can be handled are echoed to the remote process. +Afterwards, however, they remain raised in the local process. +Typically, this means that +.B rexec(1) +will exit after receiving a fatal signal, even if the remote +process has arranged to handle or ignore it. + +Differing operating systems use differing signal numbers; for example +AIX and SunOS use 18 for SIGTSTP (^Z), while Linux uses 20. Therefore, +it may have a different effect remotely than +locally. In particular, typing CNTL-Z may not suspend the execution +of the remote process. .SH EXAMPLE .PP -rexec othermachine -- cat ">remote_file; date" <local_file +rexec othermachine cat ">remote_file; date" <local_file .PP will send local_file to the othermachine as remote_file. .SH BUGS -When a signal is received locally, the signal is passed to the remote -command using -blocking i/o. Even though it is only one byte, this may result -in delay in teminating the local command if communications are slow. -Also, the signal remains raised in the local rexec procedure, so -typically the remote command cannot ask for further user input -after the signal is sent. -.PP -It is annoying to have to use the ``--'' flag to include options in -the remote command without quoting them. This is because the GNU -getopt(3) routine is being called. This behavior may be corrected -by setting the POSIXLY_CORRECT environtmental variable. .PP -Please send bug reports or system incompatibilities to the author. +Please send bug reports, system incompatibilities, +and job offers to the author. .SH "SEE ALSO" -rexec(3), rexecd(8) +rexec(3), rexecd(8), rsh(1) .SH AUTHOR .PP Michael Sadd .br -sadd@msc.cornell.edu +mas22@cornell.edu .br -http://www.msc.cornell.edu/~sadd/ +http://www.tc.cornell.edu/~sadd/ + +Thanks to Orange Gopher (2/10/97) and Johannes Plass +(plass@dipmza.physik.uni-mainz.de, Oct. 17 1996) for useful suggestions. --- rexec/rexec.c +++ rexec/rexec.c @@ -1,33 +1,36 @@ /* - rexec.c -- Copyright 1996 by Michael Sadd (sadd@msc.cornell.edu) + rexec.c -- Copyright 1996 by Michael Sadd (mas22@cornell.edu) - Version 1.1 Sept. 12 1996 + Version 1.5 Jul. 29, 1999 Permission is given to freely distribute this program provided this source is included and this copyright notice and header remain. - This program calls the system rexec subroutine to + This program calls the system rexec subroutine to act as a rexec client. Please report bugs and system incompatibilities to me. I have compiled this under Linux 2.0.18, libc5.3.12 and gcc2.7.2, as well as under AIX 3.2 and 4.1 using gcc. It is meant for Linux, however, - and should be portable to other systems. Please let me know if there + but should be portable to other systems. Please let me know if there are problems. - - Usage: rexec [ -a -c -d -h -n -s ] [--] host command + + Usage: rexec [ -acdhns ] -l username -p password host command + -l username: Sets the login name for the remote host. + -p password: Sets the password for the remote host. + -n: Explicitly prompt for name and password. Otherwise, + $HOME/.netrc will be scanned for login information. -a: Do not set up an auxiliary channel for standard error from command; the remote standard error and output are then both returned on the local standard output. + -b: Use BSD rsh style signal handling. -c: Do not close remote standard input when local standard input closes. -d: Turn on debugging information. -h: Print this usage message. - -n: Explicitly prompt for name and password. Otherwise, - $HOME/.netrc will be scanned for login information. -s: Do not echo signals received by the rexec onto the remote process. Normally, signals which can be trapped are passed on to the remote process; then, when you type CNTRL-C, the remote @@ -37,9 +40,9 @@ Example: - ~/bin@athens% rexec othermachine -- cat ">remote_file; date" <local_file + ~/bin@athens% rexec othermachine -- cat ">remote_file; date" <local_file Fri Sep 13 02:25:20 EDT 1996 - + Here my password and user name are set up in $HOME/.netrc. The only option that is very useful is -n; even then, if you haven't @@ -47,18 +50,17 @@ */ -#include "../version.h" -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <netinet/in.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <netdb.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/types.h> #include <unistd.h> #include <errno.h> +#include <netinet/in.h> #include <pwd.h> #include <signal.h> #ifndef FD_SETSIZE @@ -69,7 +71,7 @@ #define MAX_PORT IPPORT_RESERVED #define EXTRA_PORT_LOW IPPORT_USERRESERVED #define EXTRA_PORT_MAX (EXTRA_PORT_LOW + 10000) -#define BUFLEN 512 +#define BUFLEN BUFSIZ #ifndef EAGAIN #ifdef EWOULDBLOCK #define EAGAIN EWOULDBLOCK @@ -79,27 +81,37 @@ #endif #ifdef _PASSWORD_LEN #define USERLEN _PASSWORD_LEN -#else +#else #define USERLEN 256 #endif +#ifndef SA_RESTART +#define SA_RESTART 0 +#endif + +typedef void (*SIGHANDLER_T)(int); void parse_options(char *argv[], int argc, int *debug, int *extra_error, int *close_on_stdin, int *prompt, int *pass_sig, - char **host, char **command); + char **host, char **command, char **user_name, + char **passwd); void usage(char *name); int echo_fd(int fd_to, int fd_from, char *prog_name, int debug); -void set_signals(void); +void set_signal(int sig, SIGHANDLER_T handler); void echo_sig(int sig); +void safe_write_error(const char *message); /* These need to be global for signal passing. */ -int aux_sock; /* Socket for auxiliary channel. */ +int aux_sock=-1; /* Socket for auxiliary channel. */ int extra_error = 1; /* Setup special channel for standard error? */ +int bsd = 0; /* BSD compatible signals? */ +int bsd_signals[] = {SIGINT,SIGQUIT,SIGTERM}; +int debug = 0; /* Turn on debugging info? */ +char *progname; int main(int argc, char *argv[]) { - + /* Program options and parameters. */ - int debug = 0; /* Turn on debugging info? */ int prompt = 0; /* Prompt for name and password? */ int close_on_stdin = 1; /* Close socket on stdin, not on remote host. */ int pass_sig = 1; /* Should we pass signals to the remote process? */ @@ -112,9 +124,14 @@ int sock; /* Rexec socket. */ int *p_to_aux_sock; /* Pointer to socket for auxiliary channel. */ int sock_open, stdin_open, aux_open, shut_down; /* Open file descriptor flags. */ + struct addrinfo hints, *res; + int gaierr; + sa_family_t af = AF_INET; + + progname=argv[0]; parse_options(argv, argc, &debug, &extra_error, &close_on_stdin, &prompt, - &pass_sig, &host, &command); + &pass_sig, &host, &command, &user_name, &passwd); service = getservbyname("exec","tcp"); if ( (port_exec = service->s_port) >= MAX_PORT ) @@ -127,79 +144,29 @@ } port_exec = htons(DEFAULT_PORT); } - - if ( extra_error ) - { - int port_extra; /* Auxiliary port number. */ - struct sockaddr_in aux_name; /* Auxilliary socket name for bind. */ - int found_port = 0; - if (debug) - fprintf(stderr,"%s: Attempting to allocate channel for remote " - "standard error\n", argv[0]); - - if ( (aux_sock = socket(PF_INET, SOCK_STREAM , 0)) < 0) - { - fprintf(stderr,"%s: Error in socket call: ",argv[0]); - perror(NULL); - exit(1); - } - - aux_name.sin_family = AF_INET; - aux_name.sin_addr.s_addr = htonl(INADDR_ANY); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_socktype = SOCK_STREAM; + if ((gaierr = getaddrinfo(host, NULL, &hints, &res))) { + fprintf(stderr, "%s: Couldn't look up address for %s: %s", + argv[0], host, gai_strerror(gaierr)); + exit(1); + } + af = res->ai_family; + freeaddrinfo(res); - for ( port_extra = EXTRA_PORT_LOW; port_extra < EXTRA_PORT_MAX; - ++port_extra) - { - aux_name.sin_port = htons(port_extra); - found_port = (bind(aux_sock, (struct sockaddr *) &aux_name, - sizeof(aux_name)) == 0); - if ( found_port ) - break; - switch (errno) - { - case EADDRNOTAVAIL: - case EADDRINUSE: - case EACCES: - if (debug) - { - fprintf(stderr,"%s: Error from bind for port No. %d: ",argv[0], - port_extra); - perror(NULL); - fprintf(stderr,"Will try next port...\n"); - } - break; - default: - fprintf(stderr,"%s: Error binding to socket for aux. channel: ", - argv[0]); - perror(NULL); - exit(1); - break; - } - } - - if ( ! found_port ) - { - fprintf(stderr,"%s: Could not find available port for auxiliary ", - argv[0]); - fprintf(stderr,"channel in port range %d to %d.\n",EXTRA_PORT_LOW, - EXTRA_PORT_MAX); - exit(1); - } + /* Non-zero p_to_aux_sock is signal to rexec(3) to establish + auxilliary socket for standard error and signal passing. */ + if ( extra_error ) p_to_aux_sock = &aux_sock; - /* listen here? */ - - if (pass_sig) - set_signals(); - - } else /* else we just want standard error directed as standar out--no aux */ p_to_aux_sock = NULL; - if (prompt) + if (prompt || user_name == NULL || prompt == NULL) { FILE *term_in,*term_out; - + if ( ( term_in = fopen("/dev/tty","r+")) == NULL) { term_in = stdin; @@ -207,21 +174,55 @@ } else term_out = term_in; - fprintf(term_out,"Username at %s: ",host); - user_name = fgets(user_buf,USERLEN,term_in); - user_name[strlen(user_name)-1] = '\0'; /* Hopefully fgets always adds - a newline. */ - passwd = getpass("Password: "); + + if (prompt || user_name == NULL) + { + fprintf(term_out,"Username at %s: ",host); + user_name = fgets(user_buf,USERLEN,term_in); + user_name[strlen(user_name)-1] = '\0'; /* Hopefully fgets always adds + a newline. */ + } + if (prompt || passwd == NULL) + passwd = getpass("Password: "); } - if ( (sock = rexec(&host, port_exec, user_name, passwd, command, - p_to_aux_sock)) < 0 ) - { - fprintf(stderr,"%s: Error in rexec system call: ",argv[0]); - perror(NULL); + if ( user_name == NULL ) + user_name = getenv("REXEC_USER"); + if ( passwd == NULL ) + passwd = getenv("REXEC_PASS"); + + if ( (sock = rexec_af (&host, port_exec, user_name, passwd, command, + p_to_aux_sock, af)) < 0 ) + { + fprintf(stderr,"%s: Error in rexec system call,\n",argv[0]); + fprintf(stderr,"%s: (The following system error may itself be in error)\n",argv[0]); + perror(argv[0]); exit(1); } + /* Check to see if auxilliary socket really set. */ + if ( extra_error && ( p_to_aux_sock == NULL ) ) { + extra_error = 0; + if (debug) { + fprintf(stderr,"%s: Separate channel for standard error not opened.\n", + progname); + } + } + + if ( pass_sig && p_to_aux_sock ) + { + int sig; + + if (bsd) + { + for (sig = 0; sig < sizeof(bsd_signals)/sizeof(int); ++sig) + set_signal(bsd_signals[sig],echo_sig); + } else { + for (sig = 1; sig < NSIG; ++sig) + set_signal(sig,echo_sig); + } + } + sock_open = stdin_open = aux_open = 1; shut_down = 0; while (sock_open || ( aux_open && extra_error )) /* echo stdin -> remote host @@ -229,6 +230,7 @@ until the remote host closes the socket. */ { fd_set read_set; + int select_ret; FD_ZERO(&read_set); if (stdin_open) @@ -239,8 +241,13 @@ FD_SET(aux_sock, &read_set); - /* Using an infinit timeout in select (last parameter = NULL). */ - if ( select(FD_SETSIZE, &read_set, NULL, NULL, NULL) < 0 ) + /* Using an infinite timeout in select (last parameter = NULL). */ + do { + errno =0; + select_ret=select(FD_SETSIZE, &read_set, NULL, NULL, NULL); + } while ( errno == EINTR ); + + if ( select_ret < 0 ) { fprintf(stderr,"%s: Error in select system call: ",argv[0]); perror(NULL); @@ -256,7 +263,7 @@ if ( extra_error && FD_ISSET(aux_sock, &read_set) ) aux_open = echo_fd(STDERR_FILENO, aux_sock, argv[0], debug); - if ( ! stdin_open && close_on_stdin && ! shut_down ) + if ( ! stdin_open && close_on_stdin && ! shut_down ) { if (shutdown(sock, 1) <0) { @@ -273,11 +280,14 @@ } -#define OPTIONS "dahncs" +#define OPTIONS "+dahncbsl:p:" +extern char *optarg; +extern int optopt; void parse_options(char *argv[], int argc, int *debug, int *extra_error, int *close_on_stdin, int *prompt, int *pass_sig, - char **host, char **command) + char **host, char **command, char **user_name, + char **passwd) { int opt; int len = 0,ind; @@ -285,6 +295,20 @@ while ((opt = getopt(argc, argv, OPTIONS)) > 0) switch (opt) { + case 'l': + if (optarg != NULL) + *user_name=strcpy((char *)malloc((strlen(optarg)+1)*sizeof(char)),optarg); + break; + case 'p': + if ( optarg!= NULL ) + { + int passlen = strlen(optarg); + + *passwd=strcpy((char *)malloc((passlen+1)*sizeof(char)),optarg); + for (ind = 0; ind < passlen; ++ind) + optarg[ind] = '\0'; + } + break; case 'd': *debug = 1; break; @@ -300,11 +324,14 @@ case 'c': *close_on_stdin = 0; break; + case 'b': + bsd = 1; + break; case 's': *pass_sig = 0; break; default: - /* fprintf(stderr,"%s: Unknown option -%c\n",argv[0],(char)optopt);*/ + /*fprintf(stderr,"%s: Unknown option -%c\n",argv[0],(char)optopt);*/ usage(argv[0]); break; } @@ -314,12 +341,25 @@ usage(argv[0]); } *host = argv[optind++]; + if ( strcmp(argv[optind],"-l" ) == 0 ) + { + if ( (optind += 2 ) >= argc ) + { + fprintf(stderr,"%s: Require a remote command to execute.\n",argv[0]); + usage(argv[0]); + } + *user_name=argv[optind-1]; + } for ( ind = optind; ind < argc; ++ind) len += strlen(argv[ind])+1; *command = (char *) malloc((len+1)*sizeof(char)); **command = '\0'; - for ( ind = optind; (ind < argc) && strcat(*command," "); ++ind) + for ( ind = optind; ind < argc; ++ind) { + if ( ind > optind ) { + (void)strcat(*command," "); + } (void) strcat(*command, argv[ind]); + } if (*debug) { @@ -327,17 +367,20 @@ fprintf(stderr,"%s: Command to execute = %s\n", argv[0], *command); } } - + void usage(char *name) { - fprintf(stderr,"Usage: %s [ -a -c -d -h -n ] [--] host command\n", name); - fprintf(stderr,"\t-a: Do not set up an auxiliary channel for standard error\n"); - fprintf(stderr,"\t-c: Do not close remote standard in when local input closes\n"); - fprintf(stderr,"\t-d: Turn on debugging information.\n"); - fprintf(stderr,"\t-h: Print this usage message.\n"); - fprintf(stderr,"\t-n: Explicitly prompt for name and password.\n"); - fprintf(stderr,"\t-s: Do not echo signals to the remote process. \n"); - fprintf(stderr,"\t--: Signals end of options to allow options in `command`\n"); + fprintf(stderr,"Usage: %s [ -abcdhns ] -l username -p password host command\n", name); + fprintf(stderr, + " -l username: Sets the login name for the remote host.\n" + " -p password: Sets the password for the remote host.\n" + " -n: Explicitly prompt for name and password.\n" + " -a: Do not set up an auxiliary channel for standard error.\n" + " -b: Use BSD-rsh type signal handling.\n" + " -c: Do not close remote standard in when local input closes\n" + " -d: Turn on debugging information.\n" + " -h: Print this usage message.\n" + " -s: Do not echo signals to the remote process. \n"); exit(1); } @@ -345,10 +388,8 @@ zero on end of file. */ int echo_fd(int fd_to, int fd_from, char *prog_name, int debug) { - int sock_read; - char buffer[BUFLEN]; - - (void)debug; + int sock_read, sock_send; + char buffer[BUFLEN],*write_buf; if ( (sock_read = read(fd_from, buffer, BUFLEN)) < 0 ) { @@ -356,38 +397,121 @@ perror(NULL); exit(1); } - - if ( sock_read ) - write(fd_to, buffer, sock_read); + + for(write_buf=buffer,sock_send=sock_read; sock_send; ) + { + int ch = write(fd_to, write_buf, sock_send); + if (ch < 0 ) { + if ( errno == EAGAIN ) + continue; + perror(progname); + exit(1); + } + sock_send -= ch; + write_buf += ch; + if (debug && sock_send) + fprintf(stderr,"%s: sent only %d of %d bytes received.\n",progname,ch, + sock_read); + } return sock_read; } -void set_signals(void) +void set_signal(int sig, SIGHANDLER_T handler) { - int sig; - for (sig = 1; sig < NSIG; ++sig) - signal(sig, echo_sig); + switch (sig) + { + struct sigaction action; + + case SIGKILL: + case SIGSTOP: + return; + break; + default: + if ( sigaction(sig, NULL, &action) < 0 ) + { + perror(progname); + exit(1); + } + if ( action.sa_handler != SIG_IGN ) + { + action.sa_handler = handler; + action.sa_flags = SA_RESTART; + (void) sigemptyset(&(action.sa_mask)); + if ( sigaction(sig, &action, NULL) < 0 ) + { + perror(progname); + exit(1); + } + } + } + } + +#define SAFEMSGSIZ 512 + void echo_sig(int sig) { char sigch = (char) sig; + static char safe_msg[SAFEMSGSIZ]; + int saved_errno = errno; + + if (debug) { + sprintf(safe_msg,"rexec: echo_sig: received signal %d\n",sig); + safe_write_error(safe_msg); + } + + if ( (aux_sock > 0) && (write(aux_sock, &sigch, 1) < 1) && debug ) { + sprintf(safe_msg,"Error: Signal %d was not properly sent to the remote " + "process.\n",sig); + safe_write_error(safe_msg); + } + - if (extra_error) - write(aux_sock, &sigch, 1); + if ( sig == SIGTSTP ) { + sprintf(safe_msg,"%s: The stop signal does not translate correctly to all" + " remote hosts.\nDepending on the host, your remote process may " + "still be running.\n",progname); + safe_write_error(safe_msg); + } + + + if ( ! bsd ) { + sigset_t sig_mask; + + set_signal(sig,SIG_DFL); + (void)sigemptyset(&sig_mask); + (void)sigaddset(&sig_mask, sig); + if ( sigprocmask( SIG_UNBLOCK, &sig_mask, NULL) < 0 ) { + perror(progname); + exit(1); + } + + kill(getpid(), sig); /* raise(sig), but more prortable. */ + + set_signal(sig, echo_sig); + } + + errno = saved_errno; - raise(sig); } +void safe_write_error(const char *message) { + + int msg_len = strlen(message); + /* Ignore error condition--If this doesn't work, not much can be done. */ + (void) write(STDERR_FILENO, message, msg_len); + +} /* void set_no_blocking(int fd) { int old_flag = fcntl(fd, F_GETFD, 0); - + if (old_flag < 0) { perror("Error in fcntl(fd, F_GETFD, 0):"); @@ -401,3 +525,66 @@ } */ +/* + + int port_extra; + struct sockaddr_storage aux_name; + int found_port = 0; + + if (debug) + fprintf(stderr,"%s: Attempting to allocate channel for remote " + "standard error\n", argv[0]); + + if ( (aux_sock = socket(af, SOCK_STREAM , 0)) < 0) + { + fprintf(stderr,"%s: Error in socket call: ",argv[0]); + perror(NULL); + exit(1); + } + + memset(&aux_name, 0, sizeof(aux_name)); + ((struct sockaddr *)&aux_name)->sa_family = af; + + for ( port_extra = EXTRA_PORT_LOW; port_extra < EXTRA_PORT_MAX; + ++port_extra) + { + if (af == AF_INET6) + ((struct sockaddr_in6 *)&aux_name)->sin6_port = htons(port_extra); + else + ((struct sockaddr_in *)&aux_name)->sin_port = htons(port_extra); + if ( found_port = (bind(aux_sock, (struct sockaddr *) &aux_name, + sizeof(aux_name)) == 0) ) + break; + switch (errno) + { + case EADDRNOTAVAIL: + case EADDRINUSE: + case EACCES: + if (debug) + { + fprintf(stderr,"%s: Error from bind for port No. %d: ",argv[0], + port_extra); + perror(NULL); + fprintf(stderr,"Will try next port...\n"); + } + break; + default: + fprintf(stderr,"%s: Error binding to socket for aux. channel: ", + argv[0]); + perror(NULL); + exit(1); + break; + } + } + + if ( ! found_port ) + { + fprintf(stderr,"%s: Could not find available port for auxiliary ", + argv[0]); + fprintf(stderr,"channel in port range %d to %d.\n",EXTRA_PORT_LOW, + EXTRA_PORT_MAX); + exit(1); + } + p_to_aux_sock = &aux_sock; + +*/ --- rexec/rexec_af.c +++ rexec/rexec_af.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rexec.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <alloca.h> +#include <stdio.h> +#include <netdb.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int rexecoptions; +char ahostbuf[NI_MAXHOST]; + +int +rexec_af(ahost, rport, name, pass, cmd, fd2p, af) + char **ahost; + int rport; + const char *name, *pass, *cmd; + int *fd2p; + sa_family_t af; +{ + struct sockaddr_storage sa2, from; + struct addrinfo hints, *res0; + const char *orig_name = name; + const char *orig_pass = pass; + u_short port = 0; + int s, timo = 1, s3; + char c; + int gai; + char servbuff[NI_MAXSERV]; + + snprintf(servbuff, sizeof(servbuff), "%d", ntohs(rport)); + servbuff[sizeof(servbuff) - 1] = '\0'; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + gai = getaddrinfo(*ahost, servbuff, &hints, &res0); + if (gai){ + /* XXX: set errno? */ + return -1; + } + + if (res0->ai_canonname){ + strncpy(ahostbuf, res0->ai_canonname, sizeof(ahostbuf)); + ahostbuf[sizeof(ahostbuf)-1] = '\0'; + *ahost = ahostbuf; + } + else{ + *ahost = NULL; + } + ruserpass(res0->ai_canonname, &name, &pass); +retry: + s = socket(res0->ai_family, res0->ai_socktype, 0); + if (s < 0) { + perror("rexec: socket"); + return (-1); + } + if (connect(s, res0->ai_addr, res0->ai_addrlen) < 0) { + if (errno == ECONNREFUSED && timo <= 16) { + (void) close(s); + sleep(timo); + timo *= 2; + goto retry; + } + perror(res0->ai_canonname); + return (-1); + } + if (fd2p == 0) { + (void) write(s, "", 1); + port = 0; + } else { + char num[32]; + int s2, sa2len; + + s2 = socket(res0->ai_family, res0->ai_socktype, 0); + if (s2 < 0) { + (void) close(s); + return (-1); + } + listen(s2, 1); + sa2len = sizeof (sa2); + if (getsockname(s2, (struct sockaddr *)&sa2, &sa2len) < 0) { + perror("getsockname"); + (void) close(s2); + goto bad; + } + port = 0; + if (!getnameinfo((struct sockaddr *)&sa2, sa2len, + NULL, 0, servbuff, sizeof(servbuff), + NI_NUMERICSERV)) + port = atoi(servbuff); + (void) sprintf(num, "%u", port); + (void) write(s, num, strlen(num)+1); + { int len = sizeof (from); + s3 = accept(s2, (struct sockaddr *)&from, &len); + close(s2); + if (s3 < 0) { + perror("accept"); + port = 0; + goto bad; + } + } + *fd2p = s3; + } + (void) write(s, name, strlen(name) + 1); + /* should public key encypt the password here */ + (void) write(s, pass, strlen(pass) + 1); + (void) write(s, cmd, strlen(cmd) + 1); + + /* We don't need the memory allocated for the name and the password + in ruserpass anymore. */ + if (name != orig_name) + free ((char *) name); + if (pass != orig_pass) + free ((char *) pass); + + if (read(s, &c, 1) != 1) { + perror(*ahost); + goto bad; + } + if (c != 0) { + while (read(s, &c, 1) == 1) { + (void) write(2, &c, 1); + if (c == '\n') + break; + } + goto bad; + } + freeaddrinfo(res0); + return (s); +bad: + if (port) + (void) close(*fd2p); + (void) close(s); + freeaddrinfo(res0); + return (-1); +} + +int +rexec(ahost, rport, name, pass, cmd, fd2p) + char **ahost; + int rport; + const char *name, *pass, *cmd; + int *fd2p; +{ + return rexec_af(ahost, rport, name, pass, cmd, fd2p, AF_INET); +} --- rexecd/Makefile +++ rexecd/Makefile @@ -31,11 +31,7 @@ install -m$(MANMODE) rexecd.8 $(INSTALLROOT)$(MANDIR)/man8/in.rexecd.8 ln -sf in.rexecd.8 $(INSTALLROOT)$(MANDIR)/man8/rexecd.8 ifeq ($(USE_PAM),1) - @echo - @echo You have chosen to use PAM as the authentication method - @echo You should copy the rexec.pam file provided to /etc/pam.d - @echo or if you know what you are doing, tailor it to your needs - @echo + install -m644 rexec.pam $(INSTALLROOT)/etc/pam.d/rexec endif clean: --- rexecd/rexec.pam +++ rexecd/rexec.pam @@ -1,8 +1,7 @@ #%PAM-1.0 -#auth required /lib/security/pam_securetty.so -auth required /lib/security/pam_pwdb.so shadow nullok -auth required /lib/security/pam_nologin.so -auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/ftpusers -auth required /lib/security/pam_shells.so -account required /lib/security/pam_pwdb.so - +auth requisite pam_nologin.so +auth include common-auth +account include common-account +password include common-password +session required pam_loginuid.so +session include common-session --- rexecd/rexecd.c +++ rexecd/rexecd.c @@ -32,7 +32,7 @@ * * * 1-14-99 Karl R. Hakimian <hakimian@eecs.wsu.edu> - * + * * While the headers in this file claim only the purest decent from * their BSD roots, this program has had unspeakable things done to it * over the years. I have tried to clean things up and get them working @@ -112,7 +112,7 @@ */ static void fatal(const char *); -static void doit(struct sockaddr_in *fromp); +static void doit(struct sockaddr *fromp); static void getstr(char *buf, int cnt, const char *err); static const char *remote = NULL; @@ -120,17 +120,23 @@ int main(int argc, char **argv) { - struct sockaddr_in from; + struct sockaddr_storage from; socklen_t fromlen; (void)argc; fromlen = sizeof(from); - + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { fprintf(stderr, "rexecd: getpeername: %s\n", strerror(errno)); return 1; } + if (((struct sockaddr *)&from)->sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&from)->sin6_addr)) { + ((struct sockaddr *)&from)->sa_family = AF_INET; + ((struct sockaddr_in *)&from)->sin_addr.s_addr = + ((struct sockaddr_in6 *)&from)->sin6_addr.s6_addr32[3]; + } openlog(argv[0], LOG_PID, LOG_DAEMON); @@ -142,19 +148,19 @@ remote = hosts_info(&from_host); #else { - struct hostent *h = gethostbyaddr((const char *)&from.sin_addr, - sizeof(struct in_addr), - AF_INET); - if (!h || !h->h_name) { + char hbuf[NI_MAXHOST]; + + if (getnameinfo((struct sockaddr *)&from, fromlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD)) { write(0, "\1Where are you?\n", 16); return 1; } /* Be advised that this may be utter nonsense. */ - remote = strdup(h->h_name); + remote = strdup(hbuf); } #endif syslog(allow_severity, "connect from %.128s", remote); - doit(&from); + doit((struct sockaddr *)&from); return 0; } @@ -221,7 +227,7 @@ static void -doit(struct sockaddr_in *fromp) +doit(struct sockaddr *fromp) { char cmdbuf[ARG_MAX+1]; char user[16], pass[16]; @@ -273,7 +279,7 @@ We must connect back to the client here if a port was provided. KRH */ if (port != 0) { - s = socket(AF_INET, SOCK_STREAM, 0); + s = socket(fromp->sa_family, SOCK_STREAM, 0); if (s < 0) exit(1); @@ -283,8 +289,11 @@ exit(1); #endif alarm(60); - fromp->sin_port = htons(port); - if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) + if (fromp->sa_family == AF_INET6) + ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port); + else + ((struct sockaddr_in *)fromp)->sin_port = htons(port); + if (connect(s, fromp, sizeof(struct sockaddr_storage)) < 0) exit(1); alarm(0); } @@ -300,6 +309,7 @@ PAM_password = pass; pam_error = pam_start("rexec", PAM_username, &PAM_conversation,&pamh); PAM_BAIL; + pam_set_item (pamh, PAM_TTY, "tty"); pam_error = pam_authenticate(pamh, 0); PAM_BAIL; pam_error = pam_acct_mgmt(pamh, 0); @@ -420,7 +430,7 @@ else cp2 = theshell; /* - * Close all fds, in case libc has left fun stuff like + * Close all fds, in case libc has left fun stuff like * /etc/shadow open. */ for (ifd = getdtablesize()-1; ifd > 2; ifd--) close(ifd); @@ -453,4 +463,3 @@ } } while (c != 0); } - --- rlogin/Makefile +++ rlogin/Makefile @@ -12,6 +12,7 @@ install: $(PROG) install -s -o root -m$(SUIDMODE) $(PROG) $(INSTALLROOT)$(BINDIR) install -m $(MANMODE) $(PROG).1 $(INSTALLROOT)$(MANDIR)/man1 + install -m $(MANMODE) rhosts.5 $(INSTALLROOT)$(MANDIR)/man5 clean: rm -f *.o $(PROG) --- rlogin/rhosts.5 +++ rlogin/rhosts.5 @@ -0,0 +1,30 @@ +.\" Copyright (c) 1995 Peter Tobias <tobias@et-inf.fho-emden.de> +.\" This file may be distributed under the GNU General Public License. +.TH RHOSTS 5 "29 Jan 1995" "Linux" "Linux Programmer's Manual" +.SH NAME +$HOME/.rhosts \- grants or denies password-free \fBr\fP-command access +to a specific user account +.SH DESCRIPTION +The \fB.rhosts\fP file allows or denies a user who has an account on the +local host to use the \fBr\fP-commands (e.g. \fBrlogin\fP, \fBrsh\fP or +\fBrcp\fP) without supplying a password. +.PP +The file uses the following format: +.TP +\fIhostname\fP \fI[username]\fP +.PP +Such an entry grants password-free \fBr\fP-command access for the user with +the login name \fIusername\fP from the remote host \fIhostname\fP. +If no user name is specified, the user must have the same login name on +the remote host and the local host. For security reasons you should always +use the FQDN of the hostname and not the short hostname. +.PP +Netgroups can be specified by preceeding the netgroup by an @ sign. +.PP +The \fB.rhosts\fP file must have permissions 600 (that is, readable and +writable only by the owner). +.PP +.SH FILES +$HOME/.rhosts +.SH "SEE ALSO" +.BR hosts.equiv "(5), " rshd "(8), " rlogind (8) --- rlogin/rlogin.c +++ rlogin/rlogin.c @@ -279,7 +279,8 @@ /* will use SIGUSR1 for window size hack, so hold it off */ omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); - rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); + rem = rcmd_af (&host, sp->s_port, pw->pw_name, user, term, 0, + PF_UNSPEC); if (rem < 0) exit(1); --- rlogind/Makefile +++ rlogind/Makefile @@ -3,27 +3,23 @@ include ../MCONFIG include ../MRULES -OBJS = rlogind.o network.o auth.o -# logwtmp.o +OBJS = rlogind.o network.o auth.o sockconv.o -ifeq ($(USE_PAM),1) -OBJS += sockconv.o CFLAGS += -DUSE_PAM -LIBS += -ldl -lpam -lpam_misc -endif +LIBS += -ldl -lpam + +LIBS += -lutil $(LIBTERMCAP) rlogind: $(OBJS) $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ -rlogind.o: pathnames.h logwtmp.h rlogind.h ../version.h -logwtmp.o: logwtmp.h -auth.o network.o: rlogind.h +$(OBJS): pathnames.h logwtmp.h install: rlogind install -s -m$(DAEMONMODE) rlogind $(INSTALLROOT)$(SBINDIR)/in.rlogind install -m$(MANMODE) rlogind.8 $(INSTALLROOT)$(MANDIR)/man8/in.rlogind.8 ln -sf in.rlogind.8 $(INSTALLROOT)$(MANDIR)/man8/rlogind.8 + install -m 644 rlogin.pam $(INSTALLROOT)/etc/pam.d/rlogin clean: rm -f *.o rlogind - --- rlogind/auth.c +++ rlogind/auth.c @@ -31,12 +31,14 @@ * SUCH DAMAGE. */ +#include <stdio.h> #include <sys/types.h> #include <pwd.h> #include "rlogind.h" #ifdef USE_PAM +#include <grp.h> /* * Modifications for Linux-PAM: Al Longyear <longyear@netcom.com> @@ -122,7 +124,7 @@ pam_set_item(pamh, PAM_USER, localuser); pam_set_item(pamh, PAM_RUSER, remoteuser); pam_set_item(pamh, PAM_RHOST, host); - pam_set_item(pamh, PAM_TTY, "tty"); /* ? */ + pam_set_item(pamh, PAM_TTY, "rlogin"); /* we don't have a tty yet! */ network_confirm(); retval = attempt_auth(); @@ -158,18 +160,14 @@ pwd = getpwnam(localuser); if (pwd==NULL) { syslog(LOG_ERR, "user returned by PAM does not exist\n"); - /* don't print this - it tells people which accounts exist */ - /*fprintf(stderr, "rlogind: internal error\n");*/ return -1; } if (setgid(pwd->pw_gid) != 0) { syslog(LOG_ERR, "cannot assume gid for user returned by PAM\n"); - fprintf(stderr, "rlogind: internal error\n"); return -1; } if (initgroups(localuser, pwd->pw_gid) != 0) { syslog(LOG_ERR, "initgroups failed for user returned by PAM\n"); - fprintf(stderr, "rlogind: internal error\n"); return -1; } retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); @@ -194,6 +192,7 @@ #define _check_rhosts_file __check_rhosts_file #endif extern int _check_rhosts_file; +extern sa_family_t from_af; void auth_checkoptions(void) {} @@ -236,7 +235,7 @@ _check_rhosts_file = use_rhosts; - return ruserok(host, pwd->pw_uid==0, remoteuser, localuser); + return ruserok_af(host, pwd->pw_uid==0, remoteuser, localuser, from_af); } #endif /* PAM */ --- rlogind/network.c +++ rlogind/network.c @@ -88,44 +88,62 @@ return(0); } +sa_family_t from_af; +static socklen_t fromlen; static char * -find_hostname(const struct sockaddr_in *fromp, int *hostokp) +find_hostname(const struct sockaddr *fromp, int *hostokp) { - struct hostent *hop; + struct addrinfo hints, *res, *res0; + char naddr[NI_MAXHOST]; + char raddr[NI_MAXHOST]; + char hbuf[NI_MAXHOST]; char *hname; int hostok = 0; - hop = gethostbyaddr((const char *)&fromp->sin_addr, - sizeof(struct in_addr), fromp->sin_family); - if (hop == NULL) { - hname = strdup(inet_ntoa(fromp->sin_addr)); + if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0, + NI_NAMEREQD)) { + if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(hbuf, "???"); + hname = strdup(hbuf); hostok = 1; } - else if (check_all || local_domain(hop->h_name)) { + else if (check_all || local_domain(hbuf)) { /* * If name returned by gethostbyaddr is in our domain, * attempt to verify that we haven't been fooled by someone * in a remote net; look up the name and check that this * address corresponds to the name. */ - hname = strdup(hop->h_name); - hop = gethostbyname(hname); - if (hop) { - for (; hop->h_addr_list[0]; hop->h_addr_list++) { - if (!memcmp(hop->h_addr_list[0], &fromp->sin_addr, - sizeof(fromp->sin_addr))) { + hname = strdup(hbuf); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = from_af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + if (getaddrinfo(hbuf, NULL, &hints, &res0) == 0) { + if (getnameinfo(fromp, fromlen, naddr, sizeof(naddr), + NULL, 0, NI_NUMERICHOST)) + strcpy(naddr, "???"); + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != from_af) + continue; + if (getnameinfo(res->ai_addr, res->ai_addrlen, + raddr, sizeof(raddr), NULL, 0, + NI_NUMERICHOST) == 0 + && strcmp(naddr, raddr) == 0) { + free(hname); + hname = strdup(res->ai_canonname + ? res->ai_canonname : hbuf); hostok = 1; break; } } - /* not clear if this is worthwhile */ - free(hname); - hname = strdup(hop->h_name); + freeaddrinfo(res0); } } else { - hname = strdup(hop->h_name); + hname = strdup(hbuf); hostok = 1; } @@ -145,29 +163,38 @@ char * network_init(int f, int *hostokp) { - struct sockaddr_in from, *fromp; - socklen_t fromlen; + struct sockaddr_storage from; + struct sockaddr *fromp; int on = 1; char c; char *hname; int port; - from.sin_family = AF_INET; fromlen = sizeof (from); if (getpeername(f, (struct sockaddr *)&from, &fromlen) < 0) { syslog(LOG_ERR,"Can't get peer name of remote host: %m"); fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); } + fromp = (struct sockaddr *)&from; + if (fromp->sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)fromp)->sin6_addr)) { + fromp->sa_family = AF_INET; + ((struct sockaddr_in *)fromp)->sin_addr.s_addr = + ((struct sockaddr_in6 *)fromp)->sin6_addr.s6_addr32[3]; + } + from_af = fromp->sa_family; if (keepalive && setsockopt(f, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); #ifdef IP_TOS + if (from_af == AF_INET) + { #define IPTOS_LOWDELAY 0x10 on = IPTOS_LOWDELAY; if (setsockopt(f, IPPROTO_IP, IP_TOS, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } #endif - fromp = &from; alarm(60); read(f, &c, 1); @@ -178,16 +205,24 @@ alarm(0); hname = find_hostname(fromp, hostokp); - - port = ntohs(fromp->sin_port); - if (fromp->sin_family != AF_INET || + if (from_af == AF_INET6) + port = ntohs(((struct sockaddr_in6 *)fromp)->sin6_port); + else + port = ntohs(((struct sockaddr_in *)fromp)->sin_port); + if ((from_af != AF_INET && from_af != AF_INET6) || port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { + char hbuf[MAXHOSTNAMELEN]; + + if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), NULL, 0, + NI_NUMERICHOST)) + strcpy(hbuf, "???"); syslog(LOG_NOTICE, "Connection from %s on illegal port", - inet_ntoa(fromp->sin_addr)); + hbuf); fatal(f, "Permission denied", 0); } #ifdef IP_OPTIONS + if (from_af == AF_INET) { u_char optbuf[BUFSIZ/3], *cp; char lbuf[BUFSIZ]; --- rlogind/rlogin.pam +++ rlogind/rlogin.pam @@ -0,0 +1,10 @@ +#%PAM-1.0 +auth requisite pam_nologin.so +auth [user_unknown=ignore success=ok ignore=ignore auth_err=die default=bad] pam_securetty.so +auth sufficient pam_rhosts.so +auth include common-auth +auth required pam_mail.so +account include common-account +password include common-password +session required pam_loginuid.so +session include common-session --- rsh/rsh.c +++ rsh/rsh.c @@ -163,7 +163,8 @@ exit(1); } - rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); + rem = rcmd_af(&host, sp->s_port, pw->pw_name, user, args, &rfd2, + PF_UNSPEC); if (rem < 0) exit(1); --- rshd/Makefile +++ rshd/Makefile @@ -6,9 +6,8 @@ OBJS = rshd.o ifeq ($(USE_PAM),1) -# ? -CFLAGS += # -DUSE_PAM -LIBS += -ldl # -lpam -lpam_misc +CFLAGS += -DUSE_PAM +LIBS += -ldl -lpam -lpam_misc endif rshd: $(OBJS) @@ -18,6 +17,7 @@ install -s -m$(DAEMONMODE) rshd $(INSTALLROOT)$(SBINDIR)/in.rshd install -m$(MANMODE) rshd.8 $(INSTALLROOT)$(MANDIR)/man8/in.rshd.8 ln -sf in.rshd.8 $(INSTALLROOT)$(MANDIR)/man8/rshd.8 + install -m644 rsh.pam $(INSTALLROOT)/etc/pam.d/rsh clean: rm -f *.o rshd --- rshd/rsh.pam +++ rshd/rsh.pam @@ -0,0 +1,7 @@ +#%PAM-1.0 +auth required pam_rhosts.so +auth required pam_nologin.so +account include common-account +password include common-password +session required pam_loginuid.so +session include common-session --- rshd/rshd.c +++ rshd/rshd.c @@ -97,6 +97,8 @@ static int paranoid = 0; static int sent_null; static int allow_root_rhosts=0; +static sa_family_t from_af; +static socklen_t fromlen; char username[20] = "USER="; char homedir[64] = "HOME="; @@ -107,7 +109,7 @@ extern char **environ; static void error(const char *fmt, ...); -static void doit(struct sockaddr_in *fromp); +static void doit(struct sockaddr *fromp); static void getstr(char *buf, int cnt, const char *err); extern int _check_rhosts_file; @@ -275,26 +277,32 @@ return pwd; #else if (pwd->pw_uid==0 && !allow_root_rhosts) return NULL; - if (ruserok(hostname, pwd->pw_uid==0, remuser, locuser) < 0) { + if (ruserok_af(hostname, pwd->pw_uid==0, remuser, locuser, from_af) < 0) { return NULL; } return pwd; #endif } -static const char *findhostname(struct sockaddr_in *fromp, +static const char *findhostname(struct sockaddr *fromp, const char *remuser, const char *locuser, - const char *cmdbuf) + const char *cmdbuf) { - struct hostent *hp; const char *hostname; - - hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), - fromp->sin_family); + char hbuf[NI_MAXHOST]; + char naddr[NI_MAXHOST]; + char raddr[NI_MAXHOST]; + struct addrinfo hints, *res, *res0; + int gaierr; + + if ((gaierr = getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), + NULL, 0, 0))) { + error("getnameinfo: %s\n", gai_strerror(gaierr)); + exit(1); + } errno = ENOMEM; /* malloc (thus strdup) may not set it */ - if (hp) hostname = strdup(hp->h_name); - else hostname = strdup(inet_ntoa(fromp->sin_addr)); + hostname = strdup(hbuf); /* not free */ if (hostname==NULL) { /* out of memory? */ @@ -302,34 +310,44 @@ exit(1); } - /* - * Attempt to confirm the DNS. - */ -#ifdef RES_DNSRCH - _res.options &= ~RES_DNSRCH; -#endif - hp = gethostbyname(hostname); - if (hp == NULL) { - syslog(LOG_INFO, "Couldn't look up address for %s", hostname); - fail("Couldn't get address for your host (%s)\n", - remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf); - } - while (hp->h_addr_list[0] != NULL) { - if (!memcmp(hp->h_addr_list[0], &fromp->sin_addr, - sizeof(fromp->sin_addr))) { - return hostname; - } - hp->h_addr_list++; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = from_af; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + if ((gaierr = getaddrinfo(hbuf, NULL, &hints, &res0))) { + syslog(LOG_INFO, "Couldn't look up address for %s: %s", + hbuf, gai_strerror(gaierr)); + fail("Couldn't get address for your host (%s)\n", + remuser, hbuf, locuser, cmdbuf); + } else { + if (getnameinfo(fromp, fromlen, naddr, sizeof(naddr), + NULL, 0, NI_NUMERICHOST)) + strcpy(naddr, "???"); + for (res = res0; res; res = res->ai_next) { + if (res->ai_family != from_af) + continue; + if (getnameinfo(res->ai_addr, res->ai_addrlen, + raddr, sizeof(raddr), NULL, 0, + NI_NUMERICHOST) == 0 + && strcmp(naddr, raddr) == 0) { + break; /* match */ + } + } + if (res) { + freeaddrinfo(res0); + return hostname; + } } syslog(LOG_NOTICE, "Host addr %s not listed for host %s", - inet_ntoa(fromp->sin_addr), hp->h_name); - fail("Host address mismatch for %s\n", - remuser, inet_ntoa(fromp->sin_addr), locuser, cmdbuf); + naddr, res0->ai_canonname ? res0->ai_canonname : "???"); + freeaddrinfo(res0); + fail("Host address mismatch for %s\n", + remuser, naddr, locuser, cmdbuf); return NULL; /* not reachable */ } static void -doit(struct sockaddr_in *fromp) +doit(struct sockaddr *fromp) { char cmdbuf[ARG_MAX+1]; const char *theshell, *shellname; @@ -350,7 +368,7 @@ if (port != 0) { int lport = IPPORT_RESERVED - 1; - sock = rresvport(&lport); + sock = rresvport_af(&lport, from_af); if (sock < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); @@ -359,9 +377,11 @@ syslog(LOG_ERR, "2nd port not reserved\n"); exit(1); } - fromp->sin_port = htons(port); - if (connect(sock, (struct sockaddr *)fromp, - sizeof(*fromp)) < 0) { + if (from_af == AF_INET6) + ((struct sockaddr_in6 *)fromp)->sin6_port = htons(port); + else + ((struct sockaddr_in *)fromp)->sin_port = htons(port); + if (connect(sock, fromp, fromlen) < 0) { syslog(LOG_INFO, "connect second port: %m"); exit(1); } @@ -384,7 +404,7 @@ setpwent(); pwd = doauth(remuser, hostname, locuser); if (pwd == NULL) { - fail("Permission denied.\n", + fail("Permission denied.\n", remuser, hostname, locuser, cmdbuf); } @@ -416,15 +436,15 @@ exit(1); } if (pid) { - close(0); + close(0); close(1); - close(2); + close(2); close(pv[1]); stderr_parent(sock, pv[0], pid); /* NOTREACHED */ } setpgrp(); - close(sock); + close(sock); close(pv[0]); dup2(pv[1], 2); close(pv[1]); @@ -479,7 +499,7 @@ } /* - * Close all fds, in case libc has left fun stuff like + * Close all fds, in case libc has left fun stuff like * /etc/shadow open. */ for (ifd = getdtablesize()-1; ifd > 2; ifd--) close(ifd); @@ -489,18 +509,24 @@ exit(1); } -static void network_init(int fd, struct sockaddr_in *fromp) +static void network_init(int fd, struct sockaddr *fromp) { struct linger linger; - socklen_t fromlen; int on=1; int port; - fromlen = sizeof(*fromp); + fromlen = sizeof(struct sockaddr_storage); if (getpeername(fd, (struct sockaddr *) fromp, &fromlen) < 0) { syslog(LOG_ERR, "getpeername: %m"); _exit(1); } + if (fromp->sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)fromp)->sin6_addr)) { + fromp->sa_family = AF_INET; + ((struct sockaddr_in *)fromp)->sin_addr.s_addr = + ((struct sockaddr_in6 *)fromp)->sin6_addr.s6_addr32[3]; + } + from_af = fromp->sa_family; if (keepalive && setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) @@ -511,12 +537,13 @@ sizeof (linger)) < 0) syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); - if (fromp->sin_family != AF_INET) { + if (from_af != AF_INET && from_af != AF_INET6) { syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", - fromp->sin_family); + from_af); exit(1); } #ifdef IP_OPTIONS + if (from_af == AF_INET) { u_char optbuf[BUFSIZ/3], *cp; char lbuf[BUFSIZ+1], *lp; @@ -528,7 +555,7 @@ ipproto = ip->p_proto; else ipproto = IPPROTO_IP; - if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && + if (!getsockopt(fd, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && optsize != 0) { lp = lbuf; @@ -543,9 +570,9 @@ syslog(LOG_NOTICE, "Connection received from %s using IP options" " (ignored): %s", - inet_ntoa(fromp->sin_addr), lbuf); + inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr),lbuf); - if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) { + if (setsockopt(fd, ipproto, IP_OPTIONS, NULL, optsize) != 0) { syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); exit(1); } @@ -556,10 +583,18 @@ /* * Check originating port for validity. */ - port = ntohs(fromp->sin_port); + if (from_af == AF_INET6) + port = ntohs(((struct sockaddr_in6 *)fromp)->sin6_port); + else + port = ntohs(((struct sockaddr_in *)fromp)->sin_port); if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { + char hbuf[MAXHOSTNAMELEN]; + + if (getnameinfo(fromp, fromlen, hbuf, sizeof(hbuf), + NULL, 0, NI_NUMERICHOST)) + strcpy(hbuf, "???"); syslog(LOG_NOTICE|LOG_AUTH, "Connection from %s on illegal port", - inet_ntoa(fromp->sin_addr)); + hbuf); exit(1); } } @@ -568,7 +603,7 @@ main(int argc, char *argv[]) { int ch; - struct sockaddr_in from; + struct sockaddr_storage from; _check_rhosts_file=1; openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); @@ -611,11 +646,7 @@ "pam_rhosts_auth in /etc/pam.conf"); #endif /* USE_PAM */ - network_init(0, &from); - doit(&from); + network_init(0, (struct sockaddr *)&from); + doit((struct sockaddr *)&from); return 0; } - - - -
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor