summaryrefslogtreecommitdiffstats
path: root/src/appl
diff options
context:
space:
mode:
Diffstat (limited to 'src/appl')
-rw-r--r--src/appl/bsd/Imakefile70
-rw-r--r--src/appl/bsd/kcmd.c611
-rw-r--r--src/appl/bsd/krcp.c1420
-rw-r--r--src/appl/bsd/krlogin.c1468
-rw-r--r--src/appl/bsd/krlogind.M102
-rw-r--r--src/appl/bsd/krlogind.c1365
-rw-r--r--src/appl/bsd/krsh.c416
-rw-r--r--src/appl/bsd/krshd.M164
-rw-r--r--src/appl/bsd/krshd.c1340
-rw-r--r--src/appl/bsd/login.c684
-rw-r--r--src/appl/bsd/logutil.c178
-rw-r--r--src/appl/bsd/rcp.M130
-rw-r--r--src/appl/bsd/rlogin.M200
-rw-r--r--src/appl/bsd/rsh.M153
14 files changed, 8301 insertions, 0 deletions
diff --git a/src/appl/bsd/Imakefile b/src/appl/bsd/Imakefile
new file mode 100644
index 0000000000..fa3392e7ec
--- /dev/null
+++ b/src/appl/bsd/Imakefile
@@ -0,0 +1,70 @@
+# $Source$
+# $Author$
+# $Id$
+#
+# Copyright 1990,1991 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America is assumed
+# to require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. M.I.T. makes no representations about the suitability of
+# this software for any purpose. It is provided "as is" without express
+# or implied warranty.
+#
+#
+
+ DEPLIBS = $(DEPKLIB)
+#ifdef CrayArchitecture
+LOCAL_LIBRARIES = $(KLIB) -lshare -lm -lrsc
+#else
+LOCAL_LIBRARIES = $(KLIB)
+#endif
+ DEFINES = $(APPL_BSD_DEF)
+
+CLIENTSRCS= krcp.c krlogin.c krsh.c kcmd.c logutil.c
+CLIENTOBJS= krcp.o krlogin.o krsh.o kcmd.o logutil.o
+
+#ifdef CrayArchitecture
+SERVERSRCS= krshd.c
+SERVEROBJS= krshd.o
+#else
+SERVERSRCS= krlogind.c krshd.c
+SERVEROBJS= krlogind.o krshd.o
+#endif
+
+SRCS= $(CLIENTSRCS) $(SERVERSRCS)
+
+#ifdef CrayArchitecture
+all:: rsh rcp rlogin krshd
+#else
+all:: rsh rcp rlogin krshd krlogind
+#endif
+
+NormalProgramTarget(rsh,krsh.o kcmd.o,$(DEPLIBS),$(LOCAL_LIBRARIES),)
+Krb5InstallClientProgram(rsh)
+
+NormalProgramTarget(rcp,krcp.o kcmd.o,$(DEPLIBS),$(LOCAL_LIBRARIES),)
+Krb5InstallClientProgram(rcp)
+
+NormalProgramTarget(rlogin,krlogin.o kcmd.o,$(DEPLIBS),$(LOCAL_LIBRARIES),)
+Krb5InstallClientProgram(rlogin)
+
+NormalProgramTarget(krshd,krshd.o kcmd.o logutil.o,$(DEPLIBS),$(LOCAL_LIBRARIES),)
+Krb5InstallServerProgram(krshd)
+
+#ifndef CrayArchitecture
+NormalProgramTarget(krlogind,krlogind.o logutil.o kcmd.o,$(DEPLIBS),$(LOCAL_LIBRARIES),)
+#endif
+Krb5InstallServerProgram(krlogind)
+
+DependTarget()
diff --git a/src/appl/bsd/kcmd.c b/src/appl/bsd/kcmd.c
new file mode 100644
index 0000000000..a7b9f962e8
--- /dev/null
+++ b/src/appl/bsd/kcmd.c
@@ -0,0 +1,611 @@
+/*
+ * $Source$
+ * $Id$
+ */
+
+#ifndef lint
+static char *rcsid_kcmd_c =
+ "$Id$";
+#endif /* lint */
+#define LIBC_SCCS
+
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "derived from @(#)rcmd.c 5.17 (Berkeley) 6/27/88";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/param.h>
+#ifndef _TYPES_
+#include <sys/types.h>
+#define _TYPES_
+#endif
+
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+#include <sys/file.h>
+#include <sys/signal.h>
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+extern errno;
+
+#define START_PORT 5120 /* arbitrary */
+char *default_service = "host";
+
+
+
+kcmd(sock, ahost, rport, locuser, remuser, cmd, fd2p, service, realm,
+ cred, seqno, server_seqno, laddr, faddr, authopts)
+ int *sock;
+ char **ahost;
+ u_short rport;
+ char *locuser, *remuser, *cmd;
+ int *fd2p;
+ char *service;
+ char *realm;
+ krb5_creds **cred;
+ krb5_int32 *seqno;
+ krb5_int32 *server_seqno;
+ struct sockaddr_in *laddr, *faddr;
+ krb5_flags authopts;
+{
+ int s, timo = 1, pid;
+ long oldmask;
+ struct sockaddr_in sin, from, local_laddr;
+ krb5_creds *ret_cred = 0;
+ char c;
+ int lport = START_PORT;
+ struct hostent *hp;
+ int rc;
+ char *host_save;
+ krb5_error_code status;
+ krb5_error *err_ret;
+ krb5_ap_rep_enc_part *rep_ret;
+ krb5_checksum send_cksum;
+ krb5_principal server, client;
+ char *tmpstr = 0;
+ int sin_len;
+ krb5_ccache cc;
+ krb5_data outbuf;
+
+ pid = getpid();
+ hp = gethostbyname(*ahost);
+ if (hp == 0) {
+ fprintf(stderr, "%s: unknown host\n", *ahost);
+ return (-1);
+ }
+
+ host_save = malloc(strlen(hp->h_name) + 1);
+ if ( host_save == (char *) 0){
+ fprintf(stderr,"kcmd: no memory\n");
+ return(-1);
+ }
+
+ strcpy(host_save, hp->h_name);
+
+ *ahost = host_save;
+
+ /* If no service is given set to the default service */
+ if (!service) service = default_service;
+
+ sin_len = strlen(host_save) + strlen(service)
+ + (realm ? strlen(realm): 0) + 3;
+ if ( sin_len < 20 ) sin_len = 20;
+ tmpstr = malloc(sin_len);
+ if ( tmpstr == (char *) 0){
+ fprintf(stderr,"kcmd: no memory\n");
+ return(-1);
+ }
+
+ if (!(ret_cred = (krb5_creds *)calloc(1,sizeof(*ret_cred)))){
+ fprintf(stderr,"kcmd: no memory\n");
+ return(-1);
+ }
+ if ((realm == NULL) || (realm[0] == '\0')) {
+ krb5_sname_to_principal(host_save,service,1,&ret_cred->server);
+ }
+ else {
+ sprintf(tmpstr,"%s/%s@%s",service,host_save,realm);
+ krb5_parse_name(tmpstr,&ret_cred->server);
+ }
+#ifdef sgi
+ oldmask = sigignore(sigmask(SIGURG));
+#else
+ oldmask = sigblock(sigmask(SIGURG));
+#endif
+
+ for (;;) {
+ s = getport(&lport);
+ if (s < 0) {
+ if (errno == EAGAIN)
+ fprintf(stderr, "socket: All ports in use\n");
+ else
+ perror("kcmd: socket");
+#ifndef sgi
+ sigsetmask(oldmask);
+#endif
+ if (tmpstr) xfree(tmpstr);
+ if (host_save) xfree(host_save);
+ krb5_free_creds(ret_cred);
+ return (-1);
+ }
+#if defined (hpux) || defined (CRAY) /*hpux does not handle async
+ io thus setown is disabled */
+#else
+ fcntl(s, F_SETOWN, pid);
+#endif /* hpux */
+ sin.sin_family = hp->h_addrtype;
+ memcpy((caddr_t)&sin.sin_addr,hp->h_addr, hp->h_length);
+ sin.sin_port = rport;
+ if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
+ break;
+ (void) close(s);
+ if (errno == EADDRINUSE) {
+ lport--;
+ continue;
+ }
+ /*
+ * don't wait very long for Kerberos kcmd.
+ */
+ if (errno == ECONNREFUSED && timo <= 4) {
+ sleep(timo);
+ timo *= 2;
+ continue;
+ }
+#if !(defined(tek) || defined(ultrix) || defined(sun) || defined(SYSV))
+ if (hp->h_addr_list[1] != NULL) {
+ int oerrno = errno;
+
+ fprintf(stderr,
+ "connect to address %s: ", inet_ntoa(sin.sin_addr));
+ errno = oerrno;
+ perror(0);
+ hp->h_addr_list++;
+ memcpy((caddr_t)&sin.sin_addr,hp->h_addr_list[0],
+ hp->h_length);
+ fprintf(stderr, "Trying %s...\n",
+ inet_ntoa(sin.sin_addr));
+ continue;
+ }
+#endif /* !(defined(ultrix) || defined(sun)) */
+ perror(hp->h_name);
+#ifndef sgi
+ sigsetmask(oldmask);
+#endif
+ if (tmpstr) xfree(tmpstr);
+ if (host_save) xfree(host_save);
+ krb5_free_creds(ret_cred);
+ return (-1);
+ }
+ lport--;
+ if (fd2p == 0) {
+ write(s, "", 1);
+ lport = 0;
+ } else {
+ char num[8];
+ int s2 = getport(&lport), s3;
+ int len = sizeof (from);
+
+ if (s2 < 0) {
+ status = -1;
+ goto bad;
+ }
+ listen(s2, 1);
+ (void) sprintf(num, "%d", lport);
+ if (write(s, num, strlen(num)+1) != strlen(num)+1) {
+ perror("write: setting up stderr");
+ (void) close(s2);
+ status = -1;
+ goto bad;
+ }
+ s3 = accept(s2, (struct sockaddr *)&from, &len);
+ (void) close(s2);
+ if (s3 < 0) {
+ perror("accept");
+ lport = 0;
+ status = -1;
+ goto bad;
+ }
+ *fd2p = s3;
+ from.sin_port = ntohs((u_short)from.sin_port);
+ if (from.sin_family != AF_INET ||
+ from.sin_port >= IPPORT_RESERVED) {
+ fprintf(stderr,
+ "socket: protocol failure in circuit setup.\n");
+ goto bad2;
+ }
+ }
+
+ if (!laddr) laddr = &local_laddr;
+ if (!faddr) faddr = &sin;
+ else
+ memcpy(faddr,&sin,sizeof(sin));
+
+ sin_len = sizeof (struct sockaddr_in);
+ if (getsockname(s, (struct sockaddr *)laddr, &sin_len) < 0) {
+ perror("getsockname");
+ status = -1;
+ goto bad2;
+ }
+
+ /* compute checksum, using CRC-32 */
+ if (!(send_cksum.contents = (krb5_octet *)
+ malloc(krb5_checksum_size(CKSUMTYPE_CRC32)))) {
+ status = -1;
+ goto bad2;
+ }
+ /* choose some random stuff to compute checksum from */
+ sprintf(tmpstr,"%x %x",pid,pid);
+ if (status = krb5_calculate_checksum(CKSUMTYPE_CRC32,
+ tmpstr,
+ strlen(tmpstr),
+ 0,
+ 0, /* if length is 0, crc-32 doesn't
+ use the seed */
+ &send_cksum))
+ goto bad3;
+
+ status = krb5_cc_default(&cc);
+ if (status) goto bad3;
+
+ status = krb5_cc_get_principal(cc, &ret_cred->client);
+ if (status) goto bad3;
+
+ /* Get ticket from credentials cache or kdc */
+ status = krb5_get_credentials(0, cc, ret_cred);
+ if (status) goto bad3;
+
+ krb5_cc_close(cc);
+
+ /* call Kerberos library routine to obtain an authenticator,
+ pass it over the socket to the server, and obtain mutual
+ authentication. */
+ status = krb5_sendauth((krb5_pointer) &s,
+ "KCMDV0.1", ret_cred->client, ret_cred->server,
+ AP_OPTS_MUTUAL_REQUIRED,
+ &send_cksum,
+ ret_cred,
+ 0, /* We have the credentials */
+ seqno,
+ 0, /* don't need a subsession key */
+ 0, /* No error return */
+ &rep_ret);
+
+ if (status) goto bad3;
+ if (rep_ret && server_seqno) {
+ *server_seqno = rep_ret->seq_number;
+ krb5_free_ap_rep_enc_part(rep_ret);
+ }
+
+ outbuf.length = 0;
+
+ /* Send two empty messages. These will be used in a later release
+ to send a forwarded TGT and related info. */
+ if (status = krb5_write_message((krb5_pointer)&s, &outbuf))
+ goto bad3;
+ if (status = krb5_write_message((krb5_pointer)&s, &outbuf))
+ goto bad3;
+
+ (void) write(s, remuser, strlen(remuser)+1);
+ (void) write(s, cmd, strlen(cmd)+1);
+ (void) write(s, locuser, strlen(locuser)+1);
+
+ if ((rc=read(s, &c, 1)) != 1) {
+ if (rc==-1) {
+ perror(*ahost);
+ } else {
+ fprintf(stderr,"kcmd: bad connection with remote host\n");
+ }
+ status = -1;
+ goto bad3;
+ }
+ if (c != 0) {
+ while (read(s, &c, 1) == 1) {
+ (void) write(2, &c, 1);
+ if (c == '\n')
+ break;
+ }
+ status = -1;
+ goto bad3;
+ }
+#ifndef sgi
+ sigsetmask(oldmask);
+#endif
+ *sock = s;
+ if (tmpstr) xfree(tmpstr);
+ if (host_save) xfree(host_save);
+
+ /* Pass back credentials if wanted */
+ if (cred) krb5_copy_creds(ret_cred,cred);
+ krb5_free_creds(ret_cred);
+
+ return (0);
+ bad3:
+ free(send_cksum.contents);
+ bad2:
+ if (lport)
+ (void) close(*fd2p);
+ bad:
+ (void) close(s);
+#ifndef sgi
+ sigsetmask(oldmask);
+#endif
+ if (tmpstr) xfree(tmpstr);
+ if (host_save) xfree(host_save);
+ if (ret_cred)
+ krb5_free_creds(ret_cred);
+ return (status);
+}
+
+
+
+getport(alport)
+ int *alport;
+{
+ struct sockaddr_in sin;
+ int s;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return (-1);
+ for (;;) {
+ sin.sin_port = htons((u_short)*alport);
+ if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
+ return (s);
+ if (errno != EADDRINUSE) {
+ (void) close(s);
+ return (-1);
+ }
+ (*alport)--;
+ if (*alport == IPPORT_RESERVED) {
+ (void) close(s);
+ errno = EAGAIN; /* close */
+ return (-1);
+ }
+ }
+}
+
+
+
+#if defined(sun)
+/* The IMP and ultrix do not like multiple defined routines
+ and since it does not have users with NFS filesystems
+ mounted, the ruserok on it's OS will work just fine.
+ However that is not the case with SUNS who's ruserok which
+ is provided with the OS has problems with it's seteuid
+ ( which will eventually be traced no doubt to using
+ setreuid(-1,pgid)).
+ Therefore we provide a version of ruserok with fixes
+ the seteuid problem....Drawback - it can only be used
+ by a root process.*/
+
+#ifndef convex
+ruserok(rhost, superuser, ruser, luser)
+ char *rhost;
+ int superuser;
+ char *ruser, *luser;
+{
+ FILE *hostf;
+ char fhost[MAXHOSTNAMELEN];
+ int first = 1;
+ register char *sp, *p;
+ int baselen = -1;
+ int euid = -1;
+
+ sp = rhost;
+ p = fhost;
+ while (*sp) {
+ if (*sp == '.') {
+ if (baselen == -1)
+ baselen = sp - rhost;
+ *p++ = *sp++;
+ } else {
+ *p++ = islower(*sp) ? toupper(*sp++) : *sp++;
+ }
+ }
+ *p = '\0';
+ hostf = superuser ? (FILE *)0 : fopen("/etc/hosts.equiv", "r");
+ again:
+ if (hostf) {
+ if (!_validuser(hostf, fhost, luser, ruser, baselen)) {
+ (void) fclose(hostf);
+ if (euid != -1)
+ (void) setreuid ( 0,euid);
+ return(0);
+ }
+ (void) fclose(hostf);
+ }
+ if (first == 1) {
+ struct stat sbuf;
+ struct passwd *pwd;
+ char pbuf[MAXPATHLEN];
+
+ first = 0;
+ if ((pwd = getpwnam(luser)) == NULL)
+ return(-1);
+ /*
+ * Read .rhosts as the local user to avoid NFS mapping the
+ * root uid to something that can't read .rhosts.
+ */
+ euid = geteuid();
+ if (euid != -1)
+ (void) setreuid ( 0,pwd->pw_uid);
+ (void)strcpy(pbuf, pwd->pw_dir);
+ (void)strcat(pbuf, "/.rhosts");
+ if ((hostf = fopen(pbuf, "r")) == NULL){
+ if (euid != -1)
+ (void) setreuid ( 0,euid);
+ return(-1);
+ }
+ (void)fstat(fileno(hostf), &sbuf);
+ if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) {
+ fclose(hostf);
+ if (euid != -1)
+ (void) setreuid ( 0,euid);
+ return(-1);
+ }
+ goto again;
+ }
+ if (euid != -1)
+ (void) setreuid ( 0,euid);
+ return (-1);
+}
+
+
+
+_validuser(hostf, rhost, luser, ruser, baselen)
+ char *rhost, *luser, *ruser;
+ FILE *hostf;
+ int baselen;
+{
+ char *user;
+ char ahost[MAXHOSTNAMELEN];
+ register char *p;
+
+ while (fgets(ahost, sizeof (ahost), hostf)) {
+ p = ahost;
+ while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') {
+ *p = islower(*p) ? toupper(*p) : *p;
+ p++;
+ }
+ if (*p == ' ' || *p == '\t') {
+ *p++ = '\0';
+ while (*p == ' ' || *p == '\t')
+ p++;
+ user = p;
+ while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0')
+ p++;
+ } else
+ user = p;
+ *p = '\0';
+ if (_checkhost(rhost, ahost, baselen) &&
+ !strcmp(ruser, *user ? user : luser)) {
+ return (0);
+ }
+ }
+ return (-1);
+}
+#endif /* convex */
+
+
+
+_checkhost(rhost, lhost, len)
+ char *rhost, *lhost;
+ int len;
+{
+ static char ldomain[MAXHOSTNAMELEN + 1];
+ static char *domainp = NULL;
+ static int nodomain = 0;
+ register char *cp;
+
+ if (len == -1)
+ return(!strcmp(rhost, lhost));
+ if (strncmp(rhost, lhost, len))
+ return(0);
+ if (!strcmp(rhost, lhost))
+ return(1);
+ if (*(lhost + len) != '\0')
+ return(0);
+ if (nodomain)
+ return(0);
+ if (!domainp) {
+ if (gethostname(ldomain, sizeof(ldomain)) == -1) {
+ nodomain = 1;
+ return(0);
+ }
+ ldomain[MAXHOSTNAMELEN] = NULL;
+ if ((domainp = index(ldomain, '.')) == (char *)NULL) {
+ nodomain = 1;
+ return(0);
+ }
+ for (cp = ++domainp; *cp; ++cp)
+ if (islower(*cp))
+ *cp = toupper(*cp);
+ }
+ return(!strcmp(domainp, rhost + len +1));
+
+}
+#endif /* ! sysvimp */
+
+
+
+#if defined (hpux)
+int setreuid(real,eff)
+ int real,eff;
+{
+ int tmpint = -1;
+ return(setresuid(real,eff,tmpint));
+}
+#endif
+
+
+
+/* Strsave was a routine in the version 4 krb library: we put it here
+ for compatablilty with version 5 krb library, since kcmd.o is linked
+ into all programs. */
+
+char *
+ strsave(sp)
+char *sp;
+{
+ register char *ret;
+
+ if((ret = malloc((unsigned) strlen(sp)+1)) == NULL) {
+ fprintf(stderr, "no memory for saving args\n");
+ exit(1);
+ }
+ (void) strcpy(ret,sp);
+ return(ret);
+}
+
+
+
+#ifdef SYSV
+
+int killpg(pid,sig)
+ int pid,sig;
+{
+
+ if ( pid >= 0)
+ pid *= -1;
+ return(kill(pid,sig));
+}
+
+#endif
diff --git a/src/appl/bsd/krcp.c b/src/appl/bsd/krcp.c
new file mode 100644
index 0000000000..a6ecf15d5b
--- /dev/null
+++ b/src/appl/bsd/krcp.c
@@ -0,0 +1,1420 @@
+/*
+ * $Source$
+ * $Header$
+ */
+
+#ifndef lint
+static char *rcsid_rcp_c = "$Header$";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rcp.c 5.10 (Berkeley) 9/20/88";
+#endif /* not lint */
+
+#define KERBEROS
+
+ /*
+ * rcp
+ */
+#include <sys/param.h>
+#ifndef _TYPES_
+#include <sys/types.h>
+#define _TYPES_
+#endif
+#include <sys/file.h>
+#ifdef CRAY
+#include <sys/fcntl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <errno.h>
+
+#ifdef KERBEROS
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/crc-32.h>
+#include <krb5/mit-des.h>
+#include <krb5/los-proto.h>
+
+#include <com_err.h>
+
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+#define BUFSIZ 4096
+
+int sock;
+struct sockaddr_in foreign; /* set up by kcmd used by send_auth */
+char *krb_realm = (char *)0;
+char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */
+char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf,desoutbuf;
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+krb5_keyblock *session_key; /* static key for session */
+
+void try_normal();
+char **save_argv(), *strsave();
+int des_write(), des_read();
+void send_auth(), answer_auth();
+int encryptflag = 0;
+
+#define UCB_RCP "/bin/rcp"
+
+#ifdef CRAY
+#ifndef BITS64
+#define BITS64
+#endif
+#endif
+
+#else /* !KERBEROS */
+#define des_read read
+#define des_write write
+#endif /* KERBEROS */
+
+int rem;
+char *colon(), *index(), *rindex(), *strcpy();
+int errs;
+krb5_sigtype lostconn();
+int errno;
+extern char *sys_errlist[];
+int iamremote, targetshouldbedirectory;
+int iamrecursive;
+int pflag;
+struct passwd *pwd;
+#ifndef convex
+struct passwd *getpwuid();
+#endif
+int userid;
+int port;
+
+struct buffer {
+ int cnt;
+ char *buf;
+} *allocbuf();
+
+#define NULLBUF (struct buffer *) 0
+
+ /*VARARGS*/
+ int error();
+
+#define ga() (void) des_write(rem, "", 1)
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *targ, *host, *src;
+ char *suser, *tuser, *thost;
+ int i;
+ char buf[BUFSIZ], cmd[16];
+ struct servent *sp;
+ static char curhost[256];
+#ifdef KERBEROS
+ krb5_flags authopts;
+ krb5_error_code status;
+ char **orig_argv = save_argv(argc, argv);
+
+ sp = getservbyname("kshell", "tcp");
+ krb5_init_ets();
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf; /* Set up des buffers */
+
+#else
+ sp = getservbyname("shell", "tcp");
+#endif /* KERBEROS */
+ if (sp == NULL) {
+#ifdef KERBEROS
+ fprintf(stderr, "rcp: kshell/tcp: unknown service\n");
+ try_normal(orig_argv);
+#else
+ fprintf(stderr, "rcp: shell/tcp: unknown service\n");
+ exit(1);
+#endif /* KERBEROS */
+ }
+ port = sp->s_port;
+ pwd = getpwuid(userid = getuid());
+ if (pwd == 0) {
+ fprintf(stderr, "who are you?\n");
+ exit(1);
+ }
+
+ for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) {
+ (*argv)++;
+ while (**argv) switch (*(*argv)++) {
+
+ case 'r':
+ iamrecursive++;
+ break;
+
+ case 'p': /* preserve mtimes and atimes */
+ pflag++;
+ break;
+
+#ifdef KERBEROS
+ case 'x':
+ encryptflag++;
+ break;
+ case 'k': /* Change kerberos realm */
+ argc--, argv++;
+ if (argc == 0)
+ usage();
+ if(!(krb_realm = (char *)malloc(strlen(*argv) + 1))){
+ fprintf(stderr, "rcp: Cannot malloc.\n");
+ exit(1);
+ }
+ strcpy(krb_realm, *argv);
+ goto next_arg;
+#endif /* KERBEROS */
+ /* The rest of these are not for users. */
+ case 'd':
+ targetshouldbedirectory = 1;
+ break;
+
+ case 'f': /* "from" */
+ iamremote = 1;
+#if defined(KERBEROS)
+ if (encryptflag)
+ answer_auth();
+#endif /* KERBEROS */
+ (void) response();
+ source(--argc, ++argv);
+ exit(errs);
+
+ case 't': /* "to" */
+ iamremote = 1;
+#if defined(KERBEROS)
+ if (encryptflag)
+ answer_auth();
+#endif /* KERBEROS */
+ sink(--argc, ++argv);
+ exit(errs);
+
+ default:
+ usage();
+ }
+#ifdef KERBEROS
+ next_arg: ;
+#endif /* KERBEROS */
+ }
+
+ if (argc < 2)
+ usage();
+ if (argc > 2)
+ targetshouldbedirectory = 1;
+ rem = -1;
+#ifdef KERBEROS
+ (void) sprintf(cmd, "rcp%s%s%s%s",
+ iamrecursive ? " -r" : "", pflag ? " -p" : "",
+ encryptflag ? " -x" : "",
+ targetshouldbedirectory ? " -d" : "");
+#else /* !KERBEROS */
+
+ (void) sprintf(cmd, "rcp%s%s%s",
+ iamrecursive ? " -r" : "", pflag ? " -p" : "",
+ targetshouldbedirectory ? " -d" : "");
+#endif /* KERBEROS */
+
+ (void) signal(SIGPIPE, lostconn);
+ targ = colon(argv[argc - 1]);
+
+ /* Check if target machine is the current machine. */
+
+ gethostname(curhost, sizeof(curhost));
+ if (targ) { /* ... to remote */
+ *targ++ = 0;
+ if (hosteq(argv[argc - 1], curhost)) {
+
+ /* If so, pretend there wasn't even one given
+ * check for an argument of just "host:", it
+ * should become "."
+ */
+
+ if (*targ == 0) {
+ targ = ".";
+ argv[argc - 1] = targ;
+ }
+ else
+ argv[argc - 1] = targ;
+ targ = 0;
+ }
+ }
+ if (targ) {
+ /* Target machine is some remote machine */
+ if (*targ == 0)
+ targ = ".";
+ thost = index(argv[argc - 1], '@');
+ if (thost) {
+ *thost++ = 0;
+ tuser = argv[argc - 1];
+ if (*tuser == '\0')
+ tuser = NULL;
+ else if (!okname(tuser))
+ exit(1);
+ } else {
+ thost = argv[argc - 1];
+ tuser = NULL;
+ }
+ for (i = 0; i < argc - 1; i++) {
+ src = colon(argv[i]);
+ if (src) { /* remote to remote */
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ host = index(argv[i], '@');
+ if (host) {
+ *host++ = 0;
+ suser = argv[i];
+ if (*suser == '\0')
+ suser = pwd->pw_name;
+ else if (!okname(suser))
+ continue;
+#ifdef hpux
+ (void) sprintf(buf, "remsh %s -l %s -n %s %s '%s%s%s:%s'",
+#else
+ (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s%s%s:%s'",
+#endif
+ host, suser, cmd, src,
+ tuser ? tuser : "",
+ tuser ? "@" : "",
+ thost, targ);
+ } else
+#ifdef hpux
+ (void) sprintf(buf, "remsh %s -n %s %s '%s%s%s:%s'",
+#else
+ (void) sprintf(buf, "rsh %s -n %s %s '%s%s%s:%s'",
+#endif
+ argv[i], cmd, src,
+ tuser ? tuser : "",
+ tuser ? "@" : "",
+ thost, targ);
+ (void) susystem(buf);
+ } else { /* local to remote */
+ if (rem == -1) {
+ (void) sprintf(buf, "%s -t %s",
+ cmd, targ);
+ host = thost;
+#ifdef KERBEROS
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&sock, &host,
+ port,
+ pwd->pw_name,
+ tuser ? tuser :
+ pwd->pw_name,
+ buf,
+ 0,
+ "host",
+ krb_realm,
+ 0, /* No return cred */
+ 0, /* No seq # */
+ 0, /* No server seq # */
+ (struct sockaddr_in *) 0,
+ &foreign,
+ authopts);
+ if (status) {
+ fprintf(stderr,
+ "%s: kcmd to host %s failed - %s\n",
+ orig_argv[0], host,
+ error_message(status));
+ try_normal(orig_argv);
+ }
+ else {
+ rem = sock;
+ if (encryptflag)
+ send_auth();
+ }
+#else
+ rem = rcmd(&host, port, pwd->pw_name,
+ tuser ? tuser : pwd->pw_name,
+ buf, 0);
+ if (rem < 0)
+ exit(1);
+#endif /* KERBEROS */
+ if (response() < 0)
+ exit(1);
+ }
+ source(1, argv+i);
+ }
+ }
+ } else { /* ... to local */
+ if (targetshouldbedirectory)
+ verifydir(argv[argc - 1]);
+ for (i = 0; i < argc - 1; i++) {
+ src = colon(argv[i]);
+ /* Check if source machine is current machine */
+ if (src) {
+ *src++ = 0;
+ if (hosteq(argv[i], curhost)) {
+
+ /* If so, pretend src machine never given */
+
+ if (*src == 0) {
+ error("rcp: no path given in arg: %s:\n",
+ argv[i]);
+ errs++;
+ continue;
+ }
+ argv[i] = src;
+ src = 0;
+ } else {
+ /* not equiv, return colon */
+ *(--src) = ':';
+ }
+ }
+ if (src == 0) { /* local to local */
+ (void) sprintf(buf, "/bin/cp%s%s %s %s",
+ iamrecursive ? " -r" : "",
+ pflag ? " -p" : "",
+ argv[i], argv[argc - 1]);
+ (void) susystem(buf);
+ } else { /* remote to local */
+ *src++ = 0;
+ if (*src == 0)
+ src = ".";
+ host = index(argv[i], '@');
+ if (host) {
+ *host++ = 0;
+ suser = argv[i];
+ if (*suser == '\0')
+ suser = pwd->pw_name;
+ else if (!okname(suser))
+ continue;
+ } else {
+ host = argv[i];
+ suser = pwd->pw_name;
+ }
+ (void) sprintf(buf, "%s -f %s", cmd, src);
+#ifdef KERBEROS
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&sock, &host,
+ port,
+ pwd->pw_name, suser,
+ buf,
+ 0,
+ "host",
+ krb_realm,
+ 0, /* No return cred */
+ 0, /* No seq # */
+ 0, /* No server seq # */
+ (struct sockaddr_in *) 0,
+ &foreign,
+ authopts);
+ if (status) {
+ fprintf(stderr,
+ "%s: kcmd to host %s failed - %s\n",
+ orig_argv[0], host,
+ error_message(status));
+ try_normal(orig_argv);
+
+ } else {
+ rem = sock;
+ if (encryptflag)
+ send_auth();
+ }
+ sink(1, argv+argc-1);
+#else
+ rem = rcmd(&host, port, pwd->pw_name, suser,
+ buf, 0);
+ if (rem < 0)
+ continue;
+ (void) setreuid(0, userid);
+ sink(1, argv+argc-1);
+ (void) setreuid(userid, 0);
+#endif /* KERBEROS */
+ (void) close(rem);
+ rem = -1;
+ }
+ }
+ }
+ exit(errs);
+}
+
+
+
+verifydir(cp)
+ char *cp;
+{
+ struct stat stb;
+
+ if (stat(cp, &stb) >= 0) {
+ if ((stb.st_mode & S_IFMT) == S_IFDIR)
+ return;
+ errno = ENOTDIR;
+ }
+ error("rcp: %s: %s.\n", cp, sys_errlist[errno]);
+ exit(1);
+}
+
+
+
+char *colon(cp)
+ char *cp;
+{
+
+ while (*cp) {
+ if (*cp == ':')
+ return (cp);
+ if (*cp == '/')
+ return (0);
+ cp++;
+ }
+ return (0);
+}
+
+
+
+okname(cp0)
+ char *cp0;
+{
+ register char *cp = cp0;
+ register int c;
+
+ do {
+ c = *cp;
+ if (c & 0200)
+ goto bad;
+ if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-')
+ goto bad;
+ cp++;
+ } while (*cp);
+ return (1);
+ bad:
+ fprintf(stderr, "rcp: invalid user name %s\n", cp0);
+ return (0);
+}
+
+
+
+susystem(s)
+ char *s;
+{
+ int status, pid, w;
+ register krb5_sigtype (*istat)(), (*qstat)();
+
+ if ((pid = vfork()) == 0) {
+ execl("/bin/sh", "sh", "-c", s, (char *)0);
+ _exit(127);
+ }
+ istat = signal(SIGINT, SIG_IGN);
+ qstat = signal(SIGQUIT, SIG_IGN);
+ while ((w = wait(&status)) != pid && w != -1)
+ ;
+ if (w == -1)
+ status = -1;
+ (void) signal(SIGINT, istat);
+ (void) signal(SIGQUIT, qstat);
+ return (status);
+}
+
+
+
+source(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *last, *name;
+ struct stat stb;
+ static struct buffer buffer;
+ struct buffer *bp;
+ int x, readerr, f, amt;
+ off_t i;
+ char buf[BUFSIZ];
+
+ for (x = 0; x < argc; x++) {
+ name = argv[x];
+ if ((f = open(name, 0)) < 0) {
+ error("rcp: %s: %s\n", name, sys_errlist[errno]);
+ continue;
+ }
+ if (fstat(f, &stb) < 0)
+ goto notreg;
+ switch (stb.st_mode&S_IFMT) {
+
+ case S_IFREG:
+ break;
+
+ case S_IFDIR:
+ if (iamrecursive) {
+ (void) close(f);
+ rsource(name, &stb);
+ continue;
+ }
+ /* fall into ... */
+ default:
+ notreg:
+ (void) close(f);
+ error("rcp: %s: not a plain file\n", name);
+ continue;
+ }
+ last = rindex(name, '/');
+ if (last == 0)
+ last = name;
+ else
+ last++;
+ if (pflag) {
+ /*
+ * Make it compatible with possible future
+ * versions expecting microseconds.
+ */
+ (void) sprintf(buf, "T%ld 0 %ld 0\n",
+ stb.st_mtime, stb.st_atime);
+ (void) des_write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ continue;
+ }
+ }
+ (void) sprintf(buf, "C%04o %ld %s\n",
+ stb.st_mode&07777, stb.st_size, last);
+ (void) des_write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ (void) close(f);
+ continue;
+ }
+ if ((bp = allocbuf(&buffer, f, BUFSIZ)) == NULLBUF) {
+ (void) close(f);
+ continue;
+ }
+ readerr = 0;
+ for (i = 0; i < stb.st_size; i += bp->cnt) {
+ amt = bp->cnt;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (readerr == 0 && read(f, bp->buf, amt) != amt)
+ readerr = errno;
+ (void) des_write(rem, bp->buf, amt);
+ }
+ (void) close(f);
+ if (readerr == 0)
+ ga();
+ else
+ error("rcp: %s: %s\n", name, sys_errlist[readerr]);
+ (void) response();
+ }
+}
+
+
+
+#if defined(SYSV) && !defined(sysvimp)
+#include <dirent.h>
+#else
+#ifdef sysvimp
+#include <ufs/fsdir.h>
+#else
+#include <sys/dir.h>
+#endif
+#endif
+
+rsource(name, statp)
+ char *name;
+ struct stat *statp;
+{
+ DIR *d = opendir(name);
+ char *last;
+#if defined(SYSV) && !defined(sysvimp)
+ struct dirent *dp;
+#else
+ struct direct *dp;
+#endif
+ char buf[BUFSIZ];
+ char *bufv[1];
+
+ if (d == 0) {
+ error("rcp: %s: %s\n", name, sys_errlist[errno]);
+ return;
+ }
+ last = rindex(name, '/');
+ if (last == 0)
+ last = name;
+ else
+ last++;
+ if (pflag) {
+ (void) sprintf(buf, "T%ld 0 %ld 0\n",
+ statp->st_mtime, statp->st_atime);
+ (void) des_write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ closedir(d);
+ return;
+ }
+ }
+ (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last);
+ (void) des_write(rem, buf, strlen(buf));
+ if (response() < 0) {
+ closedir(d);
+ return;
+ }
+ while (dp = readdir(d)) {
+ if (dp->d_ino == 0)
+ continue;
+ if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+ continue;
+ if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) {
+ error("%s/%s: Name too long.\n", name, dp->d_name);
+ continue;
+ }
+ (void) sprintf(buf, "%s/%s", name, dp->d_name);
+ bufv[0] = buf;
+ source(1, bufv);
+ }
+ closedir(d);
+ (void) des_write(rem, "E\n", 2);
+ (void) response();
+}
+
+
+
+response()
+{
+ char resp, c, rbuf[BUFSIZ], *cp = rbuf;
+ if (des_read(rem, &resp, 1) != 1)
+ lostconn();
+ switch (resp) {
+
+ case 0: /* ok */
+ return (0);
+
+ default:
+ *cp++ = resp;
+ /* fall into... */
+ case 1: /* error, followed by err msg */
+ case 2: /* fatal error, "" */
+ do {
+ if (des_read(rem, &c, 1) != 1)
+ lostconn();
+ *cp++ = c;
+ } while (cp < &rbuf[BUFSIZ] && c != '\n');
+ if (iamremote == 0)
+ (void) write(2, rbuf, cp - rbuf);
+ errs++;
+ if (resp == 1)
+ return (-1);
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+
+
+krb5_sigtype
+ lostconn()
+{
+ if (iamremote == 0)
+ fprintf(stderr, "rcp: lost connection\n");
+ exit(1);
+}
+
+
+
+sink(argc, argv)
+ int argc;
+ char **argv;
+{
+ off_t i, j;
+ char *targ, *whopp, *cp;
+ int of, mode, wrerr, exists, first, count, amt, size;
+ struct buffer *bp;
+ static struct buffer buffer;
+ struct stat stb;
+ int targisdir = 0;
+ int mask = umask(0);
+ char *myargv[1];
+ char cmdbuf[BUFSIZ], nambuf[BUFSIZ];
+ int setimes = 0;
+ struct timeval tv[2];
+#define atime tv[0]
+#define mtime tv[1]
+#define SCREWUP(str) { whopp = str; goto screwup; }
+
+ if (!pflag)
+ (void) umask(mask);
+ if (argc != 1) {
+ error("rcp: ambiguous target\n");
+ exit(1);
+ }
+ targ = *argv;
+ if (targetshouldbedirectory)
+ verifydir(targ);
+ ga();
+ if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR)
+ targisdir = 1;
+ for (first = 1; ; first = 0) {
+ cp = cmdbuf;
+ if (des_read(rem, cp, 1) <= 0)
+ return;
+ if (*cp++ == '\n')
+ SCREWUP("unexpected '\\n'");
+ do {
+ if (des_read(rem, cp, 1) != 1)
+ SCREWUP("lost connection");
+ } while (*cp++ != '\n');
+ *cp = 0;
+ if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') {
+ if (iamremote == 0)
+ (void) write(2, cmdbuf+1, strlen(cmdbuf+1));
+ if (cmdbuf[0] == '\02')
+ exit(1);
+ errs++;
+ continue;
+ }
+ *--cp = 0;
+ cp = cmdbuf;
+ if (*cp == 'E') {
+ ga();
+ return;
+ }
+
+#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0');
+ if (*cp == 'T') {
+ setimes++;
+ cp++;
+ getnum(mtime.tv_sec);
+ if (*cp++ != ' ')
+ SCREWUP("mtime.sec not delimited");
+ getnum(mtime.tv_usec);
+ if (*cp++ != ' ')
+ SCREWUP("mtime.usec not delimited");
+ getnum(atime.tv_sec);
+ if (*cp++ != ' ')
+ SCREWUP("atime.sec not delimited");
+ getnum(atime.tv_usec);
+ if (*cp++ != '\0')
+ SCREWUP("atime.usec not delimited");
+ ga();
+ continue;
+ }
+ if (*cp != 'C' && *cp != 'D') {
+ /*
+ * Check for the case "rcp remote:foo\* local:bar".
+ * In this case, the line "No match." can be returned
+ * by the shell before the rcp command on the remote is
+ * executed so the ^Aerror_message convention isn't
+ * followed.
+ */
+ if (first) {
+ error("%s\n", cp);
+ exit(1);
+ }
+ SCREWUP("expected control record");
+ }
+ cp++;
+ mode = 0;
+ for (; cp < cmdbuf+5; cp++) {
+ if (*cp < '0' || *cp > '7')
+ SCREWUP("bad mode");
+ mode = (mode << 3) | (*cp - '0');
+ }
+ if (*cp++ != ' ')
+ SCREWUP("mode not delimited");
+ size = 0;
+ while (isdigit(*cp))
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ SCREWUP("size not delimited");
+ if (targisdir)
+ (void) sprintf(nambuf, "%s%s%s", targ,
+ *targ ? "/" : "", cp);
+ else
+ (void) strcpy(nambuf, targ);
+ exists = stat(nambuf, &stb) == 0;
+ if (cmdbuf[0] == 'D') {
+ if (exists) {
+ if ((stb.st_mode&S_IFMT) != S_IFDIR) {
+ errno = ENOTDIR;
+ goto bad;
+ }
+ if (pflag)
+ (void) chmod(nambuf, mode);
+ } else if (mkdir(nambuf, mode) < 0)
+ goto bad;
+ myargv[0] = nambuf;
+ sink(1, myargv);
+ if (setimes) {
+ setimes = 0;
+ if (utimes(nambuf, tv) < 0)
+ error("rcp: can't set times on %s: %s\n",
+ nambuf, sys_errlist[errno]);
+ }
+ continue;
+ }
+ if ((of = open(nambuf, O_WRONLY|O_CREAT, mode)) < 0) {
+ bad:
+ error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
+ continue;
+ }
+ if (exists && pflag)
+#ifdef NOFCHMOD
+ (void) chmod(nambuf, mode);
+#else
+ (void) fchmod(of, mode);
+#endif
+ ga();
+ if ((bp = allocbuf(&buffer, of, BUFSIZ)) == NULLBUF) {
+ (void) close(of);
+ continue;
+ }
+ cp = bp->buf;
+ count = 0;
+ wrerr = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ count += amt;
+ do {
+ j = des_read(rem, cp, amt);
+ if (j <= 0) {
+ if (j == 0)
+ error("rcp: dropped connection");
+ else
+ error("rcp: %s\n",
+ sys_errlist[errno]);
+ exit(1);
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ if (count == bp->cnt) {
+ if (wrerr == 0 &&
+ write(of, bp->buf, count) != count)
+ wrerr++;
+ count = 0;
+ cp = bp->buf;
+ }
+ }
+ if (count != 0 && wrerr == 0 &&
+ write(of, bp->buf, count) != count)
+ wrerr++;
+ if (ftruncate(of, size))
+ error("rcp: can't truncate %s: %s\n",
+ nambuf, sys_errlist[errno]);
+ (void) close(of);
+ (void) response();
+ if (setimes) {
+ setimes = 0;
+ if (utimes(nambuf, tv) < 0)
+ error("rcp: can't set times on %s: %s\n",
+ nambuf, sys_errlist[errno]);
+ }
+ if (wrerr)
+ error("rcp: %s: %s\n", nambuf, sys_errlist[errno]);
+ else
+ ga();
+ }
+ screwup:
+ error("rcp: protocol screwup: %s\n", whopp);
+ exit(1);
+}
+
+
+
+struct buffer *allocbuf(bp, fd, blksize)
+ struct buffer *bp;
+ int fd, blksize;
+{
+ struct stat stb;
+ int size;
+
+ if (fstat(fd, &stb) < 0) {
+ error("rcp: fstat: %s\n", sys_errlist[errno]);
+ return (NULLBUF);
+ }
+#ifdef NOROUNDUP
+ size = 0;
+#else
+ size = roundup(stb.st_blksize, blksize);
+#endif
+ if (size == 0)
+ size = blksize;
+ if (bp->cnt < size) {
+ if (bp->buf != 0)
+ free(bp->buf);
+ bp->buf = (char *)malloc((unsigned) size);
+ if (bp->buf == 0) {
+ error("rcp: malloc: out of memory\n");
+ return (NULLBUF);
+ }
+ }
+ bp->cnt = size;
+ return (bp);
+}
+
+
+
+/*VARARGS1*/
+error(fmt, a1, a2, a3, a4, a5)
+ char *fmt;
+ int a1, a2, a3, a4, a5;
+{
+ char buf[BUFSIZ], *cp = buf;
+
+ errs++;
+ *cp++ = 1;
+ (void) sprintf(cp, fmt, a1, a2, a3, a4, a5);
+ (void) des_write(rem, buf, strlen(buf));
+ if (iamremote == 0)
+ (void) write(2, buf+1, strlen(buf+1));
+}
+
+
+
+usage()
+{
+#ifdef KERBEROS
+ fprintf(stderr,
+ "Usage: \trcp [-p] [-x] [-k realm] f1 f2; or:\n\trcp [-r] [-p] [-x] [-k realm] f1 ... fn d2\n");
+#else /* !KERBEROS */
+ fputs("usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n", stderr);
+#endif
+ exit(1);
+}
+
+
+
+hosteq(h1, h2)
+ char *h1, *h2;
+{
+ struct hostent *h_ptr;
+ char hname1[256];
+
+ /* get the official names for the two hosts */
+
+ if ((h_ptr = gethostbyname(h1)) == NULL)
+ return(0);
+ strcpy(hname1, h_ptr->h_name);
+ if ((h_ptr = gethostbyname(h2)) == NULL)
+ return(0);
+
+ /*return if they are equal (strcmp returns 0 for equal - I return 1) */
+
+ return(!strcmp(hname1, h_ptr->h_name));
+}
+
+
+
+#ifdef KERBEROS
+void try_normal(argv)
+ char **argv;
+{
+ register int i;
+
+ if (!encryptflag) {
+ fprintf(stderr,"trying normal rcp (%s)\n", UCB_RCP);
+ fflush(stderr);
+ /* close all but stdin, stdout, stderr */
+ for (i = getdtablesize(); i > 2; i--)
+ (void) close(i);
+ execv(UCB_RCP, argv);
+ perror("exec");
+ }
+ exit(1);
+}
+
+
+
+char **save_argv(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int i;
+
+ char **local_argv = (char **)calloc((unsigned) argc+1,
+ (unsigned) sizeof(char *));
+ /* allocate an extra pointer, so that it is initialized to NULL
+ and execv() will work */
+ for (i = 0; i < argc; i++)
+ local_argv[i] = strsave(argv[i]);
+ return(local_argv);
+}
+
+
+
+#ifdef unicos61
+#define SIZEOF_INADDR SIZEOF_in_addr
+#else
+#define SIZEOF_INADDR sizeof(struct in_addr)
+#endif
+
+krb5_error_code tgt_keyproc(DECLARG(krb5_pointer, keyprocarg),
+ DECLARG(krb5_principal, principal),
+ DECLARG(krb5_kvno, vno),
+ DECLARG(krb5_keyblock **, key))
+ OLDDECLARG(krb5_pointer, keyprocarg)
+ OLDDECLARG(krb5_principal, principal)
+ OLDDECLARG(krb5_kvno, vno)
+ OLDDECLARG(krb5_keyblock **, key)
+{
+ krb5_creds *creds = (krb5_creds *)keyprocarg;
+
+ return krb5_copy_keyblock(&creds->keyblock, key);
+}
+
+
+
+void send_auth()
+{
+ int sin_len;
+ char *princ; /* principal in credentials cache */
+ krb5_ccache cc;
+ krb5_creds creds;
+ krb5_principal sprinc; /* principal of server */
+ krb5_data reply, msg, princ_data;
+ krb5_tkt_authent *authdat;
+ krb5_error_code status;
+ krb5_address faddr;
+
+
+
+ if (status = krb5_cc_default(&cc)){
+ fprintf(stderr,"rcp: send_auth failed krb5_cc_default : %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+ memset ((char*)&creds, 0, sizeof(creds));
+
+ if (status = krb5_cc_get_principal(cc, &creds.client)){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_cc_get_principal : %s\n",
+ error_message(status));
+ krb5_cc_close(cc);
+ exit(1);
+ }
+
+ if (status = krb5_unparse_name(creds.client, &princ)){
+ fprintf(stderr,"rcp: send_auth failed krb5_parse_name : %s\n",
+ error_message(status));
+ krb5_cc_close(cc);
+ exit(1);
+ }
+ if (status = krb5_build_principal_ext(&sprinc,
+ krb5_princ_realm(creds.client)->length,
+ krb5_princ_realm(creds.client)->data,
+ 6, "krbtgt",
+ krb5_princ_realm(creds.client)->length,
+ krb5_princ_realm(creds.client)->data,
+ 0)){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_build_principal_ext : %s\n",
+ error_message(status));
+ krb5_cc_close(cc);
+ exit(1);
+ }
+
+ creds.server = sprinc;
+
+ /* Get TGT from credentials cache */
+ if (status = krb5_get_credentials(KRB5_GC_CACHED, cc, &creds)){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_get_credentials: %s\n",
+ error_message(status));
+ krb5_cc_close(cc);
+ exit(1);
+ }
+ krb5_cc_close(cc);
+
+ krb5_free_principal(sprinc); /* creds.server is replaced
+ upon retrieval */
+
+
+ princ_data.data = princ;
+ princ_data.length = strlen(princ_data.data) + 1; /* include null
+ terminator for
+ server's convenience */
+ status = krb5_write_message((krb5_pointer) &rem, &princ_data);
+ if (status){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_write_message: %s\n",
+ error_message(status));
+ exit(1);
+ }
+ xfree(princ);
+ status = krb5_write_message((krb5_pointer) &rem, &creds.ticket);
+ if (status){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_write_message: %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+ status = krb5_read_message((krb5_pointer) &rem, &reply);
+ if (status){
+ fprintf(stderr,
+ "rcp: send_auth failed krb5_read_message: %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+ sin_len = SIZEOF_INADDR;
+ faddr.addrtype = foreign.sin_family;
+ faddr.length = SIZEOF_INADDR;
+ faddr.contents = (krb5_octet *) &foreign.sin_addr;
+
+ /* read the ap_req to get the session key */
+ status = krb5_rd_req(&reply,
+ 0, /* don't know server's name... */
+ &faddr,
+ 0, /* no fetchfrom */
+ tgt_keyproc,
+ (krb5_pointer)&creds, /* credentials as arg to
+ keyproc */
+ 0, /* no rcache for the moment XXX */
+ &authdat);
+ xfree(reply.data);
+ if (status) {
+ fprintf(stderr, "rcp: send_auth failed krb5_rd_req: %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+ krb5_copy_keyblock(authdat->ticket->enc_part2->session,&session_key);
+ krb5_free_tkt_authent(authdat);
+ krb5_free_cred_contents(&creds);
+
+ krb5_use_keytype(&eblock, session_key->keytype);
+ if ( status = krb5_process_key(&eblock,
+ session_key)){
+ fprintf(stderr, "rcp: send_auth failed krb5_process_key: %s\n",
+ error_message(status));
+ exit(1);
+ }
+
+}
+
+
+
+void
+ answer_auth()
+{
+ krb5_data pname_data, msg;
+ krb5_creds creds;
+ krb5_ccache cc;
+ krb5_error_code status;
+ extern krb5_flags krb5_kdc_default_options;
+
+
+ memset ((char*)&creds, 0, sizeof(creds));
+
+ if (status = krb5_read_message((krb5_pointer) &rem, &pname_data)) {
+ exit(1);
+ }
+
+ if (status = krb5_read_message((krb5_pointer) &rem,
+ &creds.second_ticket)) {
+ exit(1);
+ }
+
+ if (status = krb5_cc_default(&cc)){
+ exit(1);
+ }
+
+ if (status = krb5_cc_get_principal(cc, &creds.client)){
+ krb5_cc_destroy(cc);
+ krb5_cc_close(cc);
+ exit(1);
+ }
+
+ if (status = krb5_parse_name(pname_data.data, &creds.server)){
+ krb5_cc_destroy(cc);
+ krb5_cc_close(cc);
+ exit(1);
+ }
+ xfree(pname_data.data);
+
+ if (status = krb5_get_credentials(KRB5_GC_USER_USER, cc, &creds)){
+ krb5_cc_destroy(cc);
+ krb5_cc_close(cc);
+ exit(1);
+ }
+
+ if (status = krb5_mk_req_extended(AP_OPTS_USE_SESSION_KEY,
+ 0, /* no application checksum here */
+ krb5_kdc_default_options,
+ 0,
+ 0, /* no need for subkey */
+ cc,
+ &creds,
+ 0, /* don't need authenticator copy */
+ &msg)) {
+ krb5_cc_destroy(cc);
+ krb5_cc_close(cc);
+ exit(1);
+ }
+ krb5_cc_destroy(cc);
+ krb5_cc_close(cc);
+ status = krb5_write_message((krb5_pointer) &rem, &msg);
+ xfree(msg.data);
+ if (status){
+ exit(1);
+ }
+
+ /* setup eblock for des_read and write */
+ krb5_copy_keyblock(&creds.keyblock,&session_key);
+
+ /* cleanup */
+ krb5_free_cred_contents(&creds);
+
+ /* OK process key */
+ krb5_use_keytype(&eblock, session_key->keytype);
+ if ( status = krb5_process_key(&eblock,session_key)) {
+ exit(1);
+ }
+
+ return;
+}
+
+
+
+char storage[2*BUFSIZ]; /* storage for the decryption */
+int nstored = 0;
+char *store_ptr = storage;
+
+int des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len,rd_len;
+ int cc;
+ krb5_error_code status;
+
+ if (!encryptflag)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+#ifdef BITS64
+ /*
+ * XXX Ick; this assumes a big-endian word order....
+ */
+ rd_len = 0;
+ if ((cc = krb5_net_read(fd, (char *)&rd_len + 4, 4)) != 4) {
+#else
+ if ((cc = krb5_net_read(fd, (char *)&rd_len, sizeof(rd_len))) !=
+ sizeof(rd_len)) {
+#endif
+ /* XXX can't read enough, pipe
+ must have closed */
+ return(0);
+ }
+ rd_len = ntohl(rd_len);
+ net_len = krb5_encrypt_size(rd_len,eblock.crypto_entry);
+ if (net_len <= 0 || net_len > sizeof(des_inbuf)) {
+ /* preposterous length; assume out-of-sync; only
+ recourse is to close connection, so return 0 */
+ error( "rcp: Des_read size problem net_len %d rd_len %d %d.\n",
+ net_len,rd_len, len);
+ errno = E2BIG;
+ return(-1);
+ }
+ if ((cc = krb5_net_read(fd, desinbuf.data, net_len)) != net_len) {
+ /* pipe must have closed, return 0 */
+ error( "rcp: Des_read error: length received %d != expected %d.\n",
+ cc,net_len);
+ return(0);
+ }
+ /* decrypt info */
+ if ((status = krb5_decrypt(desinbuf.data,
+ (krb5_pointer) storage,
+ net_len,
+ &eblock, 0))) {
+ error("rcp: Des_read cannot decrypt data from network %s.\n",
+ error_message(status));
+ return(0);
+ }
+ store_ptr = storage;
+ nstored = rd_len;
+ if (nstored > len) {
+ memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+
+ return(nreturned);
+}
+
+
+
+int des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ long net_len;
+
+ if (!encryptflag)
+ return(write(fd, buf, len));
+
+ desoutbuf.length = krb5_encrypt_size(len,eblock.crypto_entry);
+ if (desoutbuf.length > sizeof(des_outbuf)){
+ return(-1);
+ }
+ if (( krb5_encrypt((krb5_pointer)buf,
+ desoutbuf.data,
+ len,
+ &eblock,
+ 0))){
+ return(-1);
+ }
+
+ net_len = htonl(len);
+#ifdef BITS64
+ (void) write(fd,(char *)&net_len + 4, 4);
+#else
+ (void) write(fd, &net_len, sizeof(net_len));
+#endif
+ if (write(fd, desoutbuf.data,desoutbuf.length) != desoutbuf.length){
+ return(-1);
+ }
+ else return(len);
+}
+
+#endif /* KERBEROS */
diff --git a/src/appl/bsd/krlogin.c b/src/appl/bsd/krlogin.c
new file mode 100644
index 0000000000..8191f5798e
--- /dev/null
+++ b/src/appl/bsd/krlogin.c
@@ -0,0 +1,1468 @@
+/*
+ * $Source$! * $Author$
+ * $Header$
+ */
+#ifndef lint
+static char rcsid_rlogin_c[] = "$Header$";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rlogin.c 5.12 (Berkeley) 9/19/88";
+#endif /* not lint */
+
+#define KERBEROS
+
+ /*
+ * rlogin - remote login
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#ifndef _TYPES
+#include <sys/types.h>
+#define _TYPES_
+#endif
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#ifdef SYSV
+#ifndef USE_TERMIO
+#define USE_TERMIO
+#endif
+#endif
+
+#ifdef USE_TERMIO
+#ifdef CRAY
+#include <sys/ttold.h>
+#endif
+#include <sys/termio.h>
+#define sg_flags c_lflag
+#define sg_ospeed c_cflag&CBAUD
+
+#ifndef TIOCGETP
+#define TIOCGETP TCGETA
+#endif
+#ifndef TIOCSETP
+#define TIOCSETP TCSETA
+#endif
+#ifndef TIOCSETN
+#define TIOCSETN TCSETAW
+#endif
+#else /* !USE_TERMIO */
+#include <sgtty.h>
+#endif /* USE_TERMIO */
+
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <netdb.h>
+
+#ifdef KERBEROS
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/crc-32.h>
+#include <krb5/mit-des.h>
+#include <krb5/los-proto.h>
+
+#include <com_err.h>
+
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+#define BUFSIZ 4096
+
+char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */
+char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf,desoutbuf;
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+
+void try_normal();
+char *krb_realm = (char *)0;
+int encrypt = 0;
+krb5_creds *cred;
+struct sockaddr_in local, foreign;
+
+#define UCB_RLOGIN "/usr/ucb/rlogin"
+
+#ifdef CRAY
+#ifndef BITS64
+#define BITS64
+#endif
+#endif
+
+#else /* !KERBEROS */
+#define des_read read
+#define des_write write
+#endif /* KERBEROS */
+
+
+# ifndef TIOCPKT_WINDOW
+# define TIOCPKT_WINDOW 0x80
+# endif /* TIOCPKT_WINDOW */
+
+/* concession to sun */
+# ifndef SIGUSR1
+# define SIGUSR1 30
+# endif /* SIGUSR1 */
+
+char *index(), *rindex(), *getenv(), *strcat(), *strcpy();
+#ifndef convex
+struct passwd *getpwuid();
+#endif
+char *name;
+int rem = -1; /* Remote socket fd */
+char cmdchar = '~';
+int eight = 1; /* Default to 8 bit transmission */
+int no_local_escape = 0;
+int null_local_username = 0;
+int flow = 1; /* Default is to allow flow
+ control at the local terminal */
+int flowcontrol; /* Since emacs can alter the
+ flow control characteristics
+ of a session we need a
+ variable to keep track of
+ the original characteristics */
+int confirm = 0; /* ask if ~. is given before dying. */
+int litout;
+#ifdef hpux
+char *speeds[] =
+{ "0", "50", "75", "110", "134", "150", "200", "300", "600",
+ "900", "1200", "1800", "2400", "3600", "4800", "7200", "9600",
+ "19200", "38400", "EXTA", "EXTB" };
+#else
+char *speeds[] =
+{ "0", "50", "75", "110", "134", "150", "200", "300",
+ "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
+#endif
+char term[256] = "network";
+extern int errno;
+krb5_sigtype lostpeer();
+int dosigwinch = 0;
+#ifndef sigmask
+#define sigmask(m) (1 << ((m)-1))
+#endif
+#ifdef NO_WINSIZE
+struct winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+#endif /* NO_WINSIZE */
+struct winsize winsize;
+krb5_sigtype sigwinch(), oob();
+char *host; /* external, so it can be
+ reached from confirm_death() */
+
+
+
+/*
+ * The following routine provides compatibility (such as it is)
+ * between 4.2BSD Suns and others. Suns have only a `ttysize',
+ * so we convert it to a winsize.
+ */
+#ifdef TIOCGWINSZ
+#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
+#else
+#ifdef SYSV
+#ifndef SIGWINCH
+#define SIGWINCH SIGWINDOW
+#endif
+struct ttysize {
+ int ts_lines;
+ int ts_cols;
+};
+#define DEFAULT_LINES 24
+#define DEFAULT_COLS 80
+#endif
+
+
+
+int
+ get_window_size(fd, wp)
+int fd;
+struct winsize *wp;
+{
+ struct ttysize ts;
+ int error;
+#ifdef SYSV
+ char *envbuf;
+ ts.ts_lines = DEFAULT_LINES;
+ ts.ts_cols = DEFAULT_COLS;
+ if (( envbuf = getenv("LINES")) != (char *) 0)
+ ts.ts_lines = atoi(envbuf);
+ if (( envbuf = getenv("COLUMNS")) != (char *) 0)
+ ts.ts_cols = atoi(envbuf);
+#else
+ if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
+ return (error);
+#endif
+
+ wp->ws_row = ts.ts_lines;
+ wp->ws_col = ts.ts_cols;
+ wp->ws_xpixel = 0;
+ wp->ws_ypixel = 0;
+ return (0);
+}
+#endif /* TIOCGWINSZ */
+
+
+#ifdef USE_TERMIO
+/* Globals for terminal modes and flow control */
+struct termio defmodes;
+struct termio ixon_state;
+#endif
+
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *cp = (char *) NULL;
+#ifdef USE_TERMIO
+ struct termio ttyb;
+#else
+ struct sgttyb ttyb;
+#endif
+ struct passwd *pwd;
+ struct servent *sp;
+ int uid, options = 0, oldmask;
+ int on = 1;
+#ifdef KERBEROS
+ char **orig_argv = argv;
+ int sock;
+ krb5_flags authopts;
+ krb5_error_code status;
+#endif /* KERBEROS */
+
+ if ( argc < 2 ) goto usage;
+ host = argv[1];
+ argc -= 2;
+ argv +=2;
+ another:
+ if (argc > 0 && !strcmp(*argv, "-d")) {
+ argv++, argc--;
+ options |= SO_DEBUG;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-c")) {
+ confirm = 1;
+ argv++; argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-a")) { /* ask -- make remote */
+ argv++; argc--; /* machine ask for password */
+ null_local_username = 1; /* by giving null local user */
+ goto another; /* id */
+ }
+ if (argc > 0 && !strcmp(*argv, "-t")) {
+ argv++; argc--;
+ if (argc == 0) goto usage;
+ cp = *argv++; argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-n")) {
+ no_local_escape = 1;
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-7")) { /* Pass only 7 bits */
+ eight = 0;
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-noflow")) {
+ flow = 0; /* Turn off local flow control so
+ that ^S can be passed to emacs. */
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-l")) {
+ argv++, argc--;
+ if (argc == 0)
+ goto usage;
+ name = *argv++; argc--;
+ goto another;
+ }
+ if (argc > 0 && !strncmp(*argv, "-e", 2)) {
+ cmdchar = argv[0][2];
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-8")) {
+ eight = 1;
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-L")) {
+ litout = 1;
+ argv++, argc--;
+ goto another;
+ }
+#ifdef KERBEROS
+ if (argc > 0 && !strcmp(*argv, "-k")) {
+ argv++, argc--;
+ if (argc == 0) {
+ fprintf(stderr,
+ "rlogin: -k flag must be followed with a realm name.\n");
+ exit (1);
+ }
+ if(!(krb_realm = (char *)malloc(strlen(*argv) + 1))){
+ fprintf(stderr, "rlogin: Cannot malloc.\n");
+ exit(1);
+ }
+ strcpy(krb_realm, *argv);
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-x")) {
+ encrypt++;
+ argv++, argc--;
+ goto another;
+ }
+#endif /* KERBEROS */
+ if (host == 0)
+ goto usage;
+ if (argc > 0)
+ goto usage;
+ pwd = getpwuid(getuid());
+ if (pwd == 0) {
+ fprintf(stderr, "Who are you?\n");
+ exit(1);
+ }
+#ifdef KERBEROS
+ krb5_init_ets();
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf; /* Set up des buffers */
+ /*
+ * if there is an entry in /etc/services for Kerberos login,
+ * attempt to login with Kerberos.
+ * If we fail at any step, use the standard rlogin
+ */
+ if (encrypt)
+ sp = getservbyname("eklogin","tcp");
+ else
+ sp = getservbyname("klogin","tcp");
+ if (sp == 0) {
+ fprintf(stderr, "rlogin: %s/tcp: unknown service\n",
+ encrypt ? "eklogin" : "klogin");
+
+ try_normal(orig_argv);
+ }
+#else
+ sp = getservbyname("login", "tcp");
+ if (sp == 0) {
+ fprintf(stderr, "rlogin: login/tcp: unknown service\n");
+ exit(2);
+ }
+#endif /* KERBEROS */
+ if (cp == (char *) NULL) cp = getenv("TERM");
+ if (cp)
+ (void) strcpy(term, cp);
+ if (ioctl(0, TIOCGETP, &ttyb) == 0) {
+ (void) strcat(term, "/");
+ (void) strcat(term, speeds[ttyb.sg_ospeed]);
+ }
+ (void) get_window_size(0, &winsize);
+
+#ifdef USE_TERMIO
+ /**** moved before rcmd call so that if get a SIGPIPE in rcmd **/
+ /**** we will have the defmodes set already. ***/
+ (void)ioctl(fileno(stdin), TIOCGETP, &defmodes);
+ (void)ioctl(fileno(stdin), TIOCGETP,&ixon_state);
+#endif
+ (void) signal(SIGPIPE, lostpeer);
+
+ /* will use SIGUSR1 for window size hack, so hold it off */
+#ifdef sgi
+ oldmask = sigignore(sigmask(SIGURG) | sigmask(SIGUSR1));
+#else
+ oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
+#endif
+
+#ifdef KERBEROS
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&sock, &host, sp->s_port,
+ null_local_username ? NULL : pwd->pw_name,
+ name ? name : pwd->pw_name, term,
+ 0, "host", krb_realm,
+ &cred,
+ 0, /* No need for sequence number */
+ 0, /* No need for server seq # */
+ &local, &foreign,
+ authopts);
+ if (status) {
+ fprintf(stderr,
+ "%s: kcmd to host %s failed - %s\n",orig_argv[0], host,
+ error_message(status));
+ try_normal(orig_argv);
+ }
+ rem = sock;
+
+ /* setup eblock for des_read and write */
+ krb5_use_keytype(&eblock,cred->keyblock.keytype);
+ if ( status = krb5_process_key(&eblock,&cred->keyblock)) {
+ fprintf(stderr,
+ "%s: Cannot process session key : %s.\n",
+ orig_argv, error_message(status));
+ exit(1);
+ }
+#else
+ rem = rcmd(&host, sp->s_port,
+ null_local_username ? NULL : pwd->pw_name,
+ name ? name : pwd->pw_name, term, 0);
+#endif /* KERBEROS */
+
+ if (rem < 0)
+ exit(1);
+
+ /* we need to do the SETOWN here so that we get the SIGURG
+ registered if the URG data come in early, before the reader() gets
+ to do this for real (otherwise, the signal is never generated
+ by the kernel). We block it above, so when it gets unblocked
+ it will get processed by the reader().
+ There is a possibility that the signal will get delivered to both
+ writer and reader, but that is harmless, since the writer reflects
+ it to the reader, and the oob() processing code in the reader will
+ work properly even if it is called when no oob() data is present.
+ */
+#ifndef SYSV
+ (void) fcntl(rem, F_SETOWN, getpid());
+#endif
+ if (options & SO_DEBUG &&
+ setsockopt(rem, SOL_SOCKET, SO_DEBUG, &on, sizeof (on)) < 0)
+ perror("rlogin: setsockopt (SO_DEBUG)");
+ uid = getuid();
+ if (setuid(uid) < 0) {
+ perror("rlogin: setuid");
+ exit(1);
+ }
+ flowcontrol = flow; /* Set up really correct non-volatile variable */
+ doit(oldmask);
+ /*NOTREACHED*/
+ usage:
+#ifdef KERBEROS
+ fprintf (stderr,
+ "usage: rlogin host [-option] [-option...] [-k realm ] [-t ttytype] [-l username]\n");
+ fprintf (stderr, " where option is e, 7, 8, noflow, n, a, x, or c\n");
+#else /* !KERBEROS */
+ fprintf (stderr,
+ "usage: rlogin host [-option] [-option...] [-t ttytype] [-l username]\n");
+ fprintf (stderr, " where option is e, 7, 8, noflow, n, a, or c\n");
+#endif /* KERBEROS */
+ exit(1);
+}
+
+
+
+int confirm_death ()
+{
+ char hostname[33];
+ char input;
+ int answer;
+ if (!confirm) return (1); /* no confirm, just die */
+
+ if (gethostname (hostname, sizeof(hostname)-1) != 0)
+ strcpy (hostname, "???");
+ else
+ hostname[sizeof(hostname)-1] = '\0';
+
+ fprintf (stderr, "\r\nKill session on %s from %s (y/n)? ",
+ host, hostname);
+ fflush (stderr);
+ if (read(0, &input, 1) != 1)
+ answer = EOF; /* read from stdin */
+ else
+ answer = (int) input;
+ fprintf (stderr, "%c\r\n", answer);
+ fflush (stderr);
+ return (answer == 'y' || answer == 'Y' || answer == EOF ||
+ answer == 4); /* control-D */
+}
+
+
+
+#define CRLF "\r\n"
+
+int child;
+krb5_sigtype catchild();
+krb5_sigtype copytochild(), writeroob();
+
+int defflags, tabflag;
+int deflflags;
+char deferase, defkill;
+
+#ifdef USE_TERMIO
+char defvtim, defvmin;
+#ifdef hpux
+#include <sys/bsdtty.h>
+#include <sys/ptyio.h>
+#endif
+struct tchars {
+ char t_intrc; /* interrupt */
+ char t_quitc; /* quit */
+ char t_startc; /* start output */
+ char t_stopc; /* stop output */
+ char t_eofc; /* end-of-file */
+ char t_brkc; /* input delimiter (like nl) */
+};
+#endif
+
+struct tchars deftc;
+struct tchars notc = { -1, -1, -1, -1, -1, -1 };
+struct ltchars defltc;
+struct ltchars noltc = { -1, -1, -1, -1, -1, -1 };
+
+
+
+doit(oldmask)
+{
+#ifdef USE_TERMIO
+ struct termio sb;
+#else
+ struct sgttyb sb;
+#endif
+
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+ defflags = sb.sg_flags;
+#ifdef USE_TERMIO
+ tabflag = sb.c_oflag & TABDLY;
+ defflags |= ECHO;
+ deferase = sb.c_cc[VERASE];
+ defkill = sb.c_cc[VKILL];
+ sb.c_cc[VMIN] = 1;
+ sb.c_cc[VTIME] = 1;
+ defvtim = sb.c_cc[VTIME];
+ defvmin = sb.c_cc[VMIN];
+ deftc.t_quitc = CQUIT;
+ deftc.t_startc = CSTART;
+ deftc.t_stopc = CSTOP ;
+ deftc.t_eofc = CEOF;
+ deftc.t_brkc = '\n';
+#else
+ tabflag = defflags & TBDELAY;
+ defflags &= ECHO | CRMOD;
+ deferase = sb.sg_erase;
+ defkill = sb.sg_kill;
+ (void) ioctl(0, TIOCLGET, (char *)&deflflags);
+ (void) ioctl(0, TIOCGETC, (char *)&deftc);
+#endif
+
+ notc.t_startc = deftc.t_startc;
+ notc.t_stopc = deftc.t_stopc;
+ (void) ioctl(0, TIOCGLTC, (char *)&defltc);
+ (void) signal(SIGINT, SIG_IGN);
+ setsignal(SIGHUP, exit);
+ setsignal(SIGQUIT,exit);
+ child = fork();
+ if (child == -1) {
+ perror("rlogin: fork");
+ done(1);
+ }
+ if (child == 0) {
+ mode(1);
+ if (reader(oldmask) == 0) {
+ prf("Connection closed.");
+ exit(0);
+ }
+ sleep(1);
+ prf("\007Connection closed.");
+ exit(3);
+ }
+
+ /*
+ * We may still own the socket, and may have a pending SIGURG
+ * (or might receive one soon) that we really want to send to
+ * the reader. Set a trap that simply copies such signals to
+ * the child.
+ */
+ (void) signal(SIGURG, copytochild);
+ (void) signal(SIGUSR1, writeroob);
+#ifndef sgi
+ (void) sigsetmask(oldmask);
+#endif
+ (void) signal(SIGCHLD, catchild);
+ writer();
+ prf("Closed connection.");
+ done(0);
+}
+
+
+
+/*
+ * Trap a signal, unless it is being ignored.
+ */
+setsignal(sig, act)
+ int sig, (*act)();
+{
+#ifdef sgi
+ int omask = sigignore(sigmask(sig));
+#else
+ int omask = sigblock(sigmask(sig));
+#endif
+
+ if (signal(sig, act) == SIG_IGN)
+ (void) signal(sig, SIG_IGN);
+#ifndef sgi
+ (void) sigsetmask(omask);
+#endif
+}
+
+
+
+done(status)
+ int status;
+{
+ int w;
+
+ mode(0);
+ if (child > 0) {
+ /* make sure catchild does not snap it up */
+ (void) signal(SIGCHLD, SIG_DFL);
+ if (kill(child, SIGKILL) >= 0)
+ while ((w = wait((union wait *)0)) > 0 && w != child)
+ /*void*/;
+ }
+ exit(status);
+}
+
+
+
+/*
+ * Copy SIGURGs to the child process.
+ */
+krb5_sigtype
+ copytochild()
+{
+
+ (void) kill(child, SIGURG);
+}
+
+
+
+/*
+ * This is called when the reader process gets the out-of-band (urgent)
+ * request to turn on the window-changing protocol.
+ */
+krb5_sigtype
+ writeroob()
+{
+
+ if (dosigwinch == 0) {
+ sendwindow();
+ (void) signal(SIGWINCH, sigwinch);
+ }
+ dosigwinch = 1;
+}
+
+
+
+krb5_sigtype
+ catchild()
+{
+ union wait status;
+ int pid;
+
+ again:
+ pid = wait3(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
+ if (pid == 0)
+ return;
+ /*
+ * if the child (reader) dies, just quit
+ */
+#if defined(hpux)
+ if ((pid < 0) || ((pid == child) && (!WIFSTOPPED(status.w_stopval))))
+#else
+ if ((pid < 0) || ((pid == child) && (!WIFSTOPPED( status))))
+#endif
+ done((int)(status.w_termsig | status.w_retcode));
+ goto again;
+}
+
+
+
+/*
+ * writer: write to remote: 0 -> line.
+ * ~. terminate
+ * ~^Z suspend rlogin process.
+ * ~^Y suspend rlogin process, but leave reader alone.
+ */
+writer()
+{
+ char c;
+ register n;
+ register bol = 1; /* beginning of line */
+ register local = 0;
+
+#ifdef ultrix
+ fd_set waitread;
+
+ /* we need to wait until the reader() has set up the terminal, else
+ the read() below may block and not unblock when the terminal
+ state is reset.
+ */
+ for (;;) {
+ FD_ZERO(&waitread);
+ FD_SET(0, &waitread);
+ n = select(1, &waitread, 0, 0, 0, 0);
+ if (n < 0 && errno == EINTR)
+ continue;
+ if (n > 0)
+ break;
+ else
+ if (n < 0) {
+ perror("select");
+ break;
+ }
+ }
+#endif /* ultrix */
+ for (;;) {
+ n = read(0, &c, 1);
+ if (n <= 0) {
+ if (n < 0 && errno == EINTR)
+ continue;
+ break;
+ }
+ /*
+ * If we're at the beginning of the line
+ * and recognize a command character, then
+ * we echo locally. Otherwise, characters
+ * are echo'd remotely. If the command
+ * character is doubled, this acts as a
+ * force and local echo is suppressed.
+ */
+ if (bol) {
+ bol = 0;
+ if (c == cmdchar) {
+ bol = 0;
+ local = 1;
+ continue;
+ }
+ } else if (local) {
+ local = 0;
+ if (c == '.' || c == deftc.t_eofc) {
+ if (confirm_death()) {
+ echo(c);
+ break;
+ }
+ }
+ if ((c == defltc.t_suspc || c == defltc.t_dsuspc)
+ && !no_local_escape) {
+ bol = 1;
+ echo(c);
+ stop(c);
+ continue;
+ }
+ if (c != cmdchar)
+ (void) des_write(rem, &cmdchar, 1);
+ }
+ if (des_write(rem, &c, 1) == 0) {
+ prf("line gone");
+ break;
+ }
+ bol = c == defkill || c == deftc.t_eofc ||
+ c == deftc.t_intrc || c == defltc.t_suspc ||
+ c == '\r' || c == '\n';
+ }
+}
+
+
+
+echo(c)
+ register char c;
+{
+ char buf[8];
+ register char *p = buf;
+
+ c &= 0177;
+ *p++ = cmdchar;
+ if (c < ' ') {
+ *p++ = '^';
+ *p++ = c + '@';
+ } else if (c == 0177) {
+ *p++ = '^';
+ *p++ = '?';
+ } else
+ *p++ = c;
+ *p++ = '\r';
+ *p++ = '\n';
+ (void) write(1, buf, p - buf);
+}
+
+
+
+stop(cmdc)
+ char cmdc;
+{
+ mode(0);
+ (void) signal(SIGCHLD, SIG_IGN);
+ (void) kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
+ (void) signal(SIGCHLD, catchild);
+ mode(1);
+ sigwinch(); /* check for size changes */
+}
+
+
+
+krb5_sigtype
+ sigwinch()
+{
+ struct winsize ws;
+
+ if (dosigwinch && get_window_size(0, &ws) == 0 &&
+ memcmp(&winsize, &ws, sizeof (ws))) {
+ winsize = ws;
+ sendwindow();
+ }
+}
+
+
+
+/*
+ * Send the window size to the server via the magic escape
+ */
+sendwindow()
+{
+ char obuf[4 + sizeof (struct winsize)];
+ struct winsize *wp = (struct winsize *)(obuf+4);
+
+ obuf[0] = 0377;
+ obuf[1] = 0377;
+ obuf[2] = 's';
+ obuf[3] = 's';
+ wp->ws_row = htons(winsize.ws_row);
+ wp->ws_col = htons(winsize.ws_col);
+ wp->ws_xpixel = htons(winsize.ws_xpixel);
+ wp->ws_ypixel = htons(winsize.ws_ypixel);
+ (void) des_write(rem, obuf, sizeof(obuf));
+}
+
+
+
+/*
+ * reader: read from remote: line -> 1
+ */
+#define READING 1
+#define WRITING 2
+
+char rcvbuf[8 * 1024];
+int rcvcnt;
+int rcvstate;
+int ppid;
+jmp_buf rcvtop;
+
+krb5_sigtype
+ oob()
+{
+ int out = FWRITE, atmark, n;
+ int rcvd = 0;
+ char waste[BUFSIZ], mark;
+#ifdef USE_TERMIO
+ struct termio sb;
+#else
+ struct sgttyb sb;
+#endif
+
+ while (recv(rem, &mark, 1, MSG_OOB) < 0)
+ switch (errno) {
+
+ case EWOULDBLOCK:
+ /*
+ * Urgent data not here yet.
+ * It may not be possible to send it yet
+ * if we are blocked for output
+ * and our input buffer is full.
+ */
+ if (rcvcnt < sizeof(rcvbuf)) {
+ n = read(rem, rcvbuf + rcvcnt,
+ sizeof(rcvbuf) - rcvcnt);
+ if (n <= 0)
+ return;
+ rcvd += n;
+ } else {
+ n = read(rem, waste, sizeof(waste));
+ if (n <= 0)
+ return;
+ }
+ continue;
+
+ default:
+ return;
+ }
+ if (mark & TIOCPKT_WINDOW) {
+ /*
+ * Let server know about window size changes
+ */
+ (void) kill(ppid, SIGUSR1);
+ }
+ if (!eight && (mark & TIOCPKT_NOSTOP)) {
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+#ifdef USE_TERMIO
+ sb.c_iflag |= IXOFF;
+ sb.sg_flags &= ~ICANON;
+#else
+ sb.sg_flags &= ~CBREAK;
+ sb.sg_flags |= RAW;
+ notc.t_stopc = -1;
+ notc.t_startc = -1;
+ (void) ioctl(0, TIOCSETC, (char *)&notc);
+#endif
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
+ }
+ if (!eight && (mark & TIOCPKT_DOSTOP)) {
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+#ifdef USE_TERMIO
+ sb.sg_flags |= ICANON;
+ sb.c_iflag |= IXON;
+#else
+ sb.sg_flags &= ~RAW;
+ sb.sg_flags |= CBREAK;
+ notc.t_stopc = deftc.t_stopc;
+ notc.t_startc = deftc.t_startc;
+ (void) ioctl(0, TIOCSETC, (char *)&notc);
+#endif
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
+ }
+ if (mark & TIOCPKT_FLUSHWRITE) {
+#ifdef TIOCFLUSH
+ (void) ioctl(1, TIOCFLUSH, (char *)&out);
+#else
+ (void) ioctl(1, TCFLSH, 1);
+#endif
+ for (;;) {
+ if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
+ perror("ioctl");
+ break;
+ }
+ if (atmark)
+ break;
+ n = read(rem, waste, sizeof (waste));
+ if (n <= 0)
+ break;
+ }
+ /*
+ * Don't want any pending data to be output,
+ * so clear the recv buffer.
+ * If we were hanging on a write when interrupted,
+ * don't want it to restart. If we were reading,
+ * restart anyway.
+ */
+ rcvcnt = 0;
+ longjmp(rcvtop, 1);
+ }
+
+ /*
+ * oob does not do FLUSHREAD (alas!)
+ */
+
+ /*
+ * If we filled the receive buffer while a read was pending,
+ * longjmp to the top to restart appropriately. Don't abort
+ * a pending write, however, or we won't know how much was written.
+ */
+ if (rcvd && rcvstate == READING)
+ longjmp(rcvtop, 1);
+}
+
+
+
+/*
+ * reader: read from remote: line -> 1
+ */
+reader(oldmask)
+ int oldmask;
+{
+#if (defined(BSD) && BSD >= 43) || defined(ultrix)
+ int pid = getpid();
+#else
+ int pid = -getpid();
+#endif
+ int n, remaining;
+ char *bufp = rcvbuf;
+
+ (void) signal(SIGTTOU, SIG_IGN);
+ (void) signal(SIGURG, oob);
+ ppid = getppid();
+#ifndef SYSV
+ (void) fcntl(rem, F_SETOWN, pid);
+#endif
+ (void) setjmp(rcvtop);
+#ifndef sgi
+ (void) sigsetmask(oldmask);
+#endif
+ for (;;) {
+ while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
+ rcvstate = WRITING;
+ n = write(1, bufp, remaining);
+ if (n < 0) {
+ if (errno != EINTR)
+ return (-1);
+ continue;
+ }
+ bufp += n;
+ }
+ bufp = rcvbuf;
+ rcvcnt = 0;
+ rcvstate = READING;
+ rcvcnt = des_read(rem, rcvbuf, sizeof (rcvbuf));
+ if (rcvcnt == 0)
+ return (0);
+ if (rcvcnt < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("read");
+ return (-1);
+ }
+ }
+}
+
+
+
+mode(f)
+{
+ struct ltchars *ltc;
+#ifdef USE_TERMIO
+ struct termio sb;
+#else
+ struct tchars *tc;
+ struct sgttyb sb;
+ int lflags;
+ (void) ioctl(0, TIOCLGET, (char *)&lflags);
+#endif
+
+ (void) ioctl(0, TIOCGETP, (char *)&sb);
+ switch (f) {
+
+ case 0:
+#ifdef USE_TERMIO
+ /*
+ ** remember whether IXON was set, so it can be restored
+ ** when mode(1) is next done
+ */
+ (void) ioctl(fileno(stdin), TIOCGETP, &ixon_state);
+ /*
+ ** copy the initial modes we saved into sb; this is
+ ** for restoring to the initial state
+ */
+ (void)memcpy(&sb, &defmodes, sizeof(defmodes));
+
+#else
+ sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
+ sb.sg_flags |= defflags|tabflag;
+ sb.sg_kill = defkill;
+ sb.sg_erase = deferase;
+ lflags = deflflags;
+ tc = &deftc;
+#endif
+ ltc = &defltc;
+ break;
+
+ case 1:
+#ifdef USE_TERMIO
+ /*
+ ** turn off output mappings
+ */
+ sb.c_oflag &= ~(ONLCR|OCRNL);
+ /*
+ ** turn off canonical processing and character echo;
+ ** also turn off signal checking -- ICANON might be
+ ** enough to do this, but we're being careful
+ */
+ sb.c_lflag &= ~(ECHO|ICANON|ISIG);
+ sb.c_cc[VTIME] = 1;
+ sb.c_cc[VMIN] = 1;
+ if (eight)
+ sb.c_iflag &= ~(ISTRIP);
+ /* preserve tab delays, but turn off tab-to-space expansion */
+ if ((sb.c_oflag & TABDLY) == TAB3)
+ sb.c_oflag &= ~TAB3;
+ /*
+ ** restore current flow control state
+ */
+ if ((ixon_state.c_iflag & IXON) && flow ) {
+ sb.c_iflag |= IXON;
+ } else {
+ sb.c_iflag &= ~IXON;
+ }
+#else /* ! USE_TERMIO */
+ sb.sg_flags &= ~(CBREAK|RAW);
+ sb.sg_flags |= (!flow ? RAW : CBREAK);
+ /* preserve tab delays, but turn off XTABS */
+ if ((sb.sg_flags & TBDELAY) == XTABS)
+ sb.sg_flags &= ~TBDELAY;
+ sb.sg_kill = sb.sg_erase = -1;
+#ifdef LLITOUT
+ if (litout)
+ lflags |= LLITOUT;
+#endif
+#ifdef LPASS8
+ if (eight)
+ lflags |= LPASS8;
+#endif /* LPASS8 */
+ tc = &notc;
+ sb.sg_flags &= ~defflags;
+#endif /* USE_TERMIO */
+
+ ltc = &noltc;
+ break;
+
+ default:
+ return;
+ }
+ (void) ioctl(0, TIOCSLTC, (char *)ltc);
+#ifndef USE_TERMIO
+ (void) ioctl(0, TIOCSETC, (char *)tc);
+ (void) ioctl(0, TIOCLSET, (char *)&lflags);
+#endif
+ (void) ioctl(0, TIOCSETN, (char *)&sb);
+}
+
+
+
+/*VARARGS*/
+prf(f, a1, a2, a3, a4, a5)
+ char *f;
+{
+ fprintf(stderr, f, a1, a2, a3, a4, a5);
+ fprintf(stderr, CRLF);
+}
+
+
+
+#ifdef KERBEROS
+void try_normal(argv)
+ char **argv;
+{
+ register char *host;
+
+ if (encrypt)
+ exit(1);
+ fprintf(stderr,"trying normal rlogin (%s)\n",
+ UCB_RLOGIN);
+ fflush(stderr);
+
+ host = rindex(argv[0], '/');
+ if (host)
+ host++;
+ else
+ host = argv[0];
+ if (!strcmp(host, "rlogin"))
+ argv++;
+
+ execv(UCB_RLOGIN, argv);
+ perror("exec");
+ exit(1);
+}
+
+
+
+char storage[2*BUFSIZ]; /* storage for the decryption */
+int nstored = 0;
+char *store_ptr = storage;
+
+#ifndef OLD_VERSION
+
+int des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len,rd_len;
+ int cc;
+
+ if (!encrypt)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+#ifdef BITS64
+ /*
+ * XXX Ick. This assumes big endian byte order.
+ */
+ rd_len = 0;
+ if ((cc = krb5_net_read(fd, (char *)&rd_len + 4, 4)) != 4) {
+#else
+ if ((cc = krb5_net_read(fd, (char *)&rd_len, sizeof(rd_len))) !=
+ sizeof(rd_len)) {
+#endif
+ /* XXX can't read enough, pipe
+ must have closed */
+ return(0);
+ }
+ rd_len = ntohl(rd_len);
+ net_len = krb5_encrypt_size(rd_len,eblock.crypto_entry);
+ if (net_len <= 0 || net_len > sizeof(des_inbuf)) {
+ /* preposterous length; assume out-of-sync; only
+ recourse is to close connection, so return 0 */
+ fprintf(stderr,"Read size problem.\n");
+ return(0);
+ }
+ if ((cc = krb5_net_read(fd, desinbuf.data, net_len)) != net_len) {
+ /* pipe must have closed, return 0 */
+ fprintf(stderr,
+ "Read error: length received %d != expected %d.\n",
+ cc,net_len);
+ return(0);
+ }
+ /* decrypt info */
+ if ((krb5_decrypt(desinbuf.data,
+ (krb5_pointer) storage,
+ net_len,
+ &eblock, 0))) {
+ fprintf(stderr,"Cannot decrypt data from network.\n");
+ return(0);
+ }
+ store_ptr = storage;
+ nstored = rd_len;
+ if (nstored > len) {
+ memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+
+ return(nreturned);
+}
+
+
+
+int des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ long net_len;
+
+ if (!encrypt)
+ return(write(fd, buf, len));
+
+ desoutbuf.length = krb5_encrypt_size(len,eblock.crypto_entry);
+ if (desoutbuf.length > sizeof(des_outbuf)){
+ fprintf(stderr,"Write size problem.\n");
+ return(-1);
+ }
+ if (( krb5_encrypt((krb5_pointer)buf,
+ desoutbuf.data,
+ len,
+ &eblock,
+ 0))){
+ fprintf(stderr,"Write encrypt problem.\n");
+ return(-1);
+ }
+
+ net_len = htonl(len);
+#ifdef BITS64
+ (void) write(fd,(char *)&net_len + 4, 4);
+#else
+ (void) write(fd, &net_len, sizeof(net_len));
+#endif
+ if (write(fd, desoutbuf.data,desoutbuf.length) != desoutbuf.length){
+ fprintf(stderr,"Could not write out all data.\n");
+ return(-1);
+ }
+ else return(len);
+}
+
+
+
+#else /* Original version placed here so that testing could be done
+ to determine why rlogin with encryption on is slower with
+ version 5 as compared to version 4. */
+
+#define ENCRYPT 1
+#define DECRYPT 0
+
+
+
+int des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len, rd_len;
+ int cc;
+
+ if (!encrypt)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ bcopy(store_ptr, buf, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ bcopy(store_ptr, buf, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+#ifdef BITS64
+ net_len = 0;
+ if ((cc = krb5_net_read(fd, (char *)&net_len + 4, 4)) != 4) {
+#else
+ if ((cc = krb5_net_read(fd, &net_len, sizeof(net_len))) !=
+ sizeof(net_len)) {
+#endif
+ /* XXX can't read enough, pipe
+ must have closed */
+ return(0);
+ }
+ net_len = ntohl(net_len);
+ if (net_len < 0 || net_len > sizeof(des_inbuf)) {
+ /* XXX preposterous length, probably out of sync.
+ act as if pipe closed */
+ return(0);
+ }
+ /* the writer tells us how much real data we are getting, but
+ we need to read the pad bytes (8-byte boundary) */
+#ifdef NOROUNDUP
+ rd_len = ((((net_len)+((8)-1))/(8))*(8));
+#else
+ rd_len = roundup(net_len, 8);
+#endif
+ if ((cc = krb5_net_read(fd, des_inbuf, rd_len)) != rd_len) {
+ /* pipe must have closed, return 0 */
+ return(0);
+ }
+ (void) mit_des_cbc_encrypt(
+ des_inbuf,
+ storage,
+ (net_len < 8) ? 8 : net_len,
+ eblock.priv,
+ eblock.key->contents,
+ DECRYPT);
+ /*
+ * when the cleartext block is < 8 bytes, it is "right-justified"
+ * in the block, so we need to adjust the pointer to the data
+ */
+ if (net_len < 8)
+ store_ptr = storage + 8 - net_len;
+ else
+ store_ptr = storage;
+ nstored = net_len;
+ if (nstored > len) {
+ bcopy(store_ptr, buf, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ bcopy(store_ptr, buf, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+ return(nreturned);
+}
+
+
+
+int des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ long net_len;
+ static int seeded = 0;
+ static char garbage_buf[8];
+ long garbage;
+
+ if (!encrypt)
+ return(write(fd, buf, len));
+
+#define min(a,b) ((a < b) ? a : b)
+
+ if (len < 8) {
+ if (!seeded) {
+ seeded = 1;
+ srandom((int) time((long *)0));
+ }
+ garbage = random();
+ /* insert random garbage */
+ (void) bcopy(&garbage, garbage_buf, min(sizeof(long),8));
+
+ /* this "right-justifies" the data in the buffer */
+ (void) bcopy(buf, garbage_buf + 8 - len, len);
+ }
+
+ (void) mit_des_cbc_encrypt((len < 8) ? garbage_buf : buf,
+ des_outbuf,
+ (len < 8) ? 8 : len,
+ eblock.priv,
+ eblock.key->contents,
+ ENCRYPT);
+
+ /* tell the other end the real amount, but send an 8-byte padded
+ packet */
+ net_len = htonl(len);
+#ifdef BITS64
+ (void) write(fd,(char *)&net_len + 4, 4);
+#else
+ (void) write(fd, &net_len, sizeof(net_len));
+#endif
+#ifdef NOROUNDUP
+ (void) write(fd, des_outbuf, ((((len)+((8)-1))/(8))*(8)));
+#else
+ (void) write(fd, des_outbuf, roundup(len,8));
+#endif
+ return(len);
+}
+
+#endif /* OLD_VERSION */
+#endif /* KERBEROS */
+
+
+
+krb5_sigtype lostpeer()
+{
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ prf("\007Connection closed.");
+ done(1);
+}
diff --git a/src/appl/bsd/krlogind.M b/src/appl/bsd/krlogind.M
new file mode 100644
index 0000000000..4ceebd7739
--- /dev/null
+++ b/src/appl/bsd/krlogind.M
@@ -0,0 +1,102 @@
+.\" Copyright (c) 1983 Regents of the University of California.
+.\" All rights reserved. The Berkeley software License Agreement
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\" @(#)rlogind.8c 6.3 (Berkeley) 5/24/86
+.\"
+.TH RLOGIND 8C "May 24, 1986"
+.UC 5
+.SH NAME
+rlogind \- remote login server
+.SH SYNOPSIS
+.B /etc/rlogind
+[
+.B \-d
+]
+.SH DESCRIPTION
+.I Rlogind
+is the server for the
+.IR rlogin (1C)
+program. The server provides a remote login facility
+with authentication based on privileged port numbers from trusted hosts.
+.PP
+.I Rlogind
+listens for service requests at the port indicated in
+the ``login'' service specification; see
+.IR services (5).
+When a service request is received the following protocol
+is initiated:
+.IP 1)
+The server checks the client's source port.
+If the port is not in the range 0-1023, the server
+aborts the connection.
+.IP 2)
+The server checks the client's source address
+and requests the corresponding host name (see
+.IR gethostbyaddr (3N),
+.IR hosts (5)
+and
+.IR named (8)).
+If the hostname cannot be determined,
+the dot-notation representation of the host address is used.
+.PP
+Once the source port and address have been checked,
+.I rlogind
+allocates a pseudo terminal (see
+.IR pty (4)),
+and manipulates file descriptors so that the slave
+half of the pseudo terminal becomes the
+.B stdin ,
+.B stdout ,
+and
+.B stderr
+for a login process.
+The login process is an instance of the
+.IR login (1)
+program, invoked with the
+.B \-r
+option. The login process then proceeds with the authentication
+process as described in
+.IR rshd (8C),
+but if automatic authentication fails, it reprompts the user
+to login as one finds on a standard terminal line.
+.PP
+The parent of the login process manipulates the master side of
+the pseduo terminal, operating as an intermediary
+between the login process and the client instance of the
+.I rlogin
+program. In normal operation, the packet protocol described
+in
+.IR pty (4)
+is invoked to provide ^S/^Q type facilities and propagate
+interrupt signals to the remote programs. The login process
+propagates the client terminal's baud rate and terminal type,
+as found in the environment variable, ``TERM''; see
+.IR environ (7).
+The screen or window size of the terminal is requested from the client,
+and window size changes from the client are propagated to the pseudo terminal.
+.SH DIAGNOSTICS
+All diagnostic messages are returned on the connection
+associated with the
+.BR stderr ,
+after which any network connections are closed.
+An error is indicated by a leading byte with a value of 1.
+.PP
+.B ``Try again.''
+.br
+A
+.I fork
+by the server failed.
+.PP
+.B ``/bin/sh: ...''
+.br
+The user's login shell could not be started.
+.SH BUGS
+The authentication procedure used here assumes the integrity
+of each client machine and the connecting medium. This is
+insecure, but is useful in an ``open'' environment.
+.PP
+A facility to allow all data exchanges to be encrypted should be
+present.
+.PP
+A more extensible protocol should be used.
diff --git a/src/appl/bsd/krlogind.c b/src/appl/bsd/krlogind.c
new file mode 100644
index 0000000000..9360b6feff
--- /dev/null
+++ b/src/appl/bsd/krlogind.c
@@ -0,0 +1,1365 @@
+/*
+ * $Source$
+ * $Header$
+ */
+
+
+#ifndef lint
+static char *rcsid_rlogind_c = "$Header$";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rlogind.c 5.17 (Berkeley) 8/31/88";
+#endif /* not lint */
+
+ /*
+ * remote login server:
+ * remuser\0
+ * locuser\0
+ * terminal info\0
+ * data
+ */
+
+ /*
+ * This is the rlogin daemon. The very basic protocol for checking
+ * authentication and authorization is:
+ * 1) Check authentication.
+ * 2) Check authorization via the access-control files:
+ * ~/.k5login (using krb5_kuserok) and/or
+ * ~/.rhosts (using ruserok).
+ * 3) Prompt for password if any checks fail, or if so configured.
+ * Allow login if all goes well either by calling the accompanying login.krb
+ * or /bin/login, according to the definition of DO_NOT_USE_K_LOGIN.
+ *
+ * The configuration is done either by command-line arguments passed by inetd,
+ * or by the name of the daemon. If command-line arguments are present, they
+ * take priority. The options are:
+ * -k and -K means check .k5login (using krb5_kuserok).
+ * -r and -R means check .rhosts (using ruserok).
+ * -p and -P means prompt for password.
+ * The difference between upper and lower case is as follows:
+ * If lower case -r or -k, then as long as one of krb5_kuserok or ruserok
+ * passes, allow login without password. If the -p option is passed with -r
+ * or -k, then if both checks fail, allow login but only after password
+ * verification.
+ * If uppercase -R or -K, then those checks must be passed, regardless of
+ * other checks, else no login with or without password.
+ * If the -P option is passed, then the password is verified in
+ * addition to all other checks. If -p is not passed with -k or -r, and both
+ * checks fail, then login permission is denied.
+ * -x and -e means use encryption.
+ * If no command-line arguments are present, then the presence of the
+ * letters kKrRexpP in the program-name before "logind" determine the
+ * behaviour of the program exactly as with the command-line arguments.
+ *
+ * If the ruserok check is to be used, then the client should connect from a
+ * privileged port, else deny permission.
+ */
+
+ /* DEFINES:
+ * KERBEROS - Define this if application is to be kerberised.
+ * CRYPT - Define this if encryption is to be an option.
+ * DO_NOT_USE_K_LOGIN - Define this if you want to use /bin/login instead
+ * of the accompanying login.krb. In that case, the remote user's
+ * name must be present in the local .rhosts file, regardless of
+ * any options specified.
+ * LOG_ALL_LOGINS - Define this if you want to log all logins.
+ * LOG_OTHER_USERS - Define this if you want to log all principals that do
+ * not map onto the local user.
+ * LOG_REMOTE_REALM - Define this if you want to log all principals from
+ * remote realms.
+ * Note: Root logins are always logged.
+ */
+
+#define LOG_REMOTE_REALM
+#define KERBEROS
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+/* #include <sys/unistd.h> ??? What system has a sys/unistd.h? */
+
+#include <netinet/in.h>
+#include <errno.h>
+#include <pwd.h>
+
+#ifdef sun
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#include <signal.h>
+#ifdef hpux
+#include <sys/ptyio.h>
+#endif
+
+#ifdef sysvimp
+#include <compat.h>
+#define STREAMS
+#include <sys/stropts.h>
+#endif
+
+#ifdef SYSV
+#define USE_TERMIO
+#endif
+
+#ifdef USE_TERMIO
+#include <termio.h>
+#else
+#include <sgtty.h>
+#endif /* USE_TERMIO */
+
+#include <netdb.h>
+#include <syslog.h>
+#include <strings.h>
+#include <sys/param.h>
+#include <utmp.h>
+
+#ifdef NO_WINSIZE
+struct winsize {
+ unsigned short ws_row, ws_col;
+ unsigned short ws_xpixel, ws_ypixel;
+};
+#endif /* NO_WINSIZE */
+
+#ifdef KERBEROS
+
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/crc-32.h>
+#include <krb5/mit-des.h>
+#include <krb5/los-proto.h>
+
+#include <com_err.h>
+
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+#define BUFSIZ 4096
+
+#define SECURE_MESSAGE "This rlogin session is using DES encryption for all data transmissions.\r\n"
+
+char des_inbuf[2*BUFSIZ]; /* needs to be > largest read size */
+char des_outbuf[2*BUFSIZ]; /* needs to be > largest write size */
+krb5_data desinbuf,desoutbuf;
+krb5_encrypt_block eblock; /* eblock for encrypt/decrypt */
+
+krb5_authenticator *kdata;
+krb5_ticket *ticket = 0;
+
+#ifdef CRAY
+#ifndef BITS64
+#define BITS64
+#endif
+#endif
+
+#define ARGSTR "rRkKeExXpP?"
+#else /* !KERBEROS */
+#define ARGSTR "rRpP?"
+#define des_read read
+#define des_write write
+#endif /* KERBEROS */
+
+#ifdef DO_NOT_USE_K_LOGIN
+#ifdef sysvimp
+#define LOGIN_PROGRAM "/bin/remlogin"
+#else
+#define LOGIN_PROGRAM "/bin/login"
+#endif
+#else /* DO_NOT_USE_K_LOGIN */
+#define LOGIN_PROGRAM "/krb5/etc/login.krb5"
+#endif
+
+struct utmp wtmp;
+#define NMAX sizeof(wtmp.ut_name)
+#define MAXRETRIES 4
+#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host)
+#define MAX_PROG_NAME 16
+
+char lusername[NMAX+1];
+char *rusername = 0;
+char *krusername = 0;
+char term[64];
+char rhost_name[128];
+
+extern int errno;
+int reapchild();
+struct passwd *getpwnam();
+#ifndef ultrix
+char *malloc();
+#endif
+char *progname;
+
+void fatal(), fatalperror(), doit(), usage();
+int princ_maps_to_lname(), default_realm();
+
+int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0;
+int do_encrypt = 0, passwd_if_fail = 0, passwd_req = 0;
+int failed_auth = 0, failed_k5 = 0, failed_rhosts = 0;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int opterr, optind;
+ int on = 1, fromlen, ch, i;
+ struct sockaddr_in from;
+ char *options;
+
+ progname = *argv;
+
+#ifdef KERBEROS
+
+#ifndef LOG_NDELAY
+#define LOG_NDELAY 0
+#endif
+
+
+#ifndef LOG_AUTH /* 4.2 syslog */
+ openlog(progname, LOG_PID|LOG_NDELAY);
+#else
+ openlog(progname, LOG_PID | LOG_AUTH | LOG_NDELAY, LOG_AUTH);
+#endif /* 4.2 syslog */
+
+#else /* ! KERBEROS */
+
+#ifndef LOG_AUTH /* 4.2 syslog */
+ openlog("rlogind", LOG_PID| LOG_NDELAY);
+#else
+ openlog("rlogind", LOG_PID | LOG_AUTH | LOG_NDELAY, LOG_AUTH);
+#endif /* 4.2 syslog */
+
+#endif /* KERBEROS */
+
+ if (argc == 1) { /* Get parameters from program name. */
+ if (strlen(progname) > MAX_PROG_NAME) {
+ usage();
+ exit(1);
+ }
+ options = (char *) malloc(MAX_PROG_NAME+1);
+ options[0] = '\0';
+ for (i = 0; (progname[i] != '\0') && (i < MAX_PROG_NAME); i++)
+ if (!strcmp(progname+i, "logind")) {
+ strcpy(options, "-");
+ strncat(options, progname, i);
+ argc = 2;
+ argv[1] = options;
+ argv[2] = NULL;
+ break;
+ }
+ if (options[0] == '\0') {
+ usage();
+ exit(1);
+ }
+ }
+
+ /* Analyse parameters. */
+ opterr = 0;
+ while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ switch (ch) {
+ case 'r':
+ must_pass_one = 1; /* If just 'r', any one check must succeed */
+ break;
+ case 'R': /* If 'R', must pass .rhosts check*/
+ must_pass_rhosts = 1;
+ if (must_pass_one)
+ must_pass_one = 0;
+ break;
+#ifdef KERBEROS
+ case 'k':
+ must_pass_one = 1; /* If just 'k', any one check must succeed */
+ break;
+ case 'K': /* If 'K', must pass .k5login check*/
+ must_pass_k5 = 1;
+ if (must_pass_one)
+ must_pass_one = 0;
+ break;
+#ifdef CRYPT
+ case 'x': /* Use encryption. */
+ case 'X':
+ case 'e':
+ case 'E':
+ do_encrypt = 1;
+ break;
+#endif
+#endif
+ case 'p':
+ passwd_if_fail = 1; /* Passwd reqd if any check fails */
+ break;
+ case 'P': /* passwd is a must */
+ passwd_req = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ exit(1);
+ 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");
+#ifdef STDERR_FILENO
+ fatal(STDERR_FILENO, "Can't get peer name of remote host", 1);
+#else
+ fatal(3, "Can't get peer name of remote host", 1);
+#endif
+
+ }
+ if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+
+ doit(0, &from);
+}
+
+
+
+#ifndef LOG_AUTH
+#define LOG_AUTH 0
+#endif
+
+int child;
+int cleanup();
+int netf;
+krb5_principal client;
+char line[MAXPATHLEN];
+extern char *inet_ntoa();
+
+#ifdef TIOCSWINSZ
+struct winsize win = { 0, 0, 0, 0 };
+#endif
+
+int pid; /* child process id */
+
+void doit(f, fromp)
+ int f;
+ struct sockaddr_in *fromp;
+{
+ int i, p, t, on = 1;
+ register struct hostent *hp;
+ char c;
+ char buferror[255];
+ struct passwd *pwd;
+
+ netf = -1;
+ alarm(60);
+ read(f, &c, 1);
+
+ if (c != 0){
+ exit(1);
+ }
+
+ alarm(0);
+ fromp->sin_port = ntohs((u_short)fromp->sin_port);
+ hp = gethostbyaddr(&fromp->sin_addr, sizeof (struct in_addr),
+ fromp->sin_family);
+ if (hp == 0) {
+ /*
+ * Only the name is used below.
+ */
+ sprintf(rhost_name,"%s",inet_ntoa(fromp->sin_addr));
+ }
+
+ /* Save hostent information.... */
+ else strcpy(rhost_name,hp->h_name);
+
+ if (fromp->sin_family != AF_INET)
+ fatal(f, "Permission denied - Malformed from address\n");
+
+#ifdef KERBEROS
+ if (must_pass_k5 || must_pass_one) {
+ /* Init error messages and setup des buffers */
+ krb5_init_ets();
+ desinbuf.data = des_inbuf;
+ desoutbuf.data = des_outbuf; /* Set up des buffers */
+ }
+ /* Must come from privileged port when .rhosts is being looked into */
+ if ((must_pass_rhosts || must_pass_one)
+ && (fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2))
+#else /* !KERBEROS */
+ if (fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2)
+#endif /* KERBEROS */
+ fatal(f, "Permission denied - Connection from bad port");
+
+ /* Set global netf to f now : we may need to drop everything
+ in do_krb_login. */
+ netf = f;
+
+#if defined(KERBEROS)
+ /*
+ * If encrypting, we need to respond here, since we have to send
+ * the mutual authentication stuff before the response
+ *
+ * Do_krb_login has been modified to handle rlogin type requests
+ * also....
+ */
+ /* All validation, and authorization goes through do_krb_login() */
+ do_krb_login(rhost_name);
+
+ if (failed_auth || (failed_k5 && failed_rhosts)) {
+ if (must_pass_one && passwd_if_fail)
+ passwd_req = 1;
+ else
+ fatal(netf, "Permission denied");
+ }
+#else
+ rusername = malloc(sizeof (lusername) + 1);
+ getstr(rusername, sizeof(lusername), "remuser");
+ getstr(lusername, sizeof(lusername), "locuser");
+ getstr(term, sizeof(term), "Terminal type");
+#endif
+
+ write(f, "", 1);
+ if (getpty(&p,line))
+ fatal(f, "Out of ptys");
+#ifdef TIOCSWINSZ
+ (void) ioctl(p, TIOCSWINSZ, &win);
+#endif
+
+#ifndef sysvimp /* IMP has a problem with opening and closing
+ it's stream pty by the parent process */
+
+ /* Make sure we can open slave pty, then close it for system 5 so that
+ the process group is set correctly..... */
+ t = open(line, O_RDWR);
+ if (t < 0)
+ fatalperror(f, line);
+#ifdef NOFCHMOD
+ if (chmod(t,0))
+#else
+ if (fchmod(t, 0))
+#endif
+ fatalperror(f, line);
+#ifndef SYSV
+ signal(SIGHUP, SIG_IGN);
+ vhangup();
+ signal(SIGHUP, SIG_DFL);
+#ifdef ultrix /* Someone needs to cleanup all this and have a consistant
+ way of associating controlling tty to a process. */
+ setpgrp();
+#endif
+ t = open(line, O_RDWR);
+ if (t < 0)
+ fatalperror(f, line);
+#endif
+#ifdef SYSV
+ close(t);
+#endif
+#endif /* sysvimp */
+ signal(SIGCHLD, cleanup);
+ signal(SIGTERM, cleanup);
+ pid = fork();
+ if (pid < 0)
+ fatalperror(f, "", errno);
+ if (pid == 0) {
+ {
+#ifdef USE_TERMIO
+ struct termio b;
+#define TIOCGETP TCGETA
+#define TIOCSETP TCSETA
+#ifdef MIN
+#undef MIN
+#endif
+#define MIN 1
+#define TIME 0
+
+#else
+ struct sgttyb b;
+#endif
+#ifdef SYSV
+ (void) setpgrp();
+ /* SYSV open slave device: We closed it above so pgrp
+ would be set correctly...*/
+ t = open(line, O_RDWR);
+ if (t < 0)
+ fatalperror(f, line);
+#endif
+#ifdef STREAMS
+ while (ioctl (t, I_POP, 0) == 0); /*Clear out any old lined's*/
+#endif
+ /* Under Ultrix 3.0, the pgrp of the slave pty terminal
+ needs to be set explicitly. Why rlogind works at all
+ without this on 4.3BSD is a mystery.
+ It seems to work fine on 4.3BSD with this code enabled.
+ IMP's need both ioctl and setpgrp..
+ */
+#if !defined(SYSV) || defined(sysvimp)
+ /* SYSV set process group prior to opening pty */
+#ifdef sysvimp
+ pid = 0;
+#else
+#ifdef convex
+ pid = getpgrp();
+#else
+ pid = getpgrp(getpid());
+#endif
+#endif
+ ioctl(t, TIOCSPGRP, &pid);
+ pid = 0; /*reset pid incase exec fails*/
+#endif
+#ifdef STREAMS
+ if (line_push(t) < 0)
+ fatalperror(f, "IPUSH",errno);
+#endif
+ (void)ioctl(t, TIOCGETP, &b);
+#ifdef USE_TERMIO
+ /* The key here is to just turn off echo */
+ b.c_iflag &= ~(ICRNL|IUCLC);
+ b.c_iflag |= IXON;
+ b.c_cflag |= CS8;
+ b.c_lflag |= ICANON|ISIG;
+ b.c_lflag &= ~(ECHO);
+ b.c_cc[VMIN] = MIN;
+ b.c_cc[VTIME] = TIME;
+#else
+ b.sg_flags = RAW|ANYP;
+#endif
+ (void)ioctl(t, TIOCSETP, &b);
+ /*
+ ** signal the parent that we have turned off echo
+ ** on the slave side of the pty ... he's waiting
+ ** because otherwise the rlogin protocol junk gets
+ ** echo'd to the user (locuser^@remuser^@term/baud)
+ ** and we don't get the right tty affiliation, and
+ ** other kinds of hell breaks loose ...
+ */
+ (void) write(t, &c, 1);
+
+ }
+ close(f), close(p);
+ dup2(t, 0), dup2(t, 1), dup2(t, 2);
+ if (t > 2)
+ close(t);
+#if defined(sysvimp)
+ setcompat (COMPAT_CLRPGROUP | (getcompat() & ~COMPAT_BSDTTY));
+#endif
+
+ /* Log access to account */
+ pwd = (struct passwd *) getpwnam(lusername);
+ if (pwd && (pwd->pw_uid == 0)) {
+ if (passwd_req)
+ syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) forcing password access",
+ krusername, rusername, rhost_name);
+ else
+ syslog(LOG_NOTICE, "ROOT login by %s (%s@%s) ",
+ krusername, rusername, rhost_name);
+ }
+
+#if defined(KERBEROS) && defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
+ /* Log if principal is from a remote realm */
+else if (!default_realm(client))
+#endif
+
+#if defined(KERBEROS) && defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
+ /* Log if principal name does not map to local username */
+else if (!princ_maps_to_lname(client, lusername))
+#endif /* LOG_OTHER_USERS */
+
+#ifdef LOG_ALL_LOGINS /* Log everything */
+else
+#endif
+
+#if defined(LOG_REMOTE_REALM) || defined(LOG_OTHER_USERS) || defined(LOG_ALL_LOGINS)
+ {
+ if (passwd_req)
+ syslog(LOG_NOTICE,
+ "login by %s (%s@%s) as %s forcing password access\n",
+ krusername, rusername, rhost_name, lusername);
+ else
+ syslog(LOG_NOTICE,
+ "login by %s (%s@%s) as %s\n",
+ krusername, rusername, rhost_name, lusername);
+ }
+#endif
+
+#ifdef DO_NOT_USE_K_LOGIN
+ execl(LOGIN_PROGRAM, "login", "-r", rhost_name, 0);
+#else
+ if (passwd_req)
+ execl(LOGIN_PROGRAM, "login", rhost_name,0);
+ else
+ execl(LOGIN_PROGRAM, "login", "-f", rhost_name, 0);
+#endif
+
+ fatalperror(2, LOGIN_PROGRAM, errno);
+ /*NOTREACHED*/
+ }
+ /*
+ ** wait for child to start ... read one byte
+ ** -- see the child, who writes one byte after
+ ** turning off echo on the slave side ...
+ ** The master blocks here until it reads a byte.
+ */
+ if (read(p, &c, 1) != 1) {
+ /*
+ * Problems read failed ...
+ */
+ sprintf(buferror, "Cannot read slave pty %s ",line);
+ fatalperror(p,buferror,errno);
+ }
+
+#if defined(KERBEROS)
+ if (do_encrypt)
+ if ((des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE))) < 0){
+ sprintf(buferror, "Cannot encrypt-write network.");
+ fatal(p,buferror);
+ }
+ else
+ /*
+ * if encrypting, don't turn on NBIO, else the read/write routines
+ * will fail to work properly
+ */
+#endif /* KERBEROS */
+ {
+ ioctl(f, FIONBIO, &on);
+ ioctl(p, FIONBIO, &on);
+ }
+#ifdef hpux
+ /******** FIONBIO doesn't currently work on ptys, should be O_NDELAY? **/
+ /*** get flags and add O_NDELAY **/
+ (void) fcntl(p,F_SETFL,fcntl(p,F_GETFL,0) | O_NDELAY);
+#endif
+
+ ioctl(p, TIOCPKT, &on);
+ signal(SIGTSTP, SIG_IGN);
+#ifdef hpux
+ setpgrp2(0, 0);
+#else
+ setpgrp(0, 0);
+#endif
+
+#if defined(KERBEROS)
+ /* Pass down rusername and lusername which we have
+ obtained from ticket and authorized by PWC_ACCESS.
+ Note lusername's .rhost should have entry for rusername.
+ */
+ (void) write(p, rusername, strlen(rusername) +1);
+ (void) write(p, lusername, strlen(lusername) +1);
+ /* stuff term info down to login */
+ if( write(p, term, strlen(term)+1) <= 0 ){
+ /*
+ * Problems write failed ...
+ */
+ sprintf(buferror,"Cannot write slave pty %s ",line);
+ fatalperror(f,buferror,errno);
+ }
+#endif /* KERBEROS */
+
+ protocol(f, p);
+ signal(SIGCHLD, SIG_IGN);
+ cleanup();
+}
+
+
+
+char magic[2] = { 0377, 0377 };
+#ifdef TIOCSWINSZ
+#ifndef TIOCPKT_WINDOW
+#define TIOCPKT_WINDOW 0x80
+#endif
+char oobdata[] = {TIOCPKT_WINDOW};
+#else
+char oobdata[] = {0};
+#endif
+
+/*
+ * Handle a "control" request (signaled by magic being present)
+ * in the data stream. For now, we are only willing to handle
+ * window size changes.
+ */
+control(pty, cp, n)
+ int pty;
+ char *cp;
+ int n;
+{
+ struct winsize w;
+ int pgrp;
+
+ if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's')
+ return (0);
+#ifdef TIOCSWINSZ
+ oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */
+ memcpy((char *)&w,cp+4, 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);
+ if (ioctl(pty, TIOCGPGRP, &pgrp) >= 0)
+ (void) killpg(pgrp, SIGWINCH);
+#endif
+ return (4+sizeof (w));
+}
+
+
+
+/*
+ * rlogin "protocol" machine.
+ */
+protocol(f, p)
+ int f, p;
+{
+ char pibuf[1024], fibuf[1024], *pbp, *fbp;
+ register pcc = 0, fcc = 0;
+ int cc;
+ 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).
+ */
+ signal(SIGTTOU, SIG_IGN);
+#ifdef TIOCSWINSZ
+ send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */
+#endif
+ for (;;) {
+ int ibits, obits, ebits;
+
+ ibits = 0;
+ obits = 0;
+ if (fcc)
+ obits |= (1<<p);
+ else
+ ibits |= (1<<f);
+ if (pcc >= 0)
+ if (pcc)
+ obits |= (1<<f);
+ else
+ ibits |= (1<<p);
+ ebits = (1<<p);
+ if (select(16, &ibits, &obits, &ebits, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ fatalperror(f, "select");
+ }
+ if (ibits == 0 && obits == 0 && ebits == 0) {
+ /* shouldn't happen... */
+ sleep(5);
+ continue;
+ }
+#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))
+ if (ebits & (1<<p)) {
+ 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;
+ ibits &= ~(1<<p);
+ }
+ }
+ }
+ if (ibits & (1<<f)) {
+ fcc = des_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)
+ memcpy(cp,
+ cp+n,
+ left);
+ fcc -= n;
+ goto top; /* n^2 */
+ }
+ }
+ }
+ }
+
+ if ((obits & (1<<p)) && fcc > 0) {
+ cc = write(p, fbp, fcc);
+ if (cc > 0) {
+ fcc -= cc;
+ fbp += cc;
+ }
+ }
+
+ if (ibits & (1<<p)) {
+ 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--;
+ else {
+ if (pkcontrol(pibuf[0])) {
+ pibuf[0] |= oobdata[0];
+ send(f, &pibuf[0], 1, MSG_OOB);
+ }
+ pcc = 0;
+ }
+ }
+ if ((obits & (1<<f)) && pcc > 0) {
+ cc = des_write(f, pbp, pcc);
+ if (cc < 0 && errno == EWOULDBLOCK) {
+ /* also shouldn't happen */
+ sleep(5);
+ continue;
+ }
+ if (cc > 0) {
+ pcc -= cc;
+ pbp += cc;
+ }
+ }
+ }
+}
+
+
+
+int cleanup()
+{
+ char *p;
+
+ /*
+ I dont know why P starts with the character '/', but apparently it
+ has to do with the way login set line when the initial entry for this
+ line is made.
+ */
+ p = line + sizeof("/dev/") -1 ;
+ if (!logout(p)) {
+#ifdef SYSV
+ logwtmp(p, "", "", 0, 0);
+#else
+ logwtmp(p, "", "", 0);
+#endif
+ }
+ else
+ syslog(LOG_ERR ,
+ "Cannot delete entry from utmp for %s\n",p);
+
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+#ifndef STREAMS
+ *p = 'p';
+ (void)chmod(line, 0666);
+ (void)chown(line, 0, 0);
+#endif
+ shutdown(netf, 2);
+ exit(1);
+}
+
+
+void fatal(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[512];
+ int out = 1 ; /* Output queue of f */
+
+ buf[0] = '\01'; /* error indicator */
+ (void) sprintf(buf + 1, "%s: %s.\r\n",progname, msg);
+ if ((f == netf) && (pid > 0))
+ (void) des_write(f, buf, strlen(buf));
+ else
+ (void) write(f, buf, strlen(buf));
+ syslog(LOG_ERR,"%s\n",msg);
+ if (pid > 0) {
+ signal(SIGCHLD,SIG_IGN);
+ kill(pid,SIGKILL);
+#ifdef TIOCFLUSH
+ (void) ioctl(f, TIOCFLUSH, (char *)&out);
+#else
+ (void) ioctl(f, TCFLSH, out);
+#endif
+ cleanup();
+ }
+ exit(1);
+}
+
+
+
+void fatalperror(f, msg)
+ int f;
+ char *msg;
+{
+ char buf[512];
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+
+ if ((unsigned)errno < sys_nerr)
+ (void) sprintf(buf, "%s: %s", msg, sys_errlist[errno]);
+ else
+ (void) sprintf(buf, "%s: Error %d", msg, errno);
+ fatal(f, buf);
+}
+
+#ifdef KERBEROS
+
+
+do_krb_login(host)
+ char *host;
+{
+ int rc;
+ krb5_error_code status;
+ struct sockaddr_in peersin;
+ krb5_address peeraddr;
+ struct passwd *pwd;
+ krb5_principal server;
+ char srv_name[100];
+ char def_host[100];
+ krb5_data inbuf;
+
+ if (getuid()) {
+ exit(1);
+ }
+
+ /* we want mutual authentication */
+#ifdef unicos61
+#define SIZEOF_INADDR SIZEOF_in_addr
+#else
+#define SIZEOF_INADDR sizeof(struct in_addr)
+#endif
+
+ rc = sizeof(peersin);
+ if (getpeername(netf, (struct sockaddr *)&peersin, &rc)) {
+ syslog(LOG_ERR, "get peer name failed %d", netf);
+ exit(1);
+ }
+
+ peeraddr.addrtype = peersin.sin_family;
+ peeraddr.length = SIZEOF_INADDR;
+ peeraddr.contents = (krb5_octet *)&peersin.sin_addr;
+
+ strcpy(srv_name, "host/");
+ gethostname(def_host, 100);
+ strcat(srv_name, def_host);
+ if (status = krb5_parse_name(srv_name, &server)) {
+ syslog(LOG_ERR, "parse server name %s: %s", "host",
+ error_message(status));
+ exit(1);
+ }
+ krb5_princ_type(server) = KRB5_NT_SRV_HST;
+
+ if (status = krb5_recvauth(&netf,
+ "KCMDV0.1",
+ server, /* no match on server
+ incase we have are
+ serving multiple realms*/
+ &peeraddr, /* We do want to match this
+ against caddrs in the
+ ticket. */
+ 0, /* use srv5tab */
+ 0, /* no keyproc */
+ 0, /* no keyproc arg */
+ 0, /* no rc_type */
+ 0, /* no seq number */
+ &client, /* return client */
+ &ticket, /* return ticket */
+ &kdata /* return authenticator */
+ )) {
+ syslog(LOG_ERR,
+ "Kerberos authentication failed from %s: %s\n",
+ host,error_message(status));
+
+ /* Dont exit out for klogin, but
+ grab locuser, terminal, and remuser.
+ */
+
+ /* These two reads will be used in the next release to obtain
+ a forwarded TGT and related info. */
+ if (status = krb5_read_message((krb5_pointer)&netf, &inbuf))
+ fatal(netf, "Error reading message");
+ if (inbuf.length)
+ fatal(netf, "Forwarding is not yet supported");
+ if (status = krb5_read_message((krb5_pointer)&netf, &inbuf))
+ fatal(netf, "Error reading message");
+ if (inbuf.length)
+ fatal(netf, "Forwarding is not yet supported");
+
+ getstr(lusername, sizeof(lusername), "locuser");
+ getstr(term, sizeof(term), "Terminal type");
+ rusername = malloc(sizeof (lusername) + 1);
+ getstr(rusername, sizeof(lusername), "remuser");
+
+ failed_auth = 1;
+ if (ticket)
+ krb5_free_ticket(ticket);
+ return;
+ }
+
+ /* Setup up eblock if encrypted login session */
+ /* otherwise zero out session key */
+ if (do_encrypt) {
+ krb5_use_keytype(&eblock,
+ ticket->enc_part2->session->keytype);
+ if (status = krb5_process_key(&eblock,
+ ticket->enc_part2->session))
+ fatal(netf, "Permission denied");
+ }
+
+ /* These two reads will be used in the next release to obtain
+ a forwarded TGT and related info. */
+ if (status = krb5_read_message((krb5_pointer)&netf, &inbuf))
+ fatal(netf, "Error reading message");
+ if (inbuf.length)
+ fatal(netf, "Forwarding is not yet supported");
+ if (status = krb5_read_message((krb5_pointer)&netf, &inbuf))
+ fatal(netf, "Error reading message");
+ if (inbuf.length)
+ fatal(netf, "Forwarding is not yet supported");
+
+ getstr(lusername, sizeof(lusername), "locuser");
+ getstr(term, sizeof(term), "Terminal type");
+ rusername = malloc(sizeof (lusername) + 1);
+ getstr(rusername, sizeof(lusername), "remuser");
+
+ /* OK we have authenticated this user - now check authorization. */
+ /* We must do this here since we want the same functionality as */
+ /* the MIT version without having to provide the login.krb program.*/
+
+ /* The Kerberos authenticated programs must use krb5_kuserok */
+
+ krb5_unparse_name(kdata->client,&krusername);
+
+ if (must_pass_k5 || must_pass_one) {
+ /* krb5_kuserok returns 1 if OK */
+ rc = !(krb5_kuserok(kdata->client,lusername));
+
+ if (rc){
+ syslog(LOG_ERR,
+ "Principal %s (%s@%s) logging in as %s failed krb5_kuserok.\n",
+ krusername, rusername, host, lusername);
+ if (must_pass_k5)
+ fatal(netf, "Permission denied");
+ failed_k5 = 1;
+ if (ticket)
+ krb5_free_ticket(ticket);
+ }
+ }
+
+ /* The kerberos authenticated request must pass ruserok also
+ if asked for. */
+
+ if (must_pass_rhosts || (failed_k5 && must_pass_one)) {
+ pwd = (struct passwd *) getpwnam(lusername);
+ if ((pwd == (struct passwd *) 0) ||
+ (ruserok(rhost_name, pwd->pw_uid == 0, rusername, lusername))) {
+ failed_rhosts = 1;
+ if (ticket)
+ krb5_free_ticket(ticket);
+
+ if (pwd == (struct passwd *) 0)
+ syslog(LOG_ERR,
+ "Principal %s (%s@%s) logging in as %s has no account.\n",
+ krusername, rusername, rhost_name, lusername);
+ else
+ syslog(LOG_ERR,
+ "Principal %s (%s@%s) logging in as %s failed ruserok.\n",
+ krusername, rusername, rhost_name, lusername);
+
+ if (must_pass_rhosts)
+ fatal(netf, "Permission denied");
+ }
+ }
+
+ return;
+}
+
+
+
+getstr(buf, cnt, err)
+ char *buf;
+ int cnt;
+ char *err;
+{
+
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1) {
+ exit(1);
+ }
+ if (--cnt < 0) {
+ printf("%s too long\r\n", err);
+ exit(1);
+ }
+ *buf++ = c;
+ } while (c != 0);
+}
+
+
+
+char storage[2*BUFSIZ]; /* storage for the decryption */
+int nstored = 0;
+char *store_ptr = storage;
+
+
+des_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ int len;
+{
+ int nreturned = 0;
+ long net_len,rd_len;
+ int cc,retry;
+
+ if (!do_encrypt)
+ return(read(fd, buf, len));
+
+ if (nstored >= len) {
+ memcpy(buf, store_ptr, len);
+ store_ptr += len;
+ nstored -= len;
+ return(len);
+ } else if (nstored) {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ buf += nstored;
+ len -= nstored;
+ nstored = 0;
+ }
+
+#ifdef BITS64
+ rd_len = 0;
+ if ((cc = krb5_net_read(fd, (char *)&rd_len + 4, 4)) != 4) {
+#else
+ if ((cc = krb5_net_read(fd, (char *)&rd_len, sizeof(rd_len))) !=
+ sizeof(rd_len)) {
+#endif
+ if ((cc < 0) && (errno == EWOULDBLOCK)) return(cc);
+ /* XXX can't read enough, pipe
+ must have closed */
+ return(0);
+ }
+ rd_len = ntohl(rd_len);
+ net_len = krb5_encrypt_size(rd_len,eblock.crypto_entry);
+ if (net_len < 0 || net_len > sizeof(des_inbuf)) {
+ /* XXX preposterous length, probably out of sync.
+ act as if pipe closed */
+ syslog(LOG_ERR,"Read size problem.");
+ return(0);
+ }
+ retry = 0;
+ datard:
+ if ((cc = krb5_net_read(fd, desinbuf.data, net_len)) != net_len) {
+ /* XXX can't read enough, pipe
+ must have closed */
+ if ((cc < 0) && (errno == EWOULDBLOCK)) {
+ retry++;
+ sleep(1);
+ if (retry > MAXRETRIES){
+ syslog(LOG_ERR,
+ "des_read retry count exceeded %d\n",
+ retry);
+ return(0);
+ }
+ goto datard;
+ }
+ syslog(LOG_ERR,
+ "Read data received %d != expected %d.",
+ cc, net_len);
+ return(0);
+ }
+ /* decrypt info */
+ if ((krb5_decrypt(desinbuf.data,
+ (krb5_pointer) storage,
+ net_len,
+ &eblock, 0))) {
+ syslog(LOG_ERR,"Read decrypt problem.");
+ return(0);
+ }
+ store_ptr = storage;
+ nstored = rd_len;
+ if (nstored > len) {
+ memcpy(buf, store_ptr, len);
+ nreturned += len;
+ store_ptr += len;
+ nstored -= len;
+ } else {
+ memcpy(buf, store_ptr, nstored);
+ nreturned += nstored;
+ nstored = 0;
+ }
+ return(nreturned);
+}
+
+
+
+des_write(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ long net_len;
+
+ if (!do_encrypt)
+ return(write(fd, buf, len));
+
+
+ desoutbuf.length = krb5_encrypt_size(len,eblock.crypto_entry);
+ if (desoutbuf.length > sizeof(des_outbuf)){
+ syslog(LOG_ERR,"Write size problem.");
+ return(-1);
+ }
+ if ((krb5_encrypt((krb5_pointer)buf,
+ desoutbuf.data,
+ len,
+ &eblock,
+ 0))){
+ syslog(LOG_ERR,"Write encrypt problem.");
+ return(-1);
+ }
+
+ net_len = htonl(len);
+#ifdef BITS64
+ (void) write(fd,(char *)&net_len + 4, 4);
+#else
+ (void) write(fd, &net_len, sizeof(net_len));
+#endif
+ if (write(fd, desoutbuf.data,desoutbuf.length) != desoutbuf.length){
+ syslog(LOG_ERR,"Could not write out all data.");
+ return(-1);
+ }
+ else return(len);
+}
+
+#endif /* KERBEROS */
+
+
+
+getpty(fd,slave)
+ int *fd;
+ char *slave;
+{
+ char c;
+ int i,ptynum;
+ struct stat stb;
+#ifdef STREAMS
+#ifdef sysvimp
+ *fd = open("/dev/pty", O_RDWR|O_NDELAY);
+#else
+ *fd = open("/dev/ptc", O_RDWR|O_NDELAY);
+#endif
+ if (*fd >= 0) {
+ if (fstat(*fd, &stb) < 0) {
+ close(*fd);
+ return 1;
+ }
+ ptynum = (int)(stb.st_rdev&0xFF);
+#ifdef sysvimp
+ sprintf(slave, "/dev/ttyp%x", ptynum);
+#else
+ sprintf(slave, "/dev/ttyq%x", ptynum);
+#endif
+ }
+ return (0);
+
+#else /* NOT STREAMS */
+ for (c = 'p'; c <= 's'; c++) {
+ sprintf(slave,"/dev/ptyXX");
+ slave[strlen("/dev/pty")] = c;
+ slave[strlen("/dev/ptyp")] = '0';
+ if (stat(slave, &stb) < 0)
+ break;
+ for (i = 0; i < 16; i++) {
+ slave[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
+ *fd = open(slave, O_RDWR);
+ if (*fd > 0)
+ goto gotpty;
+ }
+ }
+ return(1);
+ gotpty:
+ slave[strlen("/dev/")] = 't';
+ return(0);
+#endif /* STREAMS */
+}
+
+
+
+void usage()
+{
+#ifdef KERBEROS
+ syslog(LOG_ERR,
+ "usage: klogind [-rRkKxpP] or [r/R][k/K][x/e][p/P]logind");
+#else
+ syslog(LOG_ERR, "usage: rlogind [-rRpP] or [r/R][p/P]logind");
+#endif
+}
+
+
+
+#ifdef KERBEROS
+int princ_maps_to_lname(principal, luser)
+ krb5_principal principal;
+ char *luser;
+{
+ char kuser[10];
+ if (!(krb5_aname_to_localname(principal,
+ sizeof(kuser), kuser))
+ && (strcmp(kuser, luser) == 0)) {
+ return 1;
+ }
+ return 0;
+}
+
+int default_realm(principal)
+ krb5_principal principal;
+{
+ char *def_realm;
+ int realm_length;
+ int retval;
+
+ realm_length = krb5_princ_realm(principal)->length;
+
+ if (retval = krb5_get_default_realm(&def_realm)) {
+ return 0;
+ }
+
+ if ((realm_length != strlen(def_realm)) ||
+ (memcmp(def_realm, krb5_princ_realm(principal)->data, realm_length))) {
+ free(def_realm);
+ return 0;
+ }
+ free(def_realm);
+ return 1;
+}
+#endif
diff --git a/src/appl/bsd/krsh.c b/src/appl/bsd/krsh.c
new file mode 100644
index 0000000000..57df62ad57
--- /dev/null
+++ b/src/appl/bsd/krsh.c
@@ -0,0 +1,416 @@
+/*
+ * $Source$
+ * $Header$
+ */
+
+#ifndef lint
+static char *rcsid_rsh_c =
+ "$Header$";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rsh.c 5.7 (Berkeley) 9/20/88";
+#endif /* not lint */
+
+#define KERBEROS
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <netdb.h>
+
+#ifdef KERBEROS
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/crc-32.h>
+#include <krb5/mit-des.h>
+#endif /* KERBEROS */
+
+ /*
+ * rsh - remote shell
+ */
+
+int error();
+
+#ifndef convex
+struct passwd *getpwuid();
+#endif
+
+int errno;
+int options;
+int rfd2;
+int nflag;
+krb5_sigtype sendsig();
+
+#ifdef KERBEROS
+char *krb_realm = (char *)0;
+ void try_normal();
+#define UCB_RSH "/usr/ucb/rsh"
+#define RLOGIN_PROGRAM "/nfs/kerberos/bin/sun3/rlogin"
+#else /* KERBEROS */
+#define RLOGIN_PROGRAM "/usr/ucb/rlogin"
+#endif /* KERBEROS */
+
+#define mask(s) (1 << ((s) - 1))
+
+ main(argc, argv0)
+ int argc;
+ char **argv0;
+{
+ int rem, pid;
+ char *host, *cp, **ap, buf[BUFSIZ], *args, **argv = argv0, *user = 0;
+ register int cc;
+ int asrsh = 0;
+ struct passwd *pwd;
+ int readfrom, ready;
+ int one = 1;
+ struct servent *sp;
+ int omask;
+#ifdef KERBEROS
+ krb5_flags authopts;
+ krb5_error_code status;
+#endif /* KERBEROS */
+
+ if ( argc < 2 ) goto usage;
+ host = argv[1];
+ argc -= 2;
+ argv +=2;
+ if (!strcmp(host, "rsh")) {
+ host = *argv++, --argc;
+ asrsh = 1;
+ }
+ another:
+ if (argc > 0 && !strcmp(*argv, "-l")) {
+ argv++, argc--;
+ if (argc > 0)
+ user = *argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-n")) {
+ argv++, argc--;
+ nflag++;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-d")) {
+ argv++, argc--;
+ options |= SO_DEBUG;
+ goto another;
+ }
+#ifdef KERBEROS
+ if (argc > 0 && !strcmp(*argv, "-k")) {
+ argv++, argc--;
+ if (argc == 0) {
+ fprintf(stderr, "rsh(kerberos): -k flag must have a realm after it.\n");
+ exit (1);
+ }
+ if(!(krb_realm = (char *)malloc(strlen(*argv) + 1))){
+ fprintf(stderr, "rsh(kerberos): Cannot malloc.\n");
+ exit(1);
+ }
+ strcpy(krb_realm, *argv);
+ argv++, argc--;
+ goto another;
+ }
+ /*
+ * Ignore -x from kerberos rlogin
+ */
+ if (argc > 0 && !strncmp(*argv, "-x", 2)) {
+ argv++, argc--;
+ goto another;
+ }
+
+#endif /* KERBEROS */
+ /*
+ * Ignore the -L, -w, -e and -8 flags to allow aliases with rlogin
+ * to work
+ *
+ * There must be a better way to do this! -jmb
+ */
+ if (argc > 0 && !strncmp(*argv, "-L", 2)) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strncmp(*argv, "-w", 2)) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strncmp(*argv, "-e", 2)) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strncmp(*argv, "-8", 2)) {
+ argv++, argc--;
+ goto another;
+ }
+#ifdef ATHENA
+ /* additional Athena flags to be ignored */
+ if (argc > 0 && !strcmp(*argv, "-noflow")) { /* No local flow control option for rlogin */
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-7")) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-c")) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-a")) {
+ argv++, argc--;
+ goto another;
+ }
+ if (argc > 0 && !strcmp(*argv, "-n")) {
+ argv++, argc--;
+ goto another;
+ }
+ /*
+ ** Also ignore -t ttytype
+ */
+ if (argc > 0 && !strcmp(*argv, "-t")) {
+ argv++; argv++; argc--; argc--;
+ goto another;
+ }
+#endif /* ATHENA */
+ if (host == 0)
+ goto usage;
+ if (argv[0] == 0) {
+ if (asrsh)
+ *argv0 = "rlogin";
+ execv(RLOGIN_PROGRAM, argv0);
+ perror(RLOGIN_PROGRAM);
+ exit(1);
+ }
+ pwd = getpwuid(getuid());
+ if (pwd == 0) {
+ fprintf(stderr, "who are you?\n");
+ exit(1);
+ }
+ cc = 0;
+ for (ap = argv; *ap; ap++)
+ cc += strlen(*ap) + 1;
+ cp = args = malloc(cc);
+ for (ap = argv; *ap; ap++) {
+ (void) strcpy(cp, *ap);
+ while (*cp)
+ cp++;
+ if (ap[1])
+ *cp++ = ' ';
+ }
+#ifdef KERBEROS
+ sp = getservbyname("kshell", "tcp");
+#else
+ sp = getservbyname("shell", "tcp");
+#endif /* KERBEROS */
+ if (sp == 0) {
+#ifdef KERBEROS
+ fprintf(stderr, "rsh: kshell/tcp: unknown service\n");
+ try_normal(argv0);
+#else
+ fprintf(stderr, "rsh: shell/tcp: unknown service\n");
+#endif /* KERBEROS */
+ exit(1);
+ }
+#ifdef KERBEROS
+ krb5_init_ets();
+ authopts = AP_OPTS_MUTUAL_REQUIRED;
+ status = kcmd(&rem, &host, sp->s_port,
+ pwd->pw_name,
+ user ? user : pwd->pw_name,
+ args, &rfd2, "host", krb_realm,
+ 0, /* No need for returned credentials */
+ 0, /* No need for sequence number */
+ 0, /* No need for server seq # */
+ (struct sockaddr_in *) 0,
+ (struct sockaddr_in *) 0,
+ authopts);
+ if (status) {
+ fprintf(stderr,
+ "%s: kcmd to host %s failed - %s\n",argv[0], host,
+ error_message(status));
+ try_normal(argv0);
+ }
+#else /* !KERBEROS */
+ rem = rcmd(&host, sp->s_port, pwd->pw_name,
+ user ? user : pwd->pw_name, args, &rfd2);
+ if (rem < 0)
+ exit(1);
+#endif /* KERBEROS */
+ if (rfd2 < 0) {
+ fprintf(stderr, "rsh: can't establish stderr\n");
+ exit(2);
+ }
+ if (options & SO_DEBUG) {
+ if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof (one)) < 0)
+ perror("setsockopt (stdin)");
+ if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, sizeof (one)) < 0)
+ perror("setsockopt (stderr)");
+ }
+ (void) setuid(getuid());
+#ifdef sgi
+ omask = sigignore(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+#else
+ omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+#endif
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, sendsig);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ signal(SIGQUIT, sendsig);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, sendsig);
+ if (nflag == 0) {
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+ }
+ ioctl(rfd2, FIONBIO, &one);
+ ioctl(rem, FIONBIO, &one);
+ if (nflag == 0 && pid == 0) {
+ char *bp; int rembits, wc;
+ (void) close(rfd2);
+ reread:
+ errno = 0;
+ cc = read(0, buf, sizeof buf);
+ if (cc <= 0)
+ goto done;
+ bp = buf;
+ rewrite:
+ rembits = 1<<rem;
+ if (select(16, 0, &rembits, 0, 0) < 0) {
+ if (errno != EINTR) {
+ perror("select");
+ exit(1);
+ }
+ goto rewrite;
+ }
+ if ((rembits & (1<<rem)) == 0)
+ goto rewrite;
+ wc = write(rem, bp, cc);
+ if (wc < 0) {
+ if (errno == EWOULDBLOCK)
+ goto rewrite;
+ goto done;
+ }
+ cc -= wc; bp += wc;
+ if (cc == 0)
+ goto reread;
+ goto rewrite;
+ done:
+ (void) shutdown(rem, 1);
+ exit(0);
+ }
+#ifndef sgi
+ sigsetmask(omask);
+#endif
+ readfrom = (1<<rfd2) | (1<<rem);
+ do {
+ ready = readfrom;
+ if (select(16, &ready, 0, 0, 0) < 0) {
+ if (errno != EINTR) {
+ perror("select");
+ exit(1);
+ }
+ continue;
+ }
+ if (ready & (1<<rfd2)) {
+ errno = 0;
+ cc = read(rfd2, buf, sizeof buf);
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ readfrom &= ~(1<<rfd2);
+ } else
+ (void) write(2, buf, cc);
+ }
+ if (ready & (1<<rem)) {
+ errno = 0;
+ cc = read(rem, buf, sizeof buf);
+ if (cc <= 0) {
+ if (errno != EWOULDBLOCK)
+ readfrom &= ~(1<<rem);
+ } else
+ (void) write(1, buf, cc);
+ }
+ } while (readfrom);
+ if (nflag == 0)
+ (void) kill(pid, SIGKILL);
+ exit(0);
+ usage:
+ fprintf(stderr,
+ "usage: rsh host [ -l login ] [ -n ] command\n");
+ exit(1);
+}
+
+
+
+krb5_sigtype sendsig(signo)
+ char signo;
+{
+
+ (void) write(rfd2, &signo, 1);
+}
+
+
+
+#ifdef KERBEROS
+void try_normal(argv)
+ char **argv;
+{
+ char *host;
+
+ /*
+ * if we were invoked as 'rsh host mumble', strip off the rsh
+ * from arglist.
+ *
+ * We always want to call the Berkeley rsh as 'host mumble'
+ */
+
+ host = rindex(argv[0], '/');
+ if (host)
+ host++;
+ else
+ host = argv[0];
+
+ if (!strcmp(host, "rsh"))
+ argv++;
+
+ fprintf(stderr,"trying normal rsh (%s)\n",
+ UCB_RSH);
+ fflush(stderr);
+ execv(UCB_RSH, argv);
+ perror("exec");
+ exit(1);
+}
+#endif /* KERBEROS */
diff --git a/src/appl/bsd/krshd.M b/src/appl/bsd/krshd.M
new file mode 100644
index 0000000000..e02480dc13
--- /dev/null
+++ b/src/appl/bsd/krshd.M
@@ -0,0 +1,164 @@
+.\" Copyright (c) 1983 Regents of the University of California.
+.\" All rights reserved. The Berkeley software License Agreement
+.\" specifies the terms and conditions for redistribution.
+.\"
+.\" @(#)rshd.8c 6.3 (Berkeley) 5/24/86
+.\"
+.TH RSHD 8C "May 24, 1986"
+.UC 5
+.SH NAME
+rshd \- remote shell server
+.SH SYNOPSIS
+.B /usr/etc/rshd
+.SH DESCRIPTION
+.I Rshd
+is the server for the
+.IR rcmd (3X)
+routine and, consequently, for the
+.IR rsh (1C)
+program. The server provides remote execution facilities
+with authentication based on privileged port numbers from trusted hosts.
+.PP
+.I Rshd
+listens for service requests at the port indicated in
+the ``cmd'' service specification; see
+.IR services (5).
+When a service request is received the following protocol
+is initiated:
+.IP 1)
+The server checks the client's source port.
+If the port is not in the range 0-1023, the server
+aborts the connection.
+.IP 2)
+The server reads characters from the socket up
+to a null (`\e0') byte. The resultant string is
+interpreted as an ASCII number, base 10.
+.IP 3)
+If the number received in step 1 is non-zero,
+it is interpreted as the port number of a secondary
+stream to be used for the
+.BR stderr .
+A second connection is then created to the specified
+port on the client's machine. The source port of this
+second connection is also in the range 0-1023.
+.IP 4)
+The server checks the client's source address
+and requests the corresponding host name (see
+.IR gethostbyaddr (3N),
+.IR hosts (5)
+and
+.IR named (8)).
+If the hostname cannot be determined,
+the dot-notation representation of the host address is used.
+.IP 5)
+A null terminated user name of at most 16 characters
+is retrieved on the initial socket. This user name
+is interpreted as the user identity on the
+.BR client 's
+machine.
+.IP 6)
+A null terminated user name of at most 16 characters
+is retrieved on the initial socket. This user name
+is interpreted as a user identity to use on the
+.BR server 's
+machine.
+.IP 7)
+A null terminated command to be passed to a
+shell is retrieved on the initial socket. The length of
+the command is limited by the upper bound on the size of
+the system's argument list.
+.IP 8)
+.I Rshd
+then validates the user according to the following steps.
+The local (server-end) user name is looked up in the password file
+and a
+.I chdir
+is performed to the user's home directory. If either
+the lookup or
+.I chdir
+fail, the connection is terminated.
+If the user is not the super-user, (user id 0), the file
+.I /etc/hosts.equiv
+is consulted for a list of hosts considered ``equivalent''.
+If the client's host name is present in this file, the
+authentication is considered successful. If the lookup
+fails, or the user is the super-user, then the file
+.I .rhosts
+in the home directory of the remote user is checked for
+the machine name and identity of the user on the client's
+machine. If this lookup fails, the connection is terminated.
+.IP 9)
+A null byte is returned on the initial socket
+and the command line is passed to the normal login
+shell of the user. The
+shell inherits the network connections established
+by
+.IR rshd .
+.SH DIAGNOSTICS
+Except for the last one listed below,
+all diagnostic messages
+are returned on the initial socket,
+after which any network connections are closed.
+An error is indicated by a leading byte with a value of
+1 (0 is returned in step 9 above upon successful completion
+of all the steps prior to the execution of the login shell).
+.PP
+.B ``locuser too long''
+.br
+The name of the user on the client's machine is
+longer than 16 characters.
+.PP
+.B ``remuser too long''
+.br
+The name of the user on the remote machine is
+longer than 16 characters.
+.PP
+.B ``command too long ''
+.br
+The command line passed exceeds the size of the argument
+list (as configured into the system).
+.PP
+.B ``Login incorrect.''
+.br
+No password file entry for the user name existed.
+.PP
+.B ``No remote directory.''
+.br
+The
+.I chdir
+command to the home directory failed.
+.PP
+.B ``Permission denied.''
+.br
+The authentication procedure described above failed.
+.PP
+.B ``Can't make pipe.''
+.br
+The pipe needed for the
+.BR stderr ,
+wasn't created.
+.PP
+.B ``Try again.''
+.br
+A
+.I fork
+by the server failed.
+.PP
+.B ``<shellname>: ...''
+.br
+The user's login shell could not be started. This message is returned
+on the connection associated with the
+.BR stderr ,
+and is not preceded by a flag byte.
+.SH SEE ALSO
+rsh(1C),
+rcmd(3X)
+.SH BUGS
+The authentication procedure used here assumes the integrity
+of each client machine and the connecting medium. This is
+insecure, but is useful in an ``open'' environment.
+.PP
+A facility to allow all data exchanges to be encrypted should be
+present.
+.PP
+A more extensible protocol should be used.
diff --git a/src/appl/bsd/krshd.c b/src/appl/bsd/krshd.c
new file mode 100644
index 0000000000..0654d54fb9
--- /dev/null
+++ b/src/appl/bsd/krshd.c
@@ -0,0 +1,1340 @@
+/*
+ * $Author$
+ * $Header$
+ */
+
+#ifndef lint
+static char rcsid_rshd_c[] =
+ "$Header$";
+#endif /* lint */
+
+/*
+ * Copyright (c) 1983 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1983 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)rshd.c 5.12 (Berkeley) 9/12/88";
+#endif /* not lint */
+
+#define KERBEROS
+
+ /*
+ * remote shell server:
+ * remuser\0
+ * locuser\0
+ * command\0
+ * data
+ */
+
+ /*
+ * This is the rshell daemon. The very basic protocol for checking
+ * authentication and authorization is:
+ * 1) Check authentication.
+ * 2) Check authorization via the access-control files:
+ * ~/.k5login (using krb5_kuserok) and/or
+ * ~/.rhosts (using ruserok).
+ * Execute command if configured authoriztion checks pass, else deny
+ * permission.
+ *
+ * The configuration is done either by command-line arguments passed by inetd,
+ * or by the name of the daemon. If command-line arguments are present, they
+ * take priority. The options are:
+ * -k and -K means check .k5login (using krb5_kuserok).
+ * -r and -R means check .rhosts (using ruserok).
+ * The difference between upper and lower case is as follows:
+ * If lower case -r or -k, then as long as one of krb5_kuserok or ruserok
+ * passes, allow access. If both fail, no access. The program does not fall
+ * back on password verification.
+ * If uppercase -R or -K, then those checks must be passed, regardless of
+ * other checks, else no access.
+ *
+ * If no command-line arguments are present, then the presence of the
+ * letters kKrR in the program-name before "shd" determine the
+ * behaviour of the program exactly as with the command-line arguments.
+ */
+
+ /* DEFINES:
+ * KERBEROS - Define this if application is to be kerberised.
+ * LOG_ALL_LOGINS - Define this if you want to log all logins.
+ * LOG_OTHER_USERS - Define this if you want to log all principals that do
+ * not map onto the local user.
+ * LOG_REMOTE_REALM - Define this if you want to log all principals from
+ * remote realms.
+ * LOG_CMD - Define this if you want to log not only the user but also the
+ * command executed. This only decides the type of information
+ * logged. Whether or not to log is still decided by the above
+ * three DEFINES.
+ * Note: Root account access is always logged.
+ */
+
+#define LOG_REMOTE_REALM
+#define LOG_CMD
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+
+#if defined(CRAY) || defined(sysvimp) || defined(aux20)
+#include <sys/types.h>
+#ifndef _TYPES_
+#define _TYPES_
+#endif
+#ifndef F_OK
+#define F_OK 0
+#endif
+#endif
+
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+
+#ifndef SYSV
+#include <arpa/inet.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <pwd.h>
+
+#ifdef sun
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+#endif
+
+#include <signal.h>
+#include <netdb.h>
+
+#ifdef CRAY
+#ifndef NO_UDB
+#include <udb.h>
+#endif /* !NO_UDB */
+#include <sys/category.h>
+#include <netinet/ip.h>
+#include <sys/tfm.h>
+#include <sys/nal.h>
+#include <sys/secparm.h>
+#include <sys/usrv.h>
+#include <sys/utsname.h>
+#include <sys/sysv.h>
+#include <sys/slrec.h>
+#include <sys/unistd.h>
+#include <path.h>
+#endif /* CRAY */
+
+#include <syslog.h>
+
+#ifdef KERBEROS
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/crc-32.h>
+#include <krb5/mit-des.h>
+
+#include <com_err.h>
+
+#define ARGSTR "rRkK?"
+#else /* !KERBEROS */
+#define ARGSTR "rR?"
+
+char *strsave();
+#endif /* KERBEROS */
+
+int must_pass_rhosts = 0, must_pass_krb = 0, must_pass_one = 0;
+int failed_krb = 0;
+char *progname;
+
+#define MAX_PROG_NAME 10
+
+#ifdef CRAY
+int secflag;
+extern
+#endif /* CRAY */
+int errno;
+
+char *index(), *rindex(), *strncat();
+/*VARARGS1*/
+int error();
+
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+#if defined(BSD) && BSD >= 43
+ struct linger linger;
+#endif
+ int on = 1, fromlen;
+ struct sockaddr_in from;
+ extern int opterr, optind;
+ extern char *optarg;
+ char *options, ch;
+ int i;
+
+#ifdef CRAY
+ secflag = sysconf(_SC_CRAY_SECURE_SYS);
+#endif
+
+ progname = *argv;
+
+#ifndef LOG_ODELAY /* 4.2 syslog */
+ openlog(progname, LOG_PID);
+#else
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+ openlog(progname, LOG_PID | LOG_ODELAY, LOG_DAEMON);
+#endif /* 4.2 syslog */
+
+ if (argc == 1) { /* Get parameters from program name. */
+ if (strlen(progname) > MAX_PROG_NAME) {
+ usage();
+ exit(1);
+ }
+ options = (char *) malloc(MAX_PROG_NAME+1);
+ options[0] = '\0';
+ for (i = 0; (progname[i] != '\0') && (i < MAX_PROG_NAME); i++)
+ if (!strcmp(progname+i, "shd")) {
+ strcpy(options, "-");
+ strncat(options, progname, i);
+ argc = 2;
+ argv[1] = options;
+ argv[2] = NULL;
+ break;
+ }
+ if (options[0] == '\0') {
+ usage();
+ exit(1);
+ }
+ }
+
+ /* Analyse parameters. */
+ opterr = 0;
+ while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ switch (ch) {
+ case 'r':
+ must_pass_one = 1; /* If just 'r', any one check must succeed */
+ break;
+ case 'R': /* If 'R', must pass .rhosts check*/
+ must_pass_rhosts = 1;
+ if (must_pass_one)
+ must_pass_one = 0;
+ break;
+#ifdef KERBEROS
+ case 'k':
+ must_pass_one = 1; /* If just 'k', any one check must succeed */
+ break;
+ case 'K': /* If 'K', must pass .k5login check*/
+ must_pass_krb = 1;
+ if (must_pass_one)
+ must_pass_one = 0;
+ break;
+#endif
+ case '?':
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ fromlen = sizeof (from);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("getpeername");
+ _exit(1);
+ }
+ if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
+ sizeof (on)) < 0)
+ syslog(LOG_WARNING,
+ "setsockopt (SO_KEEPALIVE): %m");
+#if defined(BSD) && BSD >= 43
+ 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");
+#endif
+ doit(dup(0), &from);
+}
+
+#ifdef CRAY
+char username[32] = "LOGNAME=";
+#include <tmpdir.h>
+char tmpdir[64] = "TMPDIR=";
+#else
+char username[20] = "USER=";
+#endif
+
+char homedir[64] = "HOME=";
+char shell[64] = "SHELL=";
+char term[64] = "TERM=network";
+
+#ifdef KERBEROS
+char *envinit[] =
+#ifdef CRAY
+{homedir, shell, PATH, username, "TZ=GMT0", tmpdir, term, 0};
+#define TZENV 4
+#define TMPDIRENV 5
+char *getenv();
+extern
+#else
+{homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin:/usr/bin/kerberos",
+ username, term, 0};
+#endif /* CRAY */
+#else /* !KERBEROS */
+char *envinit[] =
+#ifdef CRAY
+{homedir, shell, PATH, username, "TZ=GMT0", tmpdir, term, 0};
+#define TZENV 4
+#define TMPDIRENV 5
+char *getenv();
+extern
+#else
+{homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin:/usr/bin/kerberos",
+ username, term, 0};
+#endif /* CRAY */
+#endif /* KERBEROS */
+
+char **environ;
+char ttyn[12]; /* Line string for wtmp entries */
+
+#if defined (alliant) /* Alliant compiler complains of too many
+ local variables*/
+char cmdbuf[NCARGS+1];
+#endif
+
+#ifdef CRAY
+#define SIZEOF_INADDR SIZEOF_in_addr
+int maxlogs;
+#else
+#define SIZEOF_INADDR sizeof(struct in_addr)
+#endif
+
+#define NMAX 16
+
+int pid;
+char locuser[NMAX+1];
+
+
+
+doit(f, fromp)
+ int f;
+ struct sockaddr_in *fromp;
+{
+#if defined (alliant)
+ char *cp;
+#else
+ char cmdbuf[NCARGS+1], *cp;
+#endif
+
+#ifdef KERBEROS
+ char *kremuser;
+ char *remuser;
+ krb5_authenticator *kdata;
+ krb5_ticket *ticket = 0;
+ krb5_address peeraddr;
+ krb5_principal client;
+ krb5_error_code status;
+#else
+ char remuser[NMAX +1];
+#endif
+ int tmpint;
+
+ int ioctlval, cnt;
+ char *salt, *ttynm, *tty;
+ register char *p;
+ char *crypt();
+
+#ifndef CRAY
+ struct passwd *pwd;
+#else
+ struct passwd *pwd;
+#ifndef NO_UDB
+ struct udb *ue;
+ struct udb ue_static;
+ extern struct udb *getudbnam();
+#endif
+ extern struct passwd *getpwnam(), *getpwuid();
+ static int jid;
+ int error();
+ int paddr;
+ struct nal nal;
+ int nal_error;
+ struct usrv usrv;
+ struct sysv sysv;
+ char *makejtmp(), *jtmpnam = 0;
+ int packet_level; /* Packet classification level */
+ long packet_compart; /* Packet compartments */
+#endif /* CRAY */
+
+ int s;
+ struct hostent *hp;
+ char *hostname;
+ short port;
+ int pv[2], cc;
+ long ready, readfrom;
+ char buf[BUFSIZ], sig;
+ int one = 1;
+ krb5_sigtype cleanup();
+ int fd;
+
+ krb5_principal server;
+ char srv_name[100];
+ char def_host[100];
+ krb5_data inbuf;
+#ifdef IP_TOS
+ struct tosent *tp;
+ if ((tp = gettosbyname("interactive", "tcp")) &&
+ (setsockopt(f, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
+#ifdef TOS_WARN
+ syslog(LOG_NOTICE, "setsockopt (IP_TOS): %m");
+#else
+ ; /* silently ignore TOS errors in 6E */
+#endif
+#endif /* IP_TOS */
+
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+#ifdef DEBUG
+ { int t = open("/dev/tty", 2);
+ if (t >= 0) {
+ ioctl(t, TIOCNOTTY, (char *)0);
+ (void) close(t);
+ }
+ }
+#endif
+ fromp->sin_port = ntohs((u_short)fromp->sin_port);
+ if (fromp->sin_family != AF_INET) {
+ syslog(LOG_ERR , "malformed from address\n");
+ exit(1);
+ }
+#ifdef KERBEROS
+ if ((must_pass_rhosts || must_pass_one)
+ && (fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2)) {
+#else
+ if (fromp->sin_port >= IPPORT_RESERVED ||
+ fromp->sin_port < IPPORT_RESERVED/2) {
+#endif /* KERBEROS */
+ syslog(LOG_NOTICE , "connection from bad port\n");
+ exit(1);
+ }
+
+#ifdef CRAY
+
+ /* If this is a secure system then get the packet classification
+ of f. ( Note IP_SECURITY is checked in get_packet_classification:
+ if it's not set then the user's (root) default
+ classification level and compartments are returned. )
+ Then set this process to that level/compart so that the stderr
+ connection will be labeled appropriately.
+ */
+ if (secflag) {
+ if (get_packet_classification(f,getuid(),
+ &packet_level,&packet_compart) < 0) {
+ syslog(LOG_ERR, "cannot get ip packet level\n");
+ exit(1);
+ }
+ if(secflag == TFM_UDB_5) {
+ if(setucmp(packet_compart, C_PROC) != 0) {
+ error("Unable to setucmp.\n");
+ exit(1);
+ }
+ } else if(secflag == TFM_UDB_6) {
+ if(setulvl(packet_level,C_PROC) != 0) {
+ error("Unable to setulvl.\n");
+ exit(1);
+ }
+ if(setucmp(packet_compart, C_PROC) != 0) {
+ error("Unable to setucmp.\n");
+ exit(1);
+ }
+ }
+
+ }
+#endif /* CRAY */
+
+ (void) alarm(60);
+ port = 0;
+ for (;;) {
+ char c;
+ if ((cc = read(f, &c, 1)) != 1) {
+ if (cc < 0)
+ syslog(LOG_NOTICE , "read: %m");
+ shutdown(f, 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(&lport);
+ if (s < 0) {
+ syslog(LOG_ERR ,
+ "can't get stderr port: %m");
+ exit(1);
+ }
+#ifdef KERBEROS
+ if ((must_pass_rhosts || must_pass_one)
+ && port >= IPPORT_RESERVED) {
+#else
+ if (port >= IPPORT_RESERVED) {
+#endif /* KERBEROS */
+ syslog(LOG_ERR , "2nd port not reserved\n");
+ exit(1);
+ }
+ fromp->sin_port = htons((u_short)port);
+ if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
+ syslog(LOG_INFO ,
+ "connect second port: %m");
+ exit(1);
+ }
+ }
+ dup2(f, 0);
+ dup2(f, 1);
+ dup2(f, 2);
+ hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr),
+ fromp->sin_family);
+ if (hp){
+ hostname = malloc(strlen(hp->h_name) + 1);
+ strcpy(hostname,hp->h_name);
+ }
+ else {
+ hostname = malloc(strlen((char *)inet_ntoa(fromp->sin_addr)) + 1);
+ strcpy(hostname,(char *)inet_ntoa(fromp->sin_addr));
+ }
+#ifdef KERBEROS
+ if (must_pass_krb || must_pass_one) {
+ peeraddr.addrtype = fromp->sin_family;
+ peeraddr.length = SIZEOF_INADDR;
+ peeraddr.contents = (krb5_octet *)&fromp->sin_addr;
+ strcpy(srv_name, "host/");
+
+ gethostname(def_host, 100);
+ strcat(srv_name, def_host);
+ if (status = krb5_parse_name(srv_name, &server)) {
+ syslog(LOG_ERR, "parse server name %s: %s", "host",
+ error_message(status));
+ exit(1);
+ }
+ krb5_princ_type(server) = KRB5_NT_SRV_HST;
+ krb5_init_ets();
+
+ if (status = krb5_recvauth(&f,
+ "KCMDV0.1",
+ server,
+ &peeraddr, /* We do want to match this
+ against caddrs in the
+ ticket. */
+ 0, /* use srv5tab */
+ 0, /* no keyproc */
+ 0, /* no keyproc arg */
+ 0, /* no rc_type */
+ 0, /* no seq number */
+ &client, /* return client */
+ &ticket, /* return ticket */
+ &kdata /* return authenticator */
+ )) {
+ error("Kerberos rsh or rcp failed: %s\n",
+ error_message(status));
+ exit(1);
+ }
+ krb5_unparse_name(kdata->client,&kremuser);
+ } else {
+ if (!(remuser = malloc(sizeof(locuser) + 1))){
+ error("Error no memory\n");
+ exit(1);
+ }
+ getstr(remuser, sizeof(locuser), "remuser");
+ }
+
+ /* These two reads will be used in the next release to obtain
+ a forwarded TGT and related info for the rlogin daemon.
+ Put here for compatibility, because both rsh and rlogin
+ use the same kcmd which sends this out regardless.*/
+ if (status = krb5_read_message((krb5_pointer)&f, &inbuf)) {
+ error("Error reading message");
+ exit(1);
+ }
+ if (inbuf.length) {
+ error("Forwarding is not yet supported");
+ exit(1);
+ }
+ if (status = krb5_read_message((krb5_pointer)&f, &inbuf)) {
+ error("Error reading message");
+ exit(1);
+ }
+ if (inbuf.length) {
+ error("Forwarding is not yet supported");
+ exit(1);
+ }
+
+#else
+ getstr(remuser, sizeof(remuser), "remuser");
+#endif /* KERBEROS */
+ getstr(locuser, sizeof(locuser), "locuser");
+ getstr(cmdbuf, sizeof(cmdbuf), "command");
+
+#ifdef KERBEROS
+ if (!(remuser = malloc(sizeof(locuser) + 1))){
+ error("Error no memory\n");
+ exit(1);
+ }
+ getstr(remuser, sizeof(locuser), "remuser");
+#endif
+
+#ifdef CRAY
+ paddr = inet_addr(inet_ntoa(fromp->sin_addr));
+ if(secflag){
+ /*
+ * check network authorization list
+ */
+ if (fetchnal(paddr,&nal) < 0) {
+ /*
+ * NAL file inaccessible, abort connection.
+ */
+ error("Permission denied.\n");
+ exit(1);
+ }
+ }
+#endif /* CRAY */
+
+ pwd = getpwnam(locuser);
+ if (pwd == (struct passwd *) 0 ) {
+ syslog(LOG_ERR ,
+ "Principal %s (%s@%s) for local user %s has no account.\n",
+ kremuser, remuser, hostname, locuser);
+ error("Login incorrect.\n");
+ exit(1);
+ }
+
+#ifdef CRAY
+ /* Setup job entry, and validate udb entry.
+ ( against packet level also ) */
+ if ((jid = setjob(pwd->pw_uid, 0)) < 0) {
+ error("Unable to create new job.\n");
+ exit(1);
+ }
+ if ((jtmpnam = makejtmp(pwd->pw_uid, pwd->pw_gid, jid))) {
+ register int pid, tpid;
+ int status;
+ switch(pid = fork()) {
+ case -1:
+ cleanjtmp(locuser, jtmpnam);
+ envinit[TMPDIRENV] = 0;
+ break;
+ case 0:
+ break;
+ default:
+ close(0);
+ close(1);
+ close(2);
+ close(f);
+ if (port)
+ close(s);
+ while ((tpid = wait(&status)) != pid) {
+ if (tpid < 0)
+ break;
+ }
+ cleanjtmp(locuser, jtmpnam);
+ exit(status>>8);
+ /* NOTREACHED */
+ }
+ } else {
+ envinit[TMPDIRENV] = 0;
+ }
+#ifndef NO_UDB
+ (void)getsysudb();
+
+ if ((ue = getudbnam(pwd->pw_name)) == (struct udb *)NULL) {
+ error("Unable to fetch account id.\n");
+ exit(1);
+ }
+ ue_static = *ue; /* save from setlimits call */
+ endudb();
+ if (secflag) {
+ if(getsysv(&sysv, sizeof(struct sysv)) != 0) {
+ loglogin(hostname, SLG_LLERR, 0, ue);
+ error("Permission denied.\n");
+ exit(1);
+ }
+ if ((packet_level != ue->ue_deflvl) ||
+ ((packet_compart & ue->ue_comparts) != packet_compart )){
+ loglogin(hostname, SLG_LLERR, 0, ue);
+ error("Permission denied.\n");
+ exit(1);
+ }
+ if (ue->ue_disabled != 0) {
+ loglogin(hostname,SLG_LOCK,ue->ue_logfails,ue);
+ error("Permission denied.\n");
+ exit(1);
+ }
+ maxlogs = sysv.sy_maxlogs;
+ }
+ if (acctid(getpid(), ue->ue_acids[0]) == -1) {
+ error("Unable to set account id.\n");
+ exit(1);
+ }
+ if (setshares(pwd->pw_uid, acctid(0, -1), error, 1, 0)) {
+ error("Unable to set shares.\n");
+ exit(1);
+ }
+ if (setlimits(pwd->pw_name, C_PROC, getpid(), UDBRC_INTER)) {
+ error("Unable to set limits.\n");
+ exit(1);
+ }
+ if (setlimits(pwd->pw_name, C_JOB, jid, UDBRC_INTER)) {
+ error("Unable to set limits.\n");
+ exit(1);
+ }
+ ue = &ue_static; /* restore after setlimits call */
+ endudb(); /* setlimits opens udb and leaves it
+ open so close it here. */
+#endif /* !NO_UDB */
+#endif /*CRAY*/
+
+ /* Setup wtmp entry : we do it here so that if this is a CRAY
+ the Process Id is correct and we have not lost our trusted
+ privileges. */
+ if (port) {
+ /* Place entry into wtmp */
+ sprintf(ttyn,"krsh%1d",getpid());
+#ifdef SYSV
+ logwtmp(ttyn,locuser,hostname,1,1); /*Leave wtmp open*/
+#else
+ logwtmp(ttyn,locuser,hostname,1); /*Leave wtmp open*/
+#endif
+ }
+ /* We are simply execing a program over rshd : log entry into wtmp,
+ as kexe(pid), then finish out the session right after that.
+ Syslog should have the information as to what was exec'd */
+ else {
+ sprintf(ttyn,"kexe%1d",getpid());
+#ifdef SYSV
+ logwtmp(ttyn,locuser,hostname,1,1); /* Leave open wtmp */
+#else
+ logwtmp(ttyn,locuser,hostname,1); /* Leave open wtmp */
+#endif
+ }
+
+#ifdef CRAY
+
+ /* If we are a secure system then we need to get rid of our
+ trusted facility, so that MAC on the chdir we work. Before we
+ do this make an entry into wtmp, and any other audit recording. */
+
+ if (secflag) {
+ if (getusrv(&usrv)){
+ syslog(LOG_ERR,"Cannot getusrv");
+ error("Permission denied.\n");
+ loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+ goto signout_please;
+ }
+ /*
+ * 6.0 no longer allows any form ofTRUSTED_PROCESS logins.
+ */
+ if((ue->ue_valcat & TFM_TRUSTED) ||
+ (sysv.sy_oldtfm &&
+ ((ue->ue_comparts & TRUSTED_SUBJECT) == TRUSTED_SUBJECT))) {
+ loglogin(hostname, SLG_TRSUB, ue->ue_logfails,ue);
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+
+ loglogin(hostname, SLG_OKLOG, ue->ue_logfails,ue);
+
+ /* Setup usrv structure with user udb info and
+ packet_level and packet_compart. */
+ usrv.sv_actlvl = packet_level;
+ usrv.sv_actcmp = packet_compart; /*Note get_packet_level sets
+ compartment to users default
+ compartments....*/
+ usrv.sv_permit = ue->ue_permits;
+ usrv.sv_intcls = ue->ue_intcls;
+ usrv.sv_maxcls = ue->ue_maxcls;
+ usrv.sv_intcat = ue->ue_intcat;
+ usrv.sv_valcat = ue->ue_valcat;
+ usrv.sv_savcmp = 0;
+ usrv.sv_savlvl = 0;
+
+ /*
+ * Set user values to workstation boundaries
+ */
+#ifdef MIN
+#undef MIN
+#endif
+#ifdef MAX
+#undef MAX
+#endif
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+ nal_error = 0;
+
+ if (nal.na_sort) {
+ if ((ue->ue_minlvl > nal.na_smax) ||
+ (ue->ue_maxlvl < nal.na_smin))
+ nal_error++;
+ else {
+ usrv.sv_minlvl=MAX(ue->ue_minlvl, nal.na_smin);
+ usrv.sv_maxlvl=MIN(ue->ue_maxlvl, nal.na_smax);
+
+#ifndef IP_SECURITY
+
+ if (usrv.sv_actlvl < usrv.sv_minlvl)
+ usrv.sv_actlvl = usrv.sv_minlvl;
+ if (usrv.sv_actlvl > usrv.sv_maxlvl)
+ usrv.sv_actlvl = usrv.sv_maxlvl;
+
+#else /*IP_SECURITY*/
+ if (usrv.sv_actlvl < usrv.sv_minlvl)
+ nal_error++;
+ if (usrv.sv_actlvl > usrv.sv_maxlvl)
+ nal_error++;
+ if (usrv.sv_actlvl != ue->ue_deflvl)
+ nal_error++;
+
+ usrv.sv_valcmp = ue->ue_comparts & nal.na_scmp;
+ usrv.sv_actcmp &= nal.na_scmp;
+#endif /*IP_SECURITY*/
+ usrv.sv_valcmp = ue->ue_comparts & nal.na_scmp;
+ usrv.sv_actcmp = (usrv.sv_valcmp &
+ ue->ue_defcomps);
+ }
+ } else {
+ /*
+ * If the user's minimum level is greater than
+ * zero, they cannot log on from this (ie. an
+ * unclassified) host.
+ */
+ if (ue->ue_minlvl > 0)
+ nal_error++;
+ /*
+ /*
+ * Address not in NAL, if EXEMPT_NAL is not
+ * true, then even an unclassified user is
+ * not allowed.
+ */
+ if (!EXEMPT_NAL)
+ nal_error++;
+ else {
+ usrv.sv_minlvl = 0;
+ usrv.sv_maxlvl = 0;
+ usrv.sv_valcmp = 0;
+ usrv.sv_actcmp = 0;
+ usrv.sv_actlvl = 0;
+ }
+ }
+ if (nal_error) {
+ loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+#undef MIN
+#undef MAX
+ /* Before the setusrv is done then do a sethost for paddr */
+ sethost(paddr);
+
+ if (setusrv(&usrv) == -1) {
+ loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+ if (getusrv(&usrv) == -1) {
+ error("Getusrv Permission denied.\n");
+ goto signout_please;
+ }
+
+ }
+#endif /*CRAY*/
+
+ if (chdir(pwd->pw_dir) < 0) {
+ syslog(LOG_ERR ,
+ "Principal %s (%s@%s) for local user %s has no home directory.\n",
+ kremuser, remuser, hostname, locuser);
+ error("No remote directory.\n");
+ goto signout_please;
+ }
+#ifdef KERBEROS
+ if (must_pass_krb || must_pass_one) {
+ if ( !krb5_kuserok(kdata->client,locuser) ) {
+ syslog(LOG_ERR ,
+ "Principal %s (%s@%s) for local user %s failed krb5_kuserok.\n",
+ kremuser, remuser, hostname, locuser);
+ if (must_pass_krb) {
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+ failed_krb = 1;
+ }
+ }
+ if (must_pass_rhosts || (failed_krb && must_pass_one)) {
+ if (ruserok(hostname, pwd->pw_uid == 0,
+ remuser, locuser) < 0) {
+ syslog(LOG_ERR ,
+ "Principal %s (%s@%s) for local user %s failed ruserok.\n",
+ kremuser, remuser, hostname, locuser);
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+ }
+#else
+ if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' &&
+ ruserok(hostname, pwd->pw_uid == 0, remuser, locuser) < 0) {
+ error("Permission denied.\n");
+ goto signout_please;
+ }
+#endif /* KERBEROS */
+
+ if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
+ error("Logins currently disabled.\n");
+ goto signout_please;
+ }
+
+ /* Log access to account */
+ pwd = (struct passwd *) getpwnam(locuser);
+ if (pwd && (pwd->pw_uid == 0)) {
+#ifdef LOG_CMD
+ syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s) as ROOT",
+ cmdbuf, kremuser, remuser, hostname);
+#else
+ syslog(LOG_NOTICE ,"Access as ROOT by principal %s (%s@%s)",
+ kremuser, remuser, hostname);
+#endif
+ }
+
+#if defined(KERBEROS) && defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
+ /* Log if principal is from a remote realm */
+ else if (!default_realm(client))
+#endif
+
+#if defined(KERBEROS) && defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
+ /* Log if principal name does not map to local username */
+ else if (!princ_maps_to_lname(client, lusername))
+#endif /* LOG_OTHER_USERS */
+
+#ifdef LOG_ALL_LOGINS /* Log everything */
+ else
+#endif
+
+#if defined(LOG_REMOTE_REALM) || defined(LOG_OTHER_USERS) || defined(LOG_ALL_LOGINS)
+ {
+#ifdef LOG_CMD
+ syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s) as local user %s",
+ cmdbuf, kremuser, remuser, hostname, locuser);
+#else
+ syslog(LOG_NOTICE ,"Access as %s by principal %s (%s@%s)",
+ locuser, kremuser, remuser, hostname);
+#endif
+ }
+#endif
+
+ (void) write(2, "\0", 1);
+
+ if (port) {
+ if (pipe(pv) < 0) {
+ error("Can't make pipe.\n");
+ goto signout_please;
+ }
+ pid = fork();
+ if (pid == -1) {
+ error("Try again.\n");
+ goto signout_please;
+ }
+ if (pid) {
+ signal(SIGINT, cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGPIPE, cleanup);
+ signal(SIGHUP, cleanup);
+ signal(SIGCHLD,SIG_IGN);
+
+ (void) close(0); (void) close(1); (void) close(2);
+ (void) close(f); (void) close(pv[1]);
+ readfrom = (1L<<s) | (1L<<pv[0]);
+ ioctl(pv[0], FIONBIO, (char *)&one);
+ /* should set s nbio! */
+ do {
+ ready = readfrom;
+ if (select(16, &ready, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0) < 0)
+ break;
+ if (ready & (1L<<s)) {
+ if (read(s, &sig, 1) <= 0)
+ readfrom &= ~(1L<<s);
+ else {
+ signal(sig, cleanup);
+ killpg(pid, sig);
+ }
+ }
+ if (ready & (1L<<pv[0])) {
+ errno = 0;
+ cc = read(pv[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ shutdown(s, 1+1);
+ readfrom &= ~(1L<<pv[0]);
+ } else
+ (void) write(s, buf, cc);
+ }
+ } while (readfrom);
+#ifdef KERBEROS
+ syslog(LOG_INFO ,
+ "Shell process completed.");
+#endif
+ /* Finish session in wmtp */
+#ifdef SYSV
+ logwtmp(ttyn,"","",0,0); /* Close wtmp */
+#else
+ logwtmp(ttyn,"","",0); /* Close wtmp */
+#endif
+ exit(0);
+ }
+ setpgrp(0, getpid());
+ (void) close(s); (void) close(pv[0]);
+ dup2(pv[1], 2);
+ (void) close(pv[1]);
+ }
+
+ /* We are simply execing a program over rshd : log entry into wtmp,
+ as kexe(pid), then finish out the session right after that.
+ Syslog should have the information as to what was exec'd */
+ else {
+#ifdef SYSV
+ logwtmp(ttyn,"","",0,0); /* Close wtmp */
+#else
+ logwtmp(ttyn,"","",0); /* Close wtmp */
+#endif
+ }
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = "/bin/sh";
+ (void) close(f);
+ (void) setgid((gid_t)pwd->pw_gid);
+#ifndef sgi
+ initgroups(pwd->pw_name, pwd->pw_gid);
+#endif
+ (void) setuid((uid_t)pwd->pw_uid);
+ environ = envinit;
+ strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
+ strncat(shell, pwd->pw_shell, sizeof(shell)-7);
+ strncat(username, pwd->pw_name, sizeof(username)-6);
+ cp = rindex(pwd->pw_shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+
+ execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+ perror(pwd->pw_shell);
+ perror(cp);
+ exit(1);
+
+ signout_please:
+#ifdef SYSV
+ logwtmp(ttyn,"","",0,0); /* Close wtmp */
+#else
+ logwtmp(ttyn,"","",0); /* Close wtmp */
+#endif
+ exit(1);
+}
+
+
+
+/*VARARGS1*/
+error(fmt, a1, a2, a3)
+ char *fmt;
+ int a1, a2, a3;
+{
+ char buf[BUFSIZ];
+
+ buf[0] = 1;
+ (void) sprintf(buf+1, fmt, a1, a2, a3);
+ (void) write(2, buf, strlen(buf));
+ syslog(LOG_ERR ,"%s",buf+1);
+}
+
+
+
+getstr(buf, cnt, err)
+ char *buf;
+ int cnt;
+ char *err;
+{
+ char c;
+
+ do {
+ if (read(0, &c, 1) != 1)
+ exit(1);
+ *buf++ = c;
+ if (--cnt == 0) {
+ error("%s too long\n", err);
+ exit(1);
+ }
+ } while (c != 0);
+}
+
+
+
+krb5_sigtype
+ cleanup()
+{
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ killpg(pid, SIGTERM);
+ wait(0);
+
+#ifdef SYSV
+ logwtmp(ttyn,"","",0,0); /* Close wtmp */
+#else
+ logwtmp(ttyn,"","",0); /* Close wtmp */
+#endif
+ syslog(LOG_INFO ,"Shell process completed.");
+ exit(0);
+}
+
+
+
+#ifdef CRAY
+char *makejtmp(uid, gid, jid)
+ register int uid, gid, jid;
+{
+ extern int errno;
+
+ register char *endc, *tdp = &tmpdir[strlen(tmpdir)];
+ register int i;
+
+ sprintf(tdp, "%s/jtmp.%06d", JTMPDIR, jid);
+ endc = &tmpdir[strlen(tmpdir)];
+
+ endc[1] = '\0';
+ for (i = 0; i < 26; i++) {
+ endc[0] = 'a' + i;
+ if (mkdir(tdp, JTMPMODE) != -1) {
+ chown(tdp, uid, gid);
+ return (tdp);
+ } else if (errno != EEXIST)
+ break;
+ }
+ return(NULL);
+}
+
+
+
+cleanjtmp(user, tpath)
+ register char *user, *tpath;
+{
+ switch(fork()) {
+ case -1:
+ break;
+ case 0:
+ if (secflag) {
+ execl("/bin/rm", "rm", "-rf", tpath, 0);
+ error("exec of %s failed; errno = %d\n",
+ "/bin/rm", errno);
+ } else {
+ execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0);
+ error("exec of %s failed; errno = %d\n",
+ CLEANTMPCMD, errno);
+ }
+ exit(1);
+ break;
+ default:
+ /*
+ * Just forget about the child, let init will pick it
+ * up after we exit.
+ */
+ break;
+ }
+}
+
+
+
+/***get_packet_classification
+ *
+ *
+ * int get_packet_classification():
+ * Obtain packet level and compartments from passed fd...
+ *
+ * Returns:
+ * -1: If could not get user defaults.
+ * 0: success
+ */
+#ifdef IP_SECURITY
+static int get_packet_classification(fd,useruid,level,comp)
+ int fd;
+ uid_t useruid;
+ int *level;
+ long *comp;
+{
+ struct socket_security pkt_sec;
+ struct udb *udb;
+ int retval;
+ int sockoptlen;
+
+ retval = 0;
+ getsysudb ();
+ udb = getudbuid ((int) useruid);
+ endudb ();
+ if (udb == (struct udb *) 0) return(-1);
+ /* Get packet IP packet label */
+ sockoptlen = SIZEOF_sec;
+ if ( getsockopt(fd,SOL_SOCKET,SO_SECURITY,
+ (char *) &pkt_sec,&sockoptlen)){ /* Failed */
+ return(-2);
+ }
+ *level = pkt_sec.sec_level;
+ *comp = udb->ue_defcomps;
+ return(0);
+}
+
+#else /* If no IP_SECURITY set level to users default */
+
+static int get_packet_classification(fd,useruid,level,comp)
+ int fd;
+ uid_t useruid;
+ int *level;
+ long *comp;
+{
+ struct udb *udb;
+ getsysudb ();
+ udb = getudbuid ((int) useruid);
+ endudb ();
+ if (udb == (struct udb *) 0) return(-1);
+ *level = udb->ue_deflvl;
+ *comp = udb->ue_defcomps;
+ return(0);
+}
+
+#endif /* IP_SECURITY */
+
+
+
+/*
+ * Make a security log entry for the login attempt.
+ * host = pointer to host id
+ * flag = status of login
+ * failures = current losing streak in login attempts
+ */
+/* Make a security log entry for the login attempt.
+ * host = pointer to host id
+ * flag = status of login
+ * failures = current losing streak in login attempts
+ */
+
+loglogin(host, flag, failures, ue)
+ char *host;
+ int flag;
+ int failures;
+ struct udb * ue;
+{
+ char urec[sizeof(struct slghdr) + sizeof(struct slglogin)];
+ struct slghdr *uhdr = (struct slghdr *)urec;
+ struct slglogin *ulogin=(struct slglogin *)&urec[sizeof(struct slghdr)];
+
+ strncpy(ulogin->sl_line, ttyn, sizeof(ulogin->sl_line));
+ strncpy(ulogin->sl_host, host, sizeof(ulogin->sl_host));
+ ulogin->sl_failures = failures;
+ if ( maxlogs && (failures >= maxlogs))
+ flag |= SLG_DSABL;
+ ulogin->sl_result = flag;
+ uhdr->sl_uid = ue->ue_uid;
+ uhdr->sl_ruid = ue->ue_uid;
+ uhdr->sl_juid = ue->ue_uid;
+ uhdr->sl_gid = ue->ue_gids[0];
+ uhdr->sl_rgid = ue->ue_gids[0];
+ uhdr->sl_slvl = ue->ue_deflvl;
+ /* uhdr->sl_scls = ue->ue_defcls; enable for integrity policy */
+ uhdr->sl_olvl = 0;
+ uhdr->sl_len = sizeof(urec);
+
+#ifdef CRAY2
+ slgentry(SLG_LOGN, (word *)urec);
+#else /* ! CRAY2 */
+ slgentry(SLG_LOGN, (waddr_t)urec);
+#endif
+ return;
+}
+
+#endif CRAY
+
+
+
+usage()
+{
+#ifdef KERBEROS
+ syslog(LOG_ERR, "usage: kshd [-rRkK] or [r/R][k/K]shd");
+#else
+ syslog(LOG_ERR, "usage: rshd");
+#endif
+}
+
+
+
+int princ_maps_to_lname(principal, luser)
+ krb5_principal principal;
+ char *luser;
+{
+ char kuser[10];
+ if (!(krb5_aname_to_localname(principal,
+ sizeof(kuser), kuser))
+ && (strcmp(kuser, luser) == 0)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+
+int default_realm(principal)
+ krb5_principal principal;
+{
+ char *def_realm;
+ int realm_length;
+ int retval;
+
+ realm_length = krb5_princ_realm(principal)->length;
+
+ if (retval = krb5_get_default_realm(&def_realm)) {
+ return 0;
+ }
+
+ if ((realm_length != strlen(def_realm)) ||
+ (memcmp(def_realm, krb5_princ_realm(principal)->data, realm_length))) {
+ free(def_realm);
+ return 0;
+ }
+ free(def_realm);
+ return 1;
+}
diff --git a/src/appl/bsd/login.c b/src/appl/bsd/login.c
new file mode 100644
index 0000000000..36fc46fb91
--- /dev/null
+++ b/src/appl/bsd/login.c
@@ -0,0 +1,684 @@
+/*
+ * $Source$
+ * $Author$
+ * $Id$
+ */
+
+#ifndef lint
+static char rcsid_login_c[] = "$Id$";
+#endif lint
+
+/*
+ * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+ "@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)login.c 5.25 (Berkeley) 1/6/89";
+#endif /* not lint */
+
+#define KERBEROS
+
+ /*
+ * login -f name (for pre-authenticated login)
+ * login name (for non-authenticated/non-authorized login)
+ */
+
+#define VFS
+#define BYPASS_ROOT_CHK
+
+#include <sys/param.h>
+#ifndef VFS
+#include <sys/quota.h>
+#endif VFS
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+
+#include <utmp.h>
+#include <signal.h>
+#include <lastlog.h>
+#include <errno.h>
+#ifndef NOTTYENT
+#include <ttyent.h>
+#endif /* NOTTYENT */
+#include <syslog.h>
+#include <grp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <strings.h>
+
+#ifdef UIDGID_T
+ uid_t getuid();
+#define uid_type uid_t
+#define gid_type gid_t
+#else
+ int getuid();
+#define uid_type int
+#define gid_type int
+#endif /* UIDGID_T */
+
+#define TTYGRPNAME "tty" /* name of group to own ttys */
+
+#define MOTDFILE "/etc/motd"
+#define MAILDIR "/usr/spool/mail"
+#define NOLOGIN "/etc/nologin"
+#define HUSHLOGIN ".hushlogin"
+#define LASTLOG "/usr/adm/lastlog"
+#define BSHELL "/bin/sh"
+
+#ifdef VFS
+#define QUOTAWARN "/usr/ucb/quota" /* warn user about quotas */
+#endif VFS
+
+#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host)
+#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name)
+
+/*
+ * This bounds the time given to login. Not a define so it can
+ * be patched on machines where it's too small.
+ */
+int timeout = 300;
+
+struct passwd *pwd;
+char term[64], *hostname, *username = NULL;
+
+struct sgttyb sgttyb;
+struct tchars tc = {
+ CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
+ };
+struct ltchars ltc = {
+ CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
+ };
+
+extern int errno;
+
+char *getenv();
+int putenv();
+void setenv();
+void dofork();
+
+#ifdef POSIX
+typedef void sigtype;
+#else
+typedef int sigtype;
+#endif /* POSIX */
+
+#ifdef CRAY
+char user[32] = "LOGNAME=";
+#include <tmpdir.h>
+char tmpdir[64] = "TMPDIR=";
+#else
+char user[20] = "USER=";
+#endif
+
+char homedir[64] = "HOME=";
+char shell[64] = "SHELL=";
+
+
+#ifdef KERBEROS
+char *envinit[] =
+#ifdef CRAY
+ {homedir, shell, PATH, user, "TZ=GMT0", tmpdir, 0};
+#define TZENV 4
+#define TMPDIRENV 5
+char *getenv();
+extern
+#else
+ {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin:/usr/bin/kerberos",
+ user, 0};
+#endif /* CRAY */
+#else /* !KERBEROS */
+char *envinit[] =
+#ifdef CRAY
+ {homedir, shell, PATH, user, "TZ=GMT0", tmpdir, 0};
+#define TZENV 4
+#define TMPDIRENV 5
+char *getenv();
+extern
+#else
+ {homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin:/usr/bin/kerberos",
+ user, 0};
+#endif /* CRAY */
+#endif /* KERBEROS */
+char **environ;
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern int optind;
+ extern char *optarg;
+ struct group *gr;
+ register int ch;
+ register char *p;
+ int fflag, pflag, cnt;
+ int quietlog, ioctlval;
+ sigtype timedout();
+ char *domain, *salt, *ttyn, *tty;
+ char tbuf[MAXPATHLEN + 2];
+ char *ttyname(), *stypeof(), *crypt(), *getpass();
+ time_t time();
+ off_t lseek();
+ int passwd_req = 1, preserve_env = 0;
+
+ (void)signal(SIGALRM, timedout);
+ (void)alarm((u_int)timeout);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+#ifndef VFS
+ (void)quota(Q_SETUID, 0, 0, 0);
+#endif VFS
+
+ (void)gethostname(tbuf, sizeof(tbuf));
+ domain = index(tbuf, '.');
+
+ passwd_req = 1;
+ while ((ch = getopt(argc, argv, "fp")) != EOF)
+ switch (ch) {
+ case 'f':
+ passwd_req = 0;
+ break;
+ case 'p':
+ preserve_env = 1;
+ break;
+ case '?':
+ default:
+ fprintf(stderr, "usage: login [-fp] [username]\n");
+ exit(1);
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+ if (*argv)
+ hostname = *argv;
+
+ ioctlval = 0;
+ (void)ioctl(0, TIOCLSET, (char *)&ioctlval);
+ (void)ioctl(0, TIOCNXCL, (char *)0);
+ (void)fcntl(0, F_SETFL, ioctlval);
+ (void)ioctl(0, TIOCGETP, (char *)&sgttyb);
+
+ doremotelogin();
+
+ /*
+ * If talking to an rlogin process, propagate the terminal type and
+ * baud rate across the network.
+ */
+ doremoteterm(&sgttyb);
+ sgttyb.sg_erase = CERASE;
+ sgttyb.sg_kill = CKILL;
+ (void)ioctl(0, TIOCSLTC, (char *)&ltc);
+ (void)ioctl(0, TIOCSETC, (char *)&tc);
+ (void)ioctl(0, TIOCSETP, (char *)&sgttyb);
+
+ for (cnt = getdtablesize(); cnt > 2; cnt--)
+ (void) close(cnt);
+
+ ttyn = ttyname(0);
+ if (ttyn == NULL || *ttyn == '\0')
+ ttyn = "/dev/tty??";
+ if (tty = rindex(ttyn, '/'))
+ ++tty;
+ else
+ tty = ttyn;
+
+#ifndef LOG_ODELAY /* 4.2 syslog ... */
+ openlog("login", 0);
+#else
+ openlog("login", LOG_ODELAY, LOG_AUTH);
+#endif /* 4.2 syslog */
+
+ for (cnt = 0;; username = NULL) {
+ ioctlval = 0;
+ (void)ioctl(0, TIOCSETD, (char *)&ioctlval);
+
+ if (username == NULL)
+ getloginname();
+
+ if (pwd = getpwnam(username))
+ salt = pwd->pw_passwd;
+ else
+ salt = "xx";
+
+ /* if user not super-user, check for disabled logins */
+ if (pwd == NULL || pwd->pw_uid)
+ checknologin();
+
+ /*
+ * Disallow automatic login to root; if not invoked by
+ * root, disallow if the uid's differ.
+ */
+ if (!passwd_req && pwd) {
+ int uid = (int) getuid();
+
+ passwd_req = (uid && uid != pwd->pw_uid)
+#ifndef BYPASS_ROOT_CHK
+ || (pwd->pw_uid == 0);
+#else
+ ;
+#endif
+ }
+
+ /*
+ * If no remote login authentication and a password exists
+ * for this user, prompt for one and verify it.
+ */
+ if (!passwd_req || pwd && !*pwd->pw_passwd)
+ break;
+
+ (void) setpriority(PRIO_PROCESS, 0, -4);
+ p = crypt(getpass("Password:"), salt);
+ (void) setpriority(PRIO_PROCESS, 0, 0);
+ if (pwd && !strcmp(p, pwd->pw_passwd))
+ break;
+
+ printf("Login incorrect\n");
+ if (++cnt >= 5) {
+ if (hostname)
+ syslog(LOG_ERR,
+ "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s",
+ tty, UT_HOSTSIZE, hostname, UT_NAMESIZE,
+ username);
+ else
+ syslog(LOG_ERR,
+ "REPEATED LOGIN FAILURES ON %s, %.*s",
+ tty, UT_NAMESIZE, username);
+ (void)ioctl(0, TIOCHPCL, (char *)0);
+ sleepexit(1);
+ }
+ }
+
+ /* committed to login -- turn off timeout */
+ (void)alarm((u_int)0);
+
+ /*
+ * If valid so far and root is logging in, see if root logins on
+ * this terminal are permitted.
+ */
+#ifndef BYPASS_ROOT_CHK
+ if (pwd->pw_uid == 0 && !rootterm(tty)) {
+ if (hostname)
+ syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s FROM %.*s",
+ tty, UT_HOSTSIZE, hostname);
+ else
+ syslog(LOG_ERR, "ROOT LOGIN REFUSED ON %s", tty);
+ printf("Login incorrect\n");
+ sleepexit(1);
+ }
+#endif
+
+#ifndef VFS
+ if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
+ switch(errno) {
+ case EUSERS:
+ fprintf(stderr,
+ "Too many users logged on already.\nTry again later.\n");
+ break;
+ case EPROCLIM:
+ fprintf(stderr,
+ "You have too many processes running.\n");
+ break;
+ default:
+ perror("quota (Q_SETUID)");
+ }
+ sleepexit(0);
+ }
+#endif /* !VFS */
+
+ if (chdir(pwd->pw_dir) < 0) {
+ printf("No directory %s!\n", pwd->pw_dir);
+ if (chdir("/"))
+ exit(0);
+ pwd->pw_dir = "/";
+ printf("Logging in with home = \"/\".\n");
+ }
+
+ /* nothing else left to fail -- really log in */
+ {
+ struct utmp utmp;
+
+ (void)time(&utmp.ut_time);
+ (void) strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
+ if (hostname)
+ (void) strncpy(utmp.ut_host, hostname,
+ sizeof(utmp.ut_host));
+ else
+ bzero(utmp.ut_host, sizeof(utmp.ut_host));
+ (void) strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+ login(&utmp);
+ }
+
+ quietlog = access(HUSHLOGIN, F_OK) == 0;
+ dolastlog(quietlog, tty);
+
+ {
+ static struct winsize win = { 0, 0, 0, 0 };
+
+ (void)ioctl(0, TIOCSWINSZ, (char *)&win);
+ }
+
+ (void)chown(ttyn, pwd->pw_uid,
+ (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
+
+ (void)chmod(ttyn, 0620);
+
+ (void)setgid((gid_type) pwd->pw_gid);
+
+ (void) initgroups(username, pwd->pw_gid);
+
+#ifndef VFS
+ quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
+#endif
+ (void)setuid((uid_type) pwd->pw_uid);
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = BSHELL;
+ /* turn on new line discipline for the csh */
+ else if (!strcmp(pwd->pw_shell, "/bin/csh")) {
+ ioctlval = NTTYDISC;
+ (void)ioctl(0, TIOCSETD, (char *)&ioctlval);
+ }
+ /* Destroy old environment unless requested. */
+ if (!preserve_env)
+ environ = envinit;
+
+ setenv("HOME", pwd->pw_dir);
+ setenv("SHELL", pwd->pw_shell);
+ if (term[0] == '\0')
+ (void) strncpy(term, stypeof(tty), sizeof(term));
+ setenv("TERM", term);
+ setenv("USER", pwd->pw_name);
+ setenv("PATH", "/usr/ucb:/bin:/usr/bin:");
+
+ if (tty[sizeof("tty")-1] == 'd')
+ syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
+
+ if (!quietlog) {
+ struct stat st;
+ motd();
+ (void)sprintf(tbuf, "%s/%s", MAILDIR, pwd->pw_name);
+ if (stat(tbuf, &st) == 0 && st.st_size != 0)
+ printf("You have %smail.\n",
+ (st.st_mtime > st.st_atime) ? "new " : "");
+ }
+
+#ifdef VFS
+ if (! access( QUOTAWARN, X_OK)) (void) system(QUOTAWARN);
+#endif VFS
+ (void)signal(SIGALRM, SIG_DFL);
+ (void)signal(SIGQUIT, SIG_DFL);
+ (void)signal(SIGINT, SIG_DFL);
+ (void)signal(SIGTSTP, SIG_IGN);
+
+ tbuf[0] = '-';
+ (void) strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ?
+ p + 1 : pwd->pw_shell);
+ execlp(pwd->pw_shell, tbuf, 0);
+ fprintf(stderr, "login: no shell: ");
+ perror(pwd->pw_shell);
+ exit(0);
+}
+
+
+
+getloginname()
+{
+ register int ch;
+ register char *p;
+ static char nbuf[UT_NAMESIZE + 1];
+
+ for (;;) {
+ printf("login: ");
+ for (p = nbuf; (ch = getchar()) != '\n'; ) {
+ if (ch == EOF)
+ exit(0);
+ if (p < nbuf + UT_NAMESIZE)
+ *p++ = ch;
+ }
+ if (p > nbuf)
+ if (nbuf[0] == '-')
+ fprintf(stderr,
+ "login names may not start with '-'.\n");
+ else {
+ *p = '\0';
+ username = nbuf;
+ break;
+ }
+ }
+}
+
+
+
+sigtype
+ timedout()
+{
+ fprintf(stderr, "Login timed out after %d seconds\n", timeout);
+ exit(0);
+}
+
+
+
+#ifdef NOTTYENT
+int root_tty_security = 0;
+#endif
+rootterm(tty)
+ char *tty;
+{
+#ifdef NOTTYENT
+ return(root_tty_security);
+#else
+ struct ttyent *t;
+
+ return((t = getttynam(tty)) && t->ty_status&TTY_SECURE);
+#endif NOTTYENT
+}
+
+
+
+jmp_buf motdinterrupt;
+
+motd()
+{
+ register int fd, nchars;
+ sigtype (*oldint)(), sigint();
+ char tbuf[8192];
+
+ if ((fd = open(MOTDFILE, O_RDONLY, 0)) < 0)
+ return;
+ oldint = (sigtype (*)()) signal(SIGINT, sigint);
+ if (setjmp(motdinterrupt) == 0)
+ while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+ (void)write(fileno(stdout), tbuf, nchars);
+ (void)signal(SIGINT, oldint);
+ (void)close(fd);
+}
+
+
+
+sigtype
+ sigint()
+{
+ longjmp(motdinterrupt, 1);
+}
+
+
+
+checknologin()
+{
+ register int fd, nchars;
+ char tbuf[8192];
+
+ if ((fd = open(NOLOGIN, O_RDONLY, 0)) >= 0) {
+ while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+ (void)write(fileno(stdout), tbuf, nchars);
+ sleepexit(0);
+ }
+}
+
+
+
+dolastlog(quiet, tty)
+ int quiet;
+ char *tty;
+{
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+ if (!quiet) {
+ if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
+ ll.ll_time != 0) {
+ printf("Last login: %.*s ",
+ 24-5, (char *)ctime(&ll.ll_time));
+ if (*ll.ll_host != '\0')
+ printf("from %.*s\n",
+ sizeof(ll.ll_host), ll.ll_host);
+ else
+ printf("on %.*s\n",
+ sizeof(ll.ll_line), ll.ll_line);
+ }
+ (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+ }
+ (void)time(&ll.ll_time);
+ (void) strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+ if (hostname)
+ (void) strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
+ else
+ (void) bzero(ll.ll_host, sizeof(ll.ll_host));
+ (void)write(fd, (char *)&ll, sizeof(ll));
+ (void)close(fd);
+ }
+}
+
+
+
+#undef UNKNOWN
+#define UNKNOWN "su"
+
+char *stypeof(ttyid)
+ char *ttyid;
+{
+#ifdef NOTTYENT
+ return(UNKNOWN);
+#else
+ struct ttyent *t;
+
+ return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
+#endif
+}
+
+
+
+getstr(buf, cnt, err)
+ char *buf, *err;
+ int cnt;
+{
+ char ch;
+
+ do {
+ if (read(0, &ch, sizeof(ch)) != sizeof(ch))
+ exit(1);
+ if (--cnt < 0) {
+ fprintf(stderr, "%s too long\r\n", err);
+ sleepexit(1);
+ }
+ *buf++ = ch;
+ } while (ch);
+}
+
+
+
+doremotelogin()
+{
+ static char lusername[UT_NAMESIZE+1];
+ char rusername[UT_NAMESIZE+1];
+
+ getstr(rusername, sizeof(rusername), "remuser");
+ getstr(lusername, sizeof(lusername), "locuser");
+ if (username == NULL)
+ username = lusername;
+ getstr(term, sizeof(term), "Terminal type");
+}
+
+
+
+char *speeds[] = {
+ "0", "50", "75", "110", "134", "150", "200", "300", "600",
+ "1200", "1800", "2400", "4800", "9600", "19200", "38400",
+};
+#define NSPEEDS (sizeof(speeds) / sizeof(speeds[0]))
+
+doremoteterm(tp)
+ struct sgttyb *tp;
+{
+ register char *cp = index(term, '/'), **cpp;
+ char *speed;
+
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = index(speed, '/');
+ if (cp)
+ *cp++ = '\0';
+ for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
+ if (strcmp(*cpp, speed) == 0) {
+ tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
+ break;
+ }
+ }
+ tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
+}
+
+
+
+sleepexit(eval)
+ int eval;
+{
+ sleep((u_int)5);
+ exit(eval);
+}
+
+
+
+void setenv(var, value)
+ char *var, *value;
+{
+ char *env_str;
+ int retval, str_size = strlen(var) + strlen(value) + strlen("=") + 1;
+
+ env_str = (char *) malloc(str_size);
+
+ strcpy(env_str, var);
+ strcat(env_str, "=");
+ strcat(env_str, value);
+ env_str[str_size-1] = '\0';
+
+ if (retval = putenv(env_str)) {
+ syslog(LOG_ERR, "Not enough memory\n");
+ exit(1);
+ }
+}
diff --git a/src/appl/bsd/logutil.c b/src/appl/bsd/logutil.c
new file mode 100644
index 0000000000..645df54592
--- /dev/null
+++ b/src/appl/bsd/logutil.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)login.c 5.1 (Berkeley) 9/27/88";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#if defined (CRAY) || defined (sgi)
+#include <sys/fcntl.h>
+#define L_SET 0
+#define L_INCR 1
+#endif
+#include <utmp.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#ifndef UTMP_FILE
+#define UTMP_FILE "/etc/utmp"
+#endif
+#ifndef WTMP_FILE
+#ifdef SYSV
+#define WTMPFILE "/etc/wtmp"
+#else
+#define WTMP_FILE "/usr/adm/wtmp"
+#endif
+#endif
+
+void login(ut)
+ struct utmp *ut;
+{
+ register int fd;
+ int tty;
+ off_t lseek();
+
+ tty = ttyslot();
+ if (tty > 0 && (fd = open(UTMP_FILE, O_WRONLY, 0)) >= 0) {
+ (void)lseek(fd, (long)(tty * sizeof(struct utmp)), L_SET);
+ (void)write(fd, (char *)ut, sizeof(struct utmp));
+ (void)close(fd);
+ }
+ if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) >= 0) {
+ (void)write(fd, (char *)ut, sizeof(struct utmp));
+ (void)close(fd);
+ }
+}
+
+
+
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)logout.c 5.1 (Berkeley) 8/31/88";
+#endif /* LIBC_SCCS and not lint */
+
+logout(line)
+ register char *line;
+{
+ register FILE *fp;
+ struct utmp ut;
+ int rval;
+ time_t time();
+
+ if (!(fp = fopen(UTMP_FILE, "r+")))
+ return(0);
+ rval = 1;
+ while (fread((char *)&ut, sizeof(struct utmp), 1, fp) == 1) {
+ if (!ut.ut_name[0] ||
+ strncmp(ut.ut_line, line, sizeof(ut.ut_line)))
+ continue;
+ memset(ut.ut_name,0, sizeof(ut.ut_name));
+#ifndef NO_UT_HOST
+ memset(ut.ut_host,0, sizeof(ut.ut_host));
+#endif
+ (void)time(&ut.ut_time);
+ (void)fseek(fp, (long)-sizeof(struct utmp), L_INCR);
+ (void)fwrite((char *)&ut, sizeof(struct utmp), 1, fp);
+ (void)fseek(fp, (long)0, L_INCR);
+ rval = 0;
+ }
+ (void)fclose(fp);
+ return(rval);
+}
+
+
+
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)logwtmp.c 5.2 (Berkeley) 9/20/88";
+#endif /* LIBC_SCCS and not lint */
+
+static int fd = -1;
+
+#ifndef SYSV
+logwtmp(line, name, host, keep_open)
+#else
+logwtmp(line, name, host, keep_open, logingin)
+#endif
+ char *line, *name, *host;
+ int keep_open;
+#ifdef SYSV
+ int logingin;
+#endif
+{
+ struct utmp ut;
+ struct stat buf;
+ time_t time();
+ char *strncpy();
+
+ if (fd < 0 && (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (!fstat(fd, &buf)) {
+ (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+#ifndef NO_UT_HOST
+ (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+#endif
+#ifdef SYSV
+ (void)strncpy(ut.ut_id, (char *)ut.ut_line + 4,
+ sizeof(ut.ut_id));
+ ut.ut_type = logingin ? USER_PROCESS : DEAD_PROCESS;
+ ut.ut_pid = getpid();
+#endif
+ (void)time(&ut.ut_time);
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void)ftruncate(fd, buf.st_size);
+ }
+ if ( !keep_open)
+ (void)close(fd);
+}
diff --git a/src/appl/bsd/rcp.M b/src/appl/bsd/rcp.M
new file mode 100644
index 0000000000..ed3f872cad
--- /dev/null
+++ b/src/appl/bsd/rcp.M
@@ -0,0 +1,130 @@
+.\" $Source$
+.\" $Author$
+.\" $Header$
+.\"
+.\" Copyright (c) 1983 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)rcp.1 6.6 (Berkeley) 9/20/88
+.\"
+.TH RCP 1 "Kerberos Version 4.0" "MIT Project Athena"
+.UC 5
+.SH NAME
+rcp \- remote file copy
+.SH SYNOPSIS
+.B rcp
+[
+.B \-p
+] [
+.B \-x
+] [
+.B \-k
+realm ] file1 file2
+.br
+.B rcp
+[
+.B \-p
+] [
+.B \-x
+] [
+.B \-k
+realm ] [
+.B \-r
+] file ... directory
+.SH DESCRIPTION
+.I Rcp
+copies files between machines. Each
+.I file
+or
+.I directory
+argument is either a remote file name of the
+form ``rhost:path'', or a local file name (containing no `:' characters,
+or a `/' before any `:'s).
+.PP
+If the
+.B \-r
+option
+is specified and any of the source files are directories,
+.I rcp
+copies each subtree rooted at that name; in this case
+the destination must be a directory.
+.PP
+By default, the mode and owner of
+.I file2
+are preserved if it already existed; otherwise the mode of the source file
+modified by the
+.IR umask (2)
+on the destination host is used.
+The
+.B \-p
+option causes
+.I rcp
+to attempt to preserve (duplicate) in its copies the modification
+times and modes of the source files, ignoring the
+.IR umask .
+.PP
+If
+.I path
+is not a full path name, it is interpreted relative to
+your login directory on
+.IR rhost .
+A
+.I path
+on a remote host may be quoted (using \e, ", or \(aa)
+so that the metacharacters are interpreted remotely.
+.PP
+.I Rcp
+does not prompt for passwords; it uses Kerberos authentication when
+connecting to
+.IR rhost .
+Authorization is as described in
+.IR rlogin (1).
+.PP
+The
+.B \-x
+option selects encryption of all information transferring between hosts.
+The
+.B \-k
+.I realm
+option causes
+.I rcp
+to obtain tickets for the remote host in
+.I realm
+instead of the remote host's realm as determined by
+.IR krb_realmofhost (3).
+.PP
+.I Rcp
+handles third party copies, where neither source nor target files
+are on the current machine.
+Hostnames may also take the form ``rname@rhost'' to use
+.I rname
+rather than the current user name on the remote host.
+.SH SEE ALSO
+cp(1), ftp(1), rsh(1), rlogin(1), kerberos(3), krb_getrealm(3),
+rcp(1) [UCB version]
+.SH BUGS
+Doesn't detect all cases where the target of a copy might
+be a file in cases where only a directory should be legal.
+.PP
+Is confused by any output generated by commands in a
+\&.login, \&.profile, or \&.cshrc file on the remote host.
+.PP
+The destination user and hostname may have to be specified as
+``rhost.rname'' when the destination machine is running the 4.2BSD
+version of \fIrcp\fP.
+.PP
+Kerberos is only used for the first connection of a third-party copy;
+the second connection uses the standard Berkeley rcp protocol.
+
diff --git a/src/appl/bsd/rlogin.M b/src/appl/bsd/rlogin.M
new file mode 100644
index 0000000000..c24ef85e0f
--- /dev/null
+++ b/src/appl/bsd/rlogin.M
@@ -0,0 +1,200 @@
+.\" $Source$
+.\" $Author$
+.\" $Header$
+.\"
+.\" Copyright (c) 1983 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)rlogin.1 6.9 (Berkeley) 9/19/88
+.\"
+.TH RLOGIN 1 "Kerberos Version 4.0" "MIT Project Athena"
+.UC 5
+.SH NAME
+rlogin \- remote login
+.SH SYNOPSIS
+.B rlogin
+rhost [
+\fB\-e\fR\fI\|c\fR
+] [
+.B \-8
+] [
+.B \-c
+] [
+.B \-a
+] [
+.B \-t
+termtype ] [
+.B \-n
+] [
+.B \-7
+] [
+.B \-d
+] [
+.B \-k
+realm ] [
+.B \-x
+] [
+.B \-noflow
+] [
+.B \-L
+] [
+.B \-l
+username ]
+.br
+rhost [
+\fB\-e\fR\fIc\fR
+] [
+.B \-8
+] [
+.B \-c
+] [
+.B \-a
+] [
+.B \-t
+termtype ] [
+.B \-n
+] [
+.B \-7
+] [
+.B \-d
+] [
+.B \-k
+realm ] [
+.B \-x
+] [
+.B \-noflow
+] [
+.B \-L
+] [
+.B \-l
+username ]
+.SH DESCRIPTION
+.I Rlogin
+connects your terminal on the current local host system
+.I lhost
+to the remote host system
+.I rhost.
+.PP
+The version built to use Kerberos authentication is very similar to the
+standard Berkeley rlogin(1), except that instead of the \fIrhosts\fP
+mechanism, it uses Kerberos authentication to determine the
+authorization to use a remote account.
+.PP
+Each user may have a private authorization list in a file \&.klogin
+in his login directory. Each line in this file should contain a
+Kerberos principal name of the form
+.IR principal.instance@realm .
+If the originating user is authenticated to one of the principals named
+in \&.klogin, access is granted to the account. The principal
+\fIaccountname\fP.@\fIlocalrealm\fP is granted access if there is no
+\&.klogin file.
+Otherwise
+a login and password will be prompted for on the remote machine as in
+.IR login (1).
+To avoid some security problems, the \&.klogin file must be owned by
+the remote user.
+.PP
+If there is some problem in marshaling the Kerberos authentication
+information, an error message is printed and the standard UCB rlogin is
+executed in place of the Kerberos rlogin.
+.PP
+A line of the form ``~.'' disconnects from the remote host, where
+``~'' is the escape character.
+Similarly, the line ``~^Z'' (where ^Z, control-Z, is the suspend character)
+will suspend the rlogin session.
+Substitution of the delayed-suspend character (normally ^Y)
+for the suspend character suspends the send portion of the rlogin,
+but allows output from the remote system.
+.PP
+The remote terminal type is the same as your local
+terminal type (as given in your environment TERM variable), unless the
+.B \-t
+option is specified (see below).
+The terminal or window size is also copied to the remote system
+if the server supports the option,
+and changes in size are reflected as well.
+.PP
+All echoing takes place at the remote site, so that (except for
+delays) the rlogin is transparent. Flow control via ^S and ^Q and
+flushing of input and output on interrupts are handled properly.
+.PP
+The
+.B \-8
+option allows an eight-bit input data path at all times;
+otherwise parity bits are stripped except when the remote side's
+stop and start characters are other than ^S/^Q. Eight-bit mode is the default.
+.PP
+The
+.B \-L
+option allows the rlogin session to be run in litout mode.
+.PP
+The
+.B \-e
+option allows specification of a different escape character.
+There is no space separating this option flag and the new escape
+character.
+.PP
+The
+.B \-c
+option requires confirmation before disconnecting via ``~.''
+.PP
+The
+.B \-a
+option forces the remote machine to ask for a password by sending a null local
+username. This option has no effect unless the standard UCB rlogin is
+executed in place of the Kerberos rlogin (see above).
+.PP
+The
+.B \-t
+option replaces the terminal type passed to the remote host with
+\fItermtype\fP.
+.PP
+The
+.B \-n
+option prevents suspension of rlogin via ``~^Z'' or ``~^Y''.
+.PP
+The
+.B \-7
+option forces seven-bit transmissions.
+.PP
+The
+.B \-d
+option turns on socket debugging (via \fIsetsockopt(2)\fR) on the TCP
+sockets used for communication with the remote host.
+.PP
+The
+.B \-noflow
+option forces transmission of flow control characters (^S/^Q) to the
+remote system.
+.PP
+The
+.B \-k
+option requests rlogin to obtain tickets for the remote host in realm
+.I realm
+instead of the remote host's realm as determined by
+.IR krb_realmofhost (3).
+.PP
+The
+.B \-x
+option turns on DES encryption for all data passed via the
+rlogin session. This significantly reduces response time and
+significantly increases CPU utilization.
+.SH SEE ALSO
+rsh(1), kerberos(3), krb_sendauth(3), krb_realmofhost(3),
+rlogin(1) [UCB version]
+.SH FILES
+/usr/hosts/* for \fIrhost\fP version of the command
+.SH BUGS
+More of the environment should be propagated.
diff --git a/src/appl/bsd/rsh.M b/src/appl/bsd/rsh.M
new file mode 100644
index 0000000000..eda3328d4b
--- /dev/null
+++ b/src/appl/bsd/rsh.M
@@ -0,0 +1,153 @@
+.\" $Source$
+.\" $Author$
+.\" $Header$
+.\"
+.\" Copyright (c) 1983 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms are permitted
+.\" provided that the above copyright notice and this paragraph are
+.\" duplicated in all such forms and that any documentation,
+.\" advertising materials, and other materials related to such
+.\" distribution and use acknowledge that the software was developed
+.\" by the University of California, Berkeley. The name of the
+.\" University may not be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" @(#)rsh.1 6.2 (Berkeley) 9/20/88
+.\"
+.TH RSH 1 "Kerberos Version 4.0" "MIT Project Athena"
+.UC 5
+.SH NAME
+rsh \- remote shell
+.SH SYNOPSIS
+.B rsh
+host
+[
+.B \-l
+username
+] [
+.B \-n
+] [
+.B \-d
+] [
+.B \-k
+realm ] command
+.br
+host
+[
+.B \-l
+username
+] [
+.B \-n
+] [
+.B \-d
+] [
+.B \-k
+realm ] command
+.SH DESCRIPTION
+.I Rsh
+connects to the specified
+.I host,
+and executes the specified \fIcommand\fR.
+.I Rsh
+copies its standard input to the remote command, the standard
+output of the remote command to its standard output, and the
+standard error of the remote command to its standard error.
+Interrupt, quit and terminate signals are propagated to the remote
+command; \fIrsh\fP normally terminates when the remote command does.
+.PP
+The remote username used is the same as your local username,
+unless you specify a different remote name with the
+.B \-l
+option.
+Kerberos authentication is used, and authorization is determined as in
+rlogin(1).
+.PP
+The
+.B \-k
+\fIrealm\fP option causes
+.I rsh
+to obtain tickets for the remote host in
+.I realm
+instead of the remote host's realm as determined by
+.IR krb_realmofhost (3).
+.PP
+The
+.B \-d
+option turns on socket debugging (via \fIsetsockopt(2)\fR) on the TCP
+sockets used for communication with the remote host.
+.PP
+The
+.B \-n
+option redirects input from the special device
+.I /dev/null
+(see the BUGS section below).
+.PP
+If you omit
+.I command,
+then instead of executing a single command, you will be logged in
+on the remote host using
+.IR rlogin (1).
+.PP
+Shell metacharacters which are not quoted are interpreted
+on local machine, while quoted metacharacters are interpreted on
+the remote machine.
+Thus the command
+.PP
+\ \ \ rsh otherhost cat remotefile >> localfile
+.PP
+appends the remote file
+.I remotefile
+to the local file
+.I localfile,
+while
+.PP
+\ \ \ rsh otherhost cat remotefile ">>" otherremotefile
+.PP
+appends
+.I remotefile
+to
+.I otherremotefile.
+.PP
+The host names for local machines are also commands in the directory
+/usr/hosts; if you put this directory in your search path
+then the
+.B rsh
+on the command line can be omitted.
+.SH FILES
+.ta 2i
+/etc/hosts
+.br
+/usr/hosts/*
+.DT
+.SH SEE ALSO
+rlogin(1), kerberos(3), krb_sendauth(3), krb_realmofhost(3)
+.SH BUGS
+If you are using
+.IR csh (1)
+and put a
+.IR rsh (1)
+in the background without redirecting its input
+away from the terminal, it will block even if no reads
+are posted by the remote command. If no input is desired
+you should redirect the input of
+.I rsh
+to /dev/null using the
+.B \-n
+option.
+.PP
+You cannot run an interactive command
+(like
+.IR rogue (6)
+or
+.IR vi (1));
+use
+.IR rlogin (1).
+.PP
+Stop signals stop the local \fIrsh\fP process only; this is arguably
+wrong, but currently hard to fix for reasons too complicated to
+explain here.