diff --git a/etc/pam.conf b/etc/pam.conf index 820f808edb15..661759c1f711 100644 --- a/etc/pam.conf +++ b/etc/pam.conf @@ -1,45 +1,42 @@ # Configuration file for Pluggable Authentication Modules (PAM). # # This file controls the authentication methods that login and other # utilities use. See pam(8) for a description of its format. # # Note: the final entry must say "required" -- otherwise, things don't # work quite right. If you delete the final entry, be sure to change # "sufficient" to "required" in the entry before it. # # $FreeBSD$ # If the user can authenticate with S/Key, that's sufficient; allow clear # password. Try kerberos, then try plain unix password. login auth sufficient pam_skey.so login auth requisite pam_cleartext_pass_ok.so #login auth sufficient pam_kerberosIV.so try_first_pass login auth required pam_unix.so try_first_pass # Same requirement for ftpd as login ftpd auth sufficient pam_skey.so ftpd auth requisite pam_cleartext_pass_ok.so #ftpd auth sufficient pam_kerberosIV.so try_first_pass ftpd auth required pam_unix.so try_first_pass -# r-utils are broken; ensure this doesn't bother folk -rshd auth required pam_deny.so - # Don't break startx xserver auth required pam_permit.so # XDM is difficult; it fails or moans unless there are modules for each # of the four management groups; auth, account, session and password. xdm auth required pam_unix.so #xdm auth sufficient pam_kerberosIV.so try_first_pass xdm account required pam_unix.so try_first_pass xdm session required pam_deny.so xdm password required pam_deny.so # Mail services imap auth required pam_unix.so try_first_pass pop3 auth required pam_unix.so try_first_pass # If we don't match anything else, default to using getpwnam(). other auth required pam_unix.so try_first_pass other account required pam_unix.so try_first_pass diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile index f22df18a1bf8..5cb2e96b9531 100644 --- a/libexec/rlogind/Makefile +++ b/libexec/rlogind/Makefile @@ -1,19 +1,11 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD$ PROG= rlogind SRCS= rlogind.c MAN8= rlogind.8 DPADD= ${LIBUTIL} LDADD= -lutil CFLAGS+= -Wall -DINET6 -.if defined(NOPAM) -CFLAGS+= -DNO_PAM -.else -SRCS+= auth_pam.c -DPADD+= ${LIBPAM} -LDADD+= ${MINUSLPAM} -.endif - .include diff --git a/libexec/rlogind/auth_pam.c b/libexec/rlogind/auth_pam.c deleted file mode 100644 index e45210783ed8..000000000000 --- a/libexec/rlogind/auth_pam.c +++ /dev/null @@ -1,142 +0,0 @@ -/*- - * Copyright (c) 1999 John Polstra - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - * - * $FreeBSD$ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Attempt to authenticate the user using PAM. Returns 0 if the user is - * authenticated, or 1 if not authenticated. If some sort of PAM system - * error occurs (e.g., the "/etc/pam.conf" file is missing) then this - * function returns -1. This can be used as an indication that we should - * fall back to a different authentication mechanism. - */ - -int -auth_pam(char *username) -{ - struct passwd *pawd; - pam_handle_t *pamh = NULL; - const char *tmpl_user; - const void *item; - int rval; - char *tty, *ttyn; - char hostname[MAXHOSTNAMELEN]; - char tname[sizeof(_PATH_TTY) + 10]; - int e; - - static struct pam_conv conv = { misc_conv, NULL }; - - ttyn = ttyname(STDIN_FILENO); - - if (ttyn == NULL || *ttyn == '\0') { - (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); - ttyn = tname; - } - if ((tty = strrchr(ttyn, '/')) != NULL) - ++tty; - else - tty = ttyn; - - rval = gethostname(hostname, sizeof(hostname)); - - if (rval < 0) { - syslog(LOG_ERR, "auth_pam: Failed to resolve local hostname"); - return -1; - } - if ((e = pam_start("rshd", username, &conv, &pamh)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, e)); - return -1; - } - if ((e = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_set_item(PAM_TTY): %s", - pam_strerror(pamh, e)); - return -1; - } - if (hostname != NULL && - (e = pam_set_item(pamh,PAM_RHOST, hostname)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", - pam_strerror(pamh, e)); - return -1; - } - - e = pam_authenticate(pamh, 0); - - switch (e) { - case PAM_SUCCESS: - /* - * With PAM we support the concept of a "template" - * user. The user enters a login name which is - * authenticated by PAM, usually via a remote service - * such as RADIUS or TACACS+. If authentication - * succeeds, a different but related "template" name - * is used for setting the credentials, shell, and - * home directory. The name the user enters need only - * exist on the remote authentication server, but the - * template name must be present in the local password - * database. - * - * This is supported by two various mechanisms in the - * individual modules. However, from the application's - * point of view, the template user is always passed - * back as a changed value of the PAM_USER item. - */ - if ((e = pam_get_item(pamh, PAM_USER, &item)) == - PAM_SUCCESS) { - tmpl_user = (const char *) item; - if (strcmp(username, tmpl_user) != 0) - pawd = getpwnam(tmpl_user); - } else - syslog(LOG_ERR, "Couldn't get PAM_USER: %s", pam_strerror(pamh, e)); - rval = 0; - break; - - case PAM_AUTH_ERR: - case PAM_USER_UNKNOWN: - case PAM_MAXTRIES: - rval = 1; - break; - - default: - syslog(LOG_ERR, "auth_pam: %s", pam_strerror(pamh, e)); - rval = -1; - break; - } - if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); - rval = -1; - } - return rval; -} diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c index eec34e752579..46833a6b3592 100644 --- a/libexec/rlogind/rlogind.c +++ b/libexec/rlogind/rlogind.c @@ -1,668 +1,644 @@ /*- * Copyright (c) 1983, 1988, 1989, 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * remote login server: * \0 * remuser\0 * locuser\0 * terminal_type/speed\0 * data */ #define FD_SETSIZE 16 /* don't need many bits for select */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" -#ifndef NO_PAM -#include -#include -#endif #ifndef TIOCPKT_WINDOW #define TIOCPKT_WINDOW 0x80 #endif #define ARGSTR "Dalnx" /* wrapper for KAME-special getnameinfo() */ #ifndef NI_WITHSCOPEID #define NI_WITHSCOPEID 0 #endif char *env[2]; #define NMAX 30 char lusername[NMAX+1], rusername[NMAX+1]; static char term[64] = "TERM="; #define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ int keepalive = 1; int check_all = 0; int no_delay; struct passwd *pwd; union sockunion { struct sockinet { u_char si_len; u_char si_family; u_short si_port; } su_si; struct sockaddr_in su_sin; struct sockaddr_in6 su_sin6; }; #define su_len su_si.si_len #define su_family su_si.si_family #define su_port su_si.si_port void doit __P((int, union sockunion *)); int control __P((int, char *, int)); void protocol __P((int, int)); void cleanup __P((int)); void fatal __P((int, char *, int)); int do_rlogin __P((union sockunion *)); void getstr __P((char *, int, char *)); void setup_term __P((int)); int do_krb_login __P((struct sockaddr_in *)); void usage __P((void)); -#ifndef NO_PAM -extern int auth_pam __P((char *)); -#endif int main(argc, argv) int argc; char *argv[]; { extern int __check_rhosts_file; union sockunion from; int ch, fromlen, on; openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); opterr = 0; while ((ch = getopt(argc, argv, ARGSTR)) != -1) switch (ch) { case 'D': no_delay = 1; break; case 'a': check_all = 1; break; case 'l': __check_rhosts_file = 0; break; case 'n': keepalive = 0; break; #ifdef CRYPT case 'x': doencrypt = 1; break; #endif case '?': default: usage(); break; } argc -= optind; argv += optind; fromlen = sizeof (from); if (getpeername(0, (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); } on = 1; if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); if (no_delay && setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); if (from.su_family == AF_INET) { on = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); } doit(0, &from); return 0; } int child; int netf; char line[MAXPATHLEN]; int confirmed; struct winsize win = { 0, 0, 0, 0 }; void doit(f, fromp) int f; union sockunion *fromp; { int master, pid, on = 1; int authenticated = 0; char hostname[2 * MAXHOSTNAMELEN + 1]; char nameinfo[2 * INET6_ADDRSTRLEN + 1]; char c; alarm(60); read(f, &c, 1); if (c != 0) exit(1); alarm(0); realhostname_sa(hostname, sizeof(hostname) - 1, (struct sockaddr *)fromp, fromp->su_len); /* error check ? */ fromp->su_port = ntohs((u_short)fromp->su_port); hostname[sizeof(hostname) - 1] = '\0'; { if ((fromp->su_family != AF_INET #ifdef INET6 && fromp->su_family != AF_INET6 #endif ) || fromp->su_port >= IPPORT_RESERVED || fromp->su_port < IPPORT_RESERVED/2) { getnameinfo((struct sockaddr *)fromp, fromp->su_len, nameinfo, sizeof(nameinfo), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); /* error check ? */ syslog(LOG_NOTICE, "Connection from %s on illegal port", nameinfo); fatal(f, "Permission denied", 0); } #ifdef IP_OPTIONS if (fromp->su_family == AF_INET) { u_char optbuf[BUFSIZ/3]; int optsize = sizeof(optbuf), ipproto, i; 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) { for (i = 0; i < optsize; ) { u_char c = optbuf[i]; if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "Connection refused from %s with IP option %s", inet_ntoa(fromp->su_sin.sin_addr), c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } if (c == IPOPT_EOL) break; i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } #endif if (do_rlogin(fromp) == 0) authenticated++; } if (confirmed == 0) { write(f, "", 1); confirmed = 1; /* we sent the null! */ } #ifdef CRYPT if (doencrypt) (void) des_enc_write(f, SECURE_MESSAGE, strlen(SECURE_MESSAGE), schedule, &kdata->session); #endif netf = f; pid = forkpty(&master, line, NULL, &win); if (pid < 0) { if (errno == ENOENT) fatal(f, "Out of ptys", 0); else fatal(f, "Forkpty", 1); } if (pid == 0) { if (f > 2) /* f should always be 0, but... */ (void) close(f); setup_term(0); if (*lusername=='-') { syslog(LOG_ERR, "tried to pass user \"%s\" to login", lusername); fatal(STDERR_FILENO, "invalid user", 0); } if (authenticated) { execl(_PATH_LOGIN, "login", "-p", "-h", hostname, "-f", lusername, (char *)NULL); } else execl(_PATH_LOGIN, "login", "-p", "-h", hostname, lusername, (char *)NULL); fatal(STDERR_FILENO, _PATH_LOGIN, 1); /*NOTREACHED*/ } #ifdef CRYPT /* * If encrypted, don't turn on NBIO or the des read/write * routines will croak. */ if (!doencrypt) #endif ioctl(f, FIONBIO, &on); ioctl(master, FIONBIO, &on); ioctl(master, TIOCPKT, &on); signal(SIGCHLD, cleanup); protocol(f, master); signal(SIGCHLD, SIG_IGN); cleanup(0); } char magic[2] = { 0377, 0377 }; char oobdata[] = {TIOCPKT_WINDOW}; /* * Handle a "control" request (signaled by magic being present) * in the data stream. For now, we are only willing to handle * window size changes. */ int control(pty, cp, n) int pty; char *cp; int n; { struct winsize w; if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') return (0); oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ bcopy(cp+4, (char *)&w, sizeof(w)); w.ws_row = ntohs(w.ws_row); w.ws_col = ntohs(w.ws_col); w.ws_xpixel = ntohs(w.ws_xpixel); w.ws_ypixel = ntohs(w.ws_ypixel); (void)ioctl(pty, TIOCSWINSZ, &w); return (4+sizeof (w)); } /* * rlogin "protocol" machine. */ void protocol(f, p) register int f, p; { char pibuf[1024+1], fibuf[1024], *pbp, *fbp; int pcc = 0, fcc = 0; int cc, nfd, n; char cntl; /* * Must ignore SIGTTOU, otherwise we'll stop * when we try and set slave pty's window shape * (our controlling tty is the master pty). */ (void) signal(SIGTTOU, SIG_IGN); send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ if (f > p) nfd = f + 1; else nfd = p + 1; if (nfd > FD_SETSIZE) { syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); fatal(f, "internal error (select mask too small)", 0); } for (;;) { fd_set ibits, obits, ebits, *omask; FD_ZERO(&ebits); FD_ZERO(&ibits); FD_ZERO(&obits); omask = (fd_set *)NULL; if (fcc) { FD_SET(p, &obits); omask = &obits; } else FD_SET(f, &ibits); if (pcc >= 0) { if (pcc) { FD_SET(f, &obits); omask = &obits; } else FD_SET(p, &ibits); } FD_SET(p, &ebits); if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { if (errno == EINTR) continue; fatal(f, "select", 1); } if (n == 0) { /* shouldn't happen... */ sleep(5); continue; } #define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) if (FD_ISSET(p, &ebits)) { cc = read(p, &cntl, 1); if (cc == 1 && pkcontrol(cntl)) { cntl |= oobdata[0]; send(f, &cntl, 1, MSG_OOB); if (cntl & TIOCPKT_FLUSHWRITE) { pcc = 0; FD_CLR(p, &ibits); } } } if (FD_ISSET(f, &ibits)) { #ifdef CRYPT if (doencrypt) fcc = des_enc_read(f, fibuf, sizeof(fibuf), schedule, &kdata->session); else #endif fcc = read(f, fibuf, sizeof(fibuf)); if (fcc < 0 && errno == EWOULDBLOCK) fcc = 0; else { register char *cp; int left, n; if (fcc <= 0) break; fbp = fibuf; top: for (cp = fibuf; cp < fibuf+fcc-1; cp++) if (cp[0] == magic[0] && cp[1] == magic[1]) { left = fcc - (cp-fibuf); n = control(p, cp, left); if (n) { left -= n; if (left > 0) bcopy(cp+n, cp, left); fcc -= n; goto top; /* n^2 */ } } FD_SET(p, &obits); /* try write */ } } if (FD_ISSET(p, &obits) && fcc > 0) { cc = write(p, fbp, fcc); if (cc > 0) { fcc -= cc; fbp += cc; } } if (FD_ISSET(p, &ibits)) { pcc = read(p, pibuf, sizeof (pibuf)); pbp = pibuf; if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else if (pcc <= 0) break; else if (pibuf[0] == 0) { pbp++, pcc--; #ifdef CRYPT if (!doencrypt) #endif FD_SET(f, &obits); /* try write */ } else { if (pkcontrol(pibuf[0])) { pibuf[0] |= oobdata[0]; send(f, &pibuf[0], 1, MSG_OOB); } pcc = 0; } } if ((FD_ISSET(f, &obits)) && pcc > 0) { #ifdef CRYPT if (doencrypt) cc = des_enc_write(f, pbp, pcc, schedule, &kdata->session); else #endif cc = write(f, pbp, pcc); if (cc < 0 && errno == EWOULDBLOCK) { /* * This happens when we try write after read * from p, but some old kernels balk at large * writes even when select returns true. */ if (!FD_ISSET(p, &ibits)) sleep(5); continue; } if (cc > 0) { pcc -= cc; pbp += cc; } } } } void cleanup(signo) int signo; { char *p; p = line + sizeof(_PATH_DEV) - 1; if (logout(p)) logwtmp(p, "", ""); (void)chflags(line, 0); (void)chmod(line, 0666); (void)chown(line, 0, 0); *p = 'p'; (void)chflags(line, 0); (void)chmod(line, 0666); (void)chown(line, 0, 0); shutdown(netf, 2); exit(1); } void fatal(f, msg, syserr) int f; char *msg; int syserr; { int len; char buf[BUFSIZ], *bp = buf; /* * Prepend binary one to message if we haven't sent * the magic null as confirmation. */ if (!confirmed) *bp++ = '\01'; /* error indicator */ if (syserr) len = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n", msg, strerror(errno)); else len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg); (void) write(f, buf, bp + len - buf); exit(1); } int do_rlogin(dest) union sockunion *dest; { -#ifndef NO_PAM - int retval; -#endif getstr(rusername, sizeof(rusername), "remuser too long"); getstr(lusername, sizeof(lusername), "locuser too long"); getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); -#ifndef NO_PAM - retval = auth_pam(lusername); - - if (retval) { - if (retval == -1) { - syslog(LOG_ERR, "PAM authentication failed"); - } - else { - syslog(LOG_ERR, - "User %s failed PAM authentication", lusername); - exit(1); - } - } -#endif pwd = getpwnam(lusername); if (pwd == NULL) return (-1); /* XXX why don't we syslog() failure? */ return (iruserok_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername, lusername)); } void getstr(buf, cnt, errmsg) char *buf; int cnt; char *errmsg; { char c; do { if (read(0, &c, 1) != 1) exit(1); if (--cnt < 0) fatal(STDOUT_FILENO, errmsg, 0); *buf++ = c; } while (c != 0); } extern char **environ; void setup_term(fd) int fd; { register char *cp = index(term+ENVSIZE, '/'); char *speed; struct termios tt; #ifndef notyet tcgetattr(fd, &tt); if (cp) { *cp++ = '\0'; speed = cp; cp = index(speed, '/'); if (cp) *cp++ = '\0'; cfsetspeed(&tt, atoi(speed)); } tt.c_iflag = TTYDEF_IFLAG; tt.c_oflag = TTYDEF_OFLAG; tt.c_lflag = TTYDEF_LFLAG; tcsetattr(fd, TCSAFLUSH, &tt); #else if (cp) { *cp++ = '\0'; speed = cp; cp = index(speed, '/'); if (cp) *cp++ = '\0'; tcgetattr(fd, &tt); cfsetspeed(&tt, atoi(speed)); tcsetattr(fd, TCSAFLUSH, &tt); } #endif env[0] = term; env[1] = 0; environ = env; } void usage() { syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]"); } diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile index b54f7632e029..2e40e4fdad4e 100644 --- a/libexec/rshd/Makefile +++ b/libexec/rshd/Makefile @@ -1,27 +1,18 @@ # From: @(#)Makefile 8.1 (Berkeley) 6/4/93 # $FreeBSD$ PROG= rshd SRCS= rshd.c MAN8= rshd.8 -.if defined(NOPAM) -CFLAGS+= -DNO_PAM -.else -#CFLAGS+= -DCRYPT -DNO_PAM -SRCS+= auth_pam.c -DPADD+= ${LIBPAM} -LDADD+= ${MINUSLPAM} -.endif +#CFLAGS+= -DCRYPT # For login_cap handling CFLAGS+=-DLOGIN_CAP -Wall DPADD+= ${LIBUTIL} LDADD+= -lutil # IPv6 support CFLAGS+= -DINET6 .include - -.PATH: ${.CURDIR}/../rlogind diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c index 374d1374c39a..27f992edf74d 100644 --- a/libexec/rshd/rshd.c +++ b/libexec/rshd/rshd.c @@ -1,746 +1,728 @@ /*- * Copyright (c) 1988, 1989, 1992, 1993, 1994 * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * remote shell server: * [port]\0 * remuser\0 * locuser\0 * command\0 * data */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LOGIN_CAP #include #endif /* wrapper for KAME-special getnameinfo() */ #ifndef NI_WITHSCOPEID #define NI_WITHSCOPEID 0 #endif int keepalive = 1; int log_success; /* If TRUE, log all successful accesses */ int sent_null; int no_delay; #ifdef CRYPT int doencrypt = 0; #endif union sockunion { struct sockinet { u_char si_len; u_char si_family; u_short si_port; } su_si; struct sockaddr_in su_sin; struct sockaddr_in6 su_sin6; }; #define su_len su_si.si_len #define su_family su_si.si_family #define su_port su_si.si_port void doit __P((union sockunion *)); void error __P((const char *, ...)); void getstr __P((char *, int, char *)); int local_domain __P((char *)); char *topdomain __P((char *)); void usage __P((void)); -#ifndef NO_PAM -extern int auth_pam __P((char *)); -#endif #define OPTIONS "alnDL" int main(argc, argv) int argc; char *argv[]; { extern int __check_rhosts_file; struct linger linger; int ch, on = 1, fromlen; struct sockaddr_storage from; openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); opterr = 0; while ((ch = getopt(argc, argv, OPTIONS)) != -1) switch (ch) { case 'a': /* ignored for compatability */ break; case 'l': __check_rhosts_file = 0; break; case 'n': keepalive = 0; break; #ifdef CRYPT case 'x': doencrypt = 1; break; #endif case 'D': no_delay = 1; break; case 'L': log_success = 1; break; case '?': default: usage(); break; } argc -= optind; argv += optind; #ifdef CRYPT if (doencrypt) { syslog(LOG_ERR, "-k is required for -x"); exit(2); } #endif fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { syslog(LOG_ERR, "getpeername: %m"); exit(1); } if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); linger.l_onoff = 1; linger.l_linger = 60; /* XXX */ if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof (linger)) < 0) syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); if (no_delay && setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); doit((union sockunion *)&from); /* NOTREACHED */ return(0); } char username[20] = "USER="; char homedir[64] = "HOME="; char shell[64] = "SHELL="; char path[100] = "PATH="; char *envinit[] = {homedir, shell, path, username, 0}; char **environ; void doit(fromp) union sockunion *fromp; { extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ struct passwd *pwd; u_short port; fd_set ready, readfrom; int cc, nfd, pv[2], pid, s; int one = 1; char *errorstr; char *cp, sig, buf[BUFSIZ]; char cmdbuf[NCARGS+1], locuser[16], remuser[16]; char fromhost[2 * MAXHOSTNAMELEN + 1]; char numericname[INET6_ADDRSTRLEN]; int af = fromp->su_family, err; - int retval; #ifdef CRYPT int rc; int pv1[2], pv2[2]; #endif #ifdef LOGIN_CAP login_cap_t *lc; #endif (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); fromp->su_port = ntohs((u_short)fromp->su_port); if (af != AF_INET #ifdef INET6 && af != AF_INET6 #endif ) { syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", af); exit(1); } err = getnameinfo((struct sockaddr *)fromp, fromp->su_len, numericname, sizeof(numericname), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); /* XXX: do 'err' check */ #ifdef IP_OPTIONS if (af == AF_INET) { u_char optbuf[BUFSIZ/3]; int optsize = sizeof(optbuf), ipproto, i; 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) && optsize != 0) { for (i = 0; i < optsize; ) { u_char c = optbuf[i]; if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "connection refused from %s with IP option %s", numericname, c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } if (c == IPOPT_EOL) break; i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } #endif if (fromp->su_port >= IPPORT_RESERVED || fromp->su_port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "connection from %s on illegal port %u", numericname, fromp->su_port); exit(1); } (void) alarm(60); port = 0; s = 0; /* not set or used if port == 0 */ for (;;) { char c; if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { if (cc < 0) syslog(LOG_NOTICE, "read: %m"); shutdown(0, 1+1); exit(1); } if (c == 0) break; port = port * 10 + c - '0'; } (void) alarm(0); if (port != 0) { int lport = IPPORT_RESERVED - 1; s = rresvport_af(&lport, af); if (s < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); } if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "2nd socket from %s on unreserved port %u", numericname, port); exit(1); } fromp->su_port = htons(port); if (connect(s, (struct sockaddr *)fromp, fromp->su_len) < 0) { syslog(LOG_INFO, "connect second port %d: %m", port); exit(1); } } errorstr = NULL; realhostname_sa(fromhost, sizeof(fromhost) - 1, (struct sockaddr *)fromp, fromp->su_len); fromhost[sizeof(fromhost) - 1] = '\0'; #ifdef CRYPT if (doencrypt && af == AF_INET) { struct sockaddr_in local_addr; rc = sizeof(local_addr); if (getsockname(0, (struct sockaddr *)&local_addr, &rc) < 0) { syslog(LOG_ERR, "getsockname: %m"); error("rlogind: getsockname: %m"); exit(1); } authopts = KOPT_DO_MUTUAL; rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, &fromaddr, &local_addr, kdata, "", schedule, version); des_set_key(&kdata->session, schedule); } #endif getstr(remuser, sizeof(remuser), "remuser"); getstr(locuser, sizeof(locuser), "locuser"); getstr(cmdbuf, sizeof(cmdbuf), "command"); setpwent(); pwd = getpwnam(locuser); if (pwd == NULL) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unknown login. cmd='%.80s'", remuser, fromhost, locuser, cmdbuf); if (errorstr == NULL) errorstr = "Login incorrect.\n"; goto fail; } #ifdef LOGIN_CAP lc = login_getpwclass(pwd); #endif if (chdir(pwd->pw_dir) < 0) { #ifdef LOGIN_CAP if (chdir("/") < 0 || login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: no home directory. cmd='%.80s'", remuser, fromhost, locuser, cmdbuf); error("No remote home directory.\n"); exit(0); } #else (void) chdir("/"); #ifdef notdef syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: no home directory. cmd='%.80s'", remuser, fromhost, locuser, cmdbuf); error("No remote directory.\n"); exit(1); #endif #endif pwd->pw_dir = "/"; } -#ifndef NO_PAM - retval = auth_pam(locuser); - - if (retval) { - if (retval == -1) { - syslog(LOG_ERR,"PAM authentication failed"); - } - else { - syslog(LOG_ERR, - "User %s failed PAM authentication", locuser); - exit(1); - } - } -#endif if (errorstr || (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || iruserok_sa(fromp, fromp->su_len, pwd->pw_uid == 0, remuser, locuser) < 0) { if (__rcmd_errstr) syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", remuser, fromhost, locuser, __rcmd_errstr, cmdbuf); else syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied. cmd='%.80s'", remuser, fromhost, locuser, cmdbuf); fail: if (errorstr == NULL) errorstr = "Login incorrect.\n"; error(errorstr, fromhost); exit(1); } if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { error("Logins currently disabled.\n"); exit(1); } #ifdef LOGIN_CAP if (lc != NULL && fromp->su_family == AF_INET) { /*XXX*/ char remote_ip[MAXHOSTNAMELEN]; strncpy(remote_ip, numericname, sizeof(remote_ip) - 1); remote_ip[sizeof(remote_ip) - 1] = 0; if (!auth_hostok(lc, fromhost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", remuser, fromhost, locuser, __rcmd_errstr, cmdbuf); error("Login incorrect.\n"); exit(1); } if (!auth_timeok(lc, time(NULL))) { error("Logins not available right now\n"); exit(1); } } #endif /* !LOGIN_CAP */ #if BSD > 43 /* before fork, while we're session leader */ if (setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failed: %m"); #endif (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; if (port) { if (pipe(pv) < 0) { error("Can't make pipe.\n"); exit(1); } #ifdef CRYPT if (doencrypt) { if (pipe(pv1) < 0) { error("Can't make 2nd pipe.\n"); exit(1); } if (pipe(pv2) < 0) { error("Can't make 3rd pipe.\n"); exit(1); } } #endif pid = fork(); if (pid == -1) { error("Can't fork; try again.\n"); exit(1); } if (pid) { #ifdef CRYPT if (doencrypt) { static char msg[] = SECURE_MESSAGE; (void) close(pv1[1]); (void) close(pv2[1]); des_enc_write(s, msg, sizeof(msg) - 1, schedule, &kdata->session); } else #endif { (void) close(0); (void) close(1); } (void) close(2); (void) close(pv[1]); FD_ZERO(&readfrom); FD_SET(s, &readfrom); FD_SET(pv[0], &readfrom); if (pv[0] > s) nfd = pv[0]; else nfd = s; #ifdef CRYPT if (doencrypt) { FD_ZERO(&writeto); FD_SET(pv2[0], &writeto); FD_SET(pv1[0], &readfrom); nfd = MAX(nfd, pv2[0]); nfd = MAX(nfd, pv1[0]); } else #endif ioctl(pv[0], FIONBIO, (char *)&one); /* should set s nbio! */ nfd++; do { ready = readfrom; #ifdef CRYPT if (doencrypt) { wready = writeto; if (select(nfd, &ready, &wready, (fd_set *) 0, (struct timeval *) 0) < 0) break; } else #endif if (select(nfd, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) break; if (FD_ISSET(s, &ready)) { int ret; #ifdef CRYPT if (doencrypt) ret = des_enc_read(s, &sig, 1, schedule, &kdata->session); else #endif ret = read(s, &sig, 1); if (ret <= 0) FD_CLR(s, &readfrom); else killpg(pid, sig); } if (FD_ISSET(pv[0], &ready)) { errno = 0; cc = read(pv[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(s, 1+1); FD_CLR(pv[0], &readfrom); } else { #ifdef CRYPT if (doencrypt) (void) des_enc_write(s, buf, cc, schedule, &kdata->session); else #endif (void) write(s, buf, cc); } } #ifdef CRYPT if (doencrypt && FD_ISSET(pv1[0], &ready)) { errno = 0; cc = read(pv1[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(pv1[0], 1+1); FD_CLR(pv1[0], &readfrom); } else (void) des_enc_write(STDOUT_FILENO, buf, cc, schedule, &kdata->session); } if (doencrypt && FD_ISSET(pv2[0], &wready)) { errno = 0; cc = des_enc_read(STDIN_FILENO, buf, sizeof(buf), schedule, &kdata->session); if (cc <= 0) { shutdown(pv2[0], 1+1); FD_CLR(pv2[0], &writeto); } else (void) write(pv2[0], buf, cc); } #endif } while (FD_ISSET(s, &readfrom) || #ifdef CRYPT (doencrypt && FD_ISSET(pv1[0], &readfrom)) || #endif FD_ISSET(pv[0], &readfrom)); exit(0); } setpgrp(0, getpid()); (void) close(s); (void) close(pv[0]); #ifdef CRYPT if (doencrypt) { close(pv1[0]); close(pv2[0]); dup2(pv1[1], 1); dup2(pv2[1], 0); close(pv1[1]); close(pv2[1]); } #endif dup2(pv[1], 2); close(pv[1]); } if (*pwd->pw_shell == '\0') pwd->pw_shell = _PATH_BSHELL; environ = envinit; strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); strcat(path, _PATH_DEFPATH); strncat(shell, pwd->pw_shell, sizeof(shell)-7); strncat(username, pwd->pw_name, sizeof(username)-6); cp = strrchr(pwd->pw_shell, '/'); if (cp) cp++; else cp = pwd->pw_shell; #ifdef LOGIN_CAP if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { syslog(LOG_ERR, "setusercontext: %m"); exit(1); } login_close(lc); #else (void) setgid((gid_t)pwd->pw_gid); initgroups(pwd->pw_name, pwd->pw_gid); (void) setuid((uid_t)pwd->pw_uid); #endif endpwent(); if (log_success || pwd->pw_uid == 0) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", remuser, fromhost, locuser, cmdbuf); } execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); perror(pwd->pw_shell); exit(1); } /* * Report error to client. Note: can't be used until second socket has * connected to client, or older clients will hang waiting for that * connection first. */ #if __STDC__ #include #else #include #endif void #if __STDC__ error(const char *fmt, ...) #else error(fmt, va_alist) char *fmt; va_dcl #endif { va_list ap; int len; char *bp, buf[BUFSIZ]; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif bp = buf; if (sent_null == 0) { *bp++ = 1; len = 1; } else len = 0; (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); (void)write(STDERR_FILENO, buf, len + strlen(bp)); } void getstr(buf, cnt, err) char *buf, *err; int cnt; { char c; do { if (read(STDIN_FILENO, &c, 1) != 1) exit(1); *buf++ = c; if (--cnt == 0) { error("%s too long\n", err); exit(1); } } while (c != 0); } /* * Check whether host h is in our local domain, * defined as sharing the last two components of the domain part, * or the entire domain part if the local domain has only one component. * If either name is unqualified (contains no '.'), * assume that the host is local, as it will be * interpreted as such. */ int local_domain(h) char *h; { char localhost[MAXHOSTNAMELEN]; char *p1, *p2; localhost[0] = 0; (void) gethostname(localhost, sizeof(localhost) - 1); localhost[sizeof(localhost) - 1] = '\0'; p1 = topdomain(localhost); p2 = topdomain(h); if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) return (1); return (0); } char * topdomain(h) char *h; { char *p, *maybe = NULL; int dots = 0; for (p = h + strlen(h); p >= h; p--) { if (*p == '.') { if (++dots == 2) return (p); maybe = p; } } return (maybe); } void usage() { syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); exit(2); }