diff options
Diffstat (limited to 'src/appl')
-rw-r--r-- | src/appl/bsd/Imakefile | 70 | ||||
-rw-r--r-- | src/appl/bsd/kcmd.c | 611 | ||||
-rw-r--r-- | src/appl/bsd/krcp.c | 1420 | ||||
-rw-r--r-- | src/appl/bsd/krlogin.c | 1468 | ||||
-rw-r--r-- | src/appl/bsd/krlogind.M | 102 | ||||
-rw-r--r-- | src/appl/bsd/krlogind.c | 1365 | ||||
-rw-r--r-- | src/appl/bsd/krsh.c | 416 | ||||
-rw-r--r-- | src/appl/bsd/krshd.M | 164 | ||||
-rw-r--r-- | src/appl/bsd/krshd.c | 1340 | ||||
-rw-r--r-- | src/appl/bsd/login.c | 684 | ||||
-rw-r--r-- | src/appl/bsd/logutil.c | 178 | ||||
-rw-r--r-- | src/appl/bsd/rcp.M | 130 | ||||
-rw-r--r-- | src/appl/bsd/rlogin.M | 200 | ||||
-rw-r--r-- | src/appl/bsd/rsh.M | 153 |
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 *)¬c); +#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 *)¬c); +#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 = ¬c; + 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 *)<c); + (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. |