All rights reserved.\n";
#endif /* not lint */
-#ifndef lint
-static char sccsid[] = "@(#)rshd.c 5.12 (Berkeley) 9/12/88";
-#endif /* not lint */
+/* based on @(#)rshd.c 5.12 (Berkeley) 9/12/88 */
/*
* remote shell server:
* 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.
+ * -k means trust krb4 or krb5
+ * -5 means trust krb5
+ * -4 means trust krb4 (using .klogin)
*
- * 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.
- * SERVE_V4 - Define this if v4 rlogin clients are also to be served.
+ * KRB5_KRB4_COMPAT - Define this if v4 rlogin clients are also to be served.
* ALWAYS_V5_KUSEROK - Define this if you want .k5login to be
* checked even for v4 clients (instead of .klogin).
* LOG_ALL_LOGINS - Define this if you want to log all logins.
* Note: Root account access is always logged.
*/
-#define SERVE_V4
#define SERVE_NON_KRB
#define LOG_REMOTE_REALM
#define LOG_CMD
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/file.h>
-#include <sys/time.h>
-
-#ifdef NEED_SYS_FCNTL_H
-#include <sys/fcntl.h>
-#endif
-#ifdef USE_UNISTD_H
+
+#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
#ifdef __SCO__
#include <sys/unistd.h>
#endif
-/** XXX -- this may be bogus **/
-#if defined(CRAY) || defined(sysvimp) || defined(aux20)
-#ifndef _TYPES_
-#define _TYPES_
-#endif
-#ifndef F_OK
-#define F_OK 0
-#endif
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#if !defined(KERBEROS) || !defined(KRB5_KRB4_COMPAT)
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
+#include <sys/socket.h>
#endif
-/** XXX **/
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <fcntl.h>
-/* not portable: #include <sys/resource.h> */
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
#include <netinet/in.h>
-
#include <arpa/inet.h>
#include <stdio.h>
+#include <grp.h>
#include <errno.h>
#include <pwd.h>
#include <ctype.h>
#include <string.h>
+#include <libpty.h>
+#include <sys/wait.h>
#ifdef HAVE_SYS_LABEL_H
/* only SunOS 4? */
#include <sys/audit.h>
#include <pwdadj.h>
#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
#include <signal.h>
+#if !defined(KERBEROS) || !defined(KRB5_KRB4_COMPAT)
+/* Ultrix doesn't protect it vs multiple inclusion, and krb.h includes it */
#include <netdb.h>
-
+#endif
+
#ifdef CRAY
#ifndef NO_UDB
#include <udb.h>
#endif /* CRAY */
#include <syslog.h>
+
+#ifdef POSIX_TERMIOS
+#include <termios.h>
+#endif
#ifdef HAVE_SYS_FILIO_H
/* get FIONBIO from sys/filio.h, so what if it is a compatibility feature */
#endif
#ifdef KERBEROS
-#include <krb5/krb5.h>
-#include <krb5/asn1.h>
-#include <krb5/crc-32.h>
-#include <krb5/mit-des.h>
-#include <krb5/ext-proto.h>
-
+#include <krb5.h>
#include <com_err.h>
-
#include "loginpaths.h"
-
-/** XXX - make these portable **/
-#ifdef hpux
-/* has no killpg... */
-#define killpg(pid, sig) kill(-(pid), (sig))
+#ifdef KRB5_KRB4_COMPAT
+#include <kerberosIV/krb.h>
+Key_schedule v4_schedule;
#endif
+#include <k5-util.h>
-#ifdef __svr4__
-#define setpgrp(a,b) setpgrp()
-#define getpgrp(a) getpgid(a)
-/* has no killpg... */
-#define killpg(pid, sig) kill(-(pid), (sig))
+#ifdef HAVE_PATHS_H
+#include <paths.h>
#endif
-#ifdef linux
-#define setpgrp(a,b) setpgid(a,b)
+#if defined(_PATH_NOLOGIN)
+#define NOLOGIN _PATH_NOLOGIN
+#else
+#define NOLOGIN "/etc/nologin"
#endif
-#ifdef __SCO__
-/* sco has getgroups and setgroups but no initgroups */
-int initgroups(char* name, gid_t basegid) {
- gid_t others[NGROUPS_MAX+1];
- int ngrps;
+#include "defines.h"
- others[0] = basegid;
- ngrps = getgroups(NGROUPS_MAX, others+1);
- return setgroups(ngrps+1, others);
-}
+#if HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+#ifndef MAXDNAME
+#define MAXDNAME 256 /*per the rfc*/
#endif
-/** XXX **/
-#define ARGSTR "rRkKD:?"
+#define ARGSTR "ek54ciD:S:M:AP:?L:w:"
+
+
+
+
+#define MAXRETRIES 4
+
+krb5_context bsd_context;
+char *srvtab = NULL;
+krb5_keytab keytab = NULL;
+krb5_ccache ccache = NULL;
+
+void fatal(int, const char *);
+
+int require_encrypt = 0;
+int do_encrypt = 0;
+int anyport = 0;
+char *kprogdir = KPROGDIR;
+int netf;
+int maxhostlen = 0;
+int stripdomain = 1;
+int always_ip = 0;
+
+static krb5_error_code recvauth(int netfd, struct sockaddr *peersin,
+ int *valid_checksum);
+
#else /* !KERBEROS */
-#define ARGSTR "rRD:?"
+
+#define ARGSTR "RD:?"
#endif /* KERBEROS */
+
-int must_pass_rhosts = 0, must_pass_k5 = 0, must_pass_one = 0;
-int failed_k5 = 0;
+#ifndef HAVE_KILLPG
+#define killpg(pid, sig) kill(-(pid), (sig))
+#endif
+
+/* There are two authentication related masks:
+ * auth_ok and auth_sent.
+* The auth_ok mask is the oring of authentication systems any one
+* of which can be used.
+* The auth_sent mask is the oring of one or more authentication/authorization
+* systems that succeeded. If the anding
+* of these two masks is true, then authorization is successful.
+*/
+#define AUTH_KRB4 (0x1)
+#define AUTH_KRB5 (0x2)
+int auth_ok = 0, auth_sent = 0;
+int checksum_required = 0, checksum_ignored = 0;
char *progname;
#define MAX_PROG_NAME 10
+/* Leave room for 4 environment variables to be passed */
+#define MAXENV 4
+#define SAVEENVPAD 0,0,0,0 /* padding for envinit slots */
+char *save_env[MAXENV];
+int num_env = 0;
+
#ifdef CRAY
int secflag;
extern
#endif /* CRAY */
-int errno;
-/*VARARGS1*/
-int error();
+void error (char *fmt, ...)
+#if !defined (__cplusplus) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+ __attribute__ ((__format__ (__printf__, 1, 2)))
+#endif
+ ;
+
+void usage(void), getstr(int, char *, int, char *),
+ doit(int, struct sockaddr *);
+
+#ifndef HAVE_INITGROUPS
+int initgroups(char* name, gid_t basegid) {
+ gid_t others[NGROUPS_MAX+1];
+ int ngrps;
+ others[0] = basegid;
+ ngrps = getgroups(NGROUPS_MAX, others+1);
+ return setgroups(ngrps+1, others);
+}
+#endif
-main(argc, argv)
+int main(argc, argv)
int argc;
char **argv;
{
#if defined(BSD) && BSD+0 >= 43
struct linger linger;
#endif
- int on = 1, fromlen;
- struct sockaddr_in from;
+ int on = 1;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
extern int opterr, optind;
extern char *optarg;
- char *options, ch;
- int i;
+ int ch;
int fd;
int debug_port = 0;
+#ifdef KERBEROS
+ krb5_error_code status;
+#endif
#ifdef CRAY
secflag = sysconf(_SC_CRAY_SECURE_SYS);
#endif
- progname = *argv;
+ progname = strrchr (*argv, '/');
+ progname = progname ? progname + 1 : *argv;
#ifndef LOG_ODELAY /* 4.2 syslog */
openlog(progname, LOG_PID);
#else
-#ifndef LOG_DAEMON
-#define LOG_DAEMON 0
+#ifndef LOG_AUTH
+#define LOG_AUTH 0
#endif
- openlog(progname, LOG_PID | LOG_ODELAY, LOG_DAEMON);
+ openlog(progname, LOG_PID | LOG_ODELAY, LOG_AUTH);
#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();
+#ifdef KERBEROS
+ status = krb5_init_context(&bsd_context);
+ if (status) {
+ syslog(LOG_ERR, "Error initializing krb5: %s",
+ error_message(status));
exit(1);
- }
}
+#endif
- /* Analyse parameters. */
+ /* Analyze parameters. */
opterr = 0;
- while ((ch = getopt(argc, argv, ARGSTR)) != EOF)
+ while ((ch = getopt(argc, argv, ARGSTR)) != -1)
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 */
+#ifdef KRB5_KRB4_COMPAT
+ auth_ok |= (AUTH_KRB5|AUTH_KRB4);
+#else
+ auth_ok |= AUTH_KRB5;
+#endif /* KRB5_KRB4_COMPAT*/
+ break;
+
+ case '5':
+ auth_ok |= AUTH_KRB5;
+ break;
+ case 'c':
+ checksum_required = 1;
+ break;
+ case 'i':
+ checksum_ignored = 1;
+ break;
+
+#ifdef KRB5_KRB4_COMPAT
+ case '4':
+ auth_ok |= AUTH_KRB4;
+ break;
+#endif
+
+ case 'e':
+ require_encrypt = 1;
+ break;
+
+ case 'S':
+ if ((status = krb5_kt_resolve(bsd_context, optarg, &keytab))) {
+ com_err(progname, status, "while resolving srvtab file %s",
+ optarg);
+ exit(2);
+ }
+ break;
+
+ case 'M':
+ krb5_set_default_realm(bsd_context, optarg);
+ break;
+
+ case 'A':
+ anyport = 1;
+ break;
+
+ case 'P':
+ kprogdir = optarg;
break;
- case 'K': /* If 'K', must pass .k5login check*/
- must_pass_k5 = 1;
- if (must_pass_one)
- must_pass_one = 0;
+
+ case 'L':
+ if (num_env < MAXENV) {
+ save_env[num_env] = strdup(optarg);
+ if(!save_env[num_env++]) {
+ com_err(progname, ENOMEM, "in saving environment");
+ exit(2);
+ }
+ } else {
+ fprintf(stderr, "%s: Only %d -L arguments allowed\n",
+ progname, MAXENV);
+ exit(2);
+ }
break;
#endif
case 'D':
debug_port = atoi(optarg);
break;
- case '?':
- default:
+ case 'w':
+ if (!strcmp(optarg, "ip"))
+ always_ip = 1;
+ else {
+ char *cp;
+ cp = strchr(optarg, ',');
+ if (cp == NULL)
+ maxhostlen = atoi(optarg);
+ else if (*(++cp)) {
+ if (!strcmp(cp, "striplocal"))
+ stripdomain = 1;
+ else if (!strcmp(cp, "nostriplocal"))
+ stripdomain = 0;
+ else {
+ usage();
+ exit(1);
+ }
+ *(--cp) = '\0';
+ maxhostlen = atoi(optarg);
+ }
+ }
+ break;
+ case '?':
+ default:
usage();
exit(1);
break;
}
+
+ if (optind == 0) {
+ usage();
+ exit(1);
+ }
+
argc -= optind;
argv += optind;
fromlen = sizeof (from);
- if (debug_port) {
- int s;
- struct sockaddr_in sin;
-
- if ((s = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
- fprintf(stderr, "Error in socket: %s\n", strerror(errno));
- exit(2);
- }
-
- memset((char *) &sin, 0,sizeof(sin));
- sin.sin_family = AF_INET;
- sin.sin_port = htons(debug_port);
- sin.sin_addr.s_addr = INADDR_ANY;
-
- if ((bind(s, (struct sockaddr *) &sin, sizeof(sin))) < 0) {
- fprintf(stderr, "Error in bind: %s\n", strerror(errno));
- exit(2);
- }
-
- if ((listen(s, 5)) < 0) {
- fprintf(stderr, "Error in listen: %s\n", strerror(errno));
- exit(2);
- }
-
- if ((fd = accept(s, &from, &fromlen)) < 0) {
- fprintf(stderr, "Error in accept: %s\n", strerror(errno));
- exit(2);
- }
-
- close(s);
- } else {
+ if (debug_port)
+ fd = accept_a_connection(debug_port, (struct sockaddr *)&from,
+ &fromlen);
+ else {
if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
fprintf(stderr, "%s: ", progname);
perror("getpeername");
_exit(1);
}
-
+
fd = 0;
}
-
+
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
sizeof (on)) < 0)
- syslog(LOG_WARNING,
- "setsockopt (SO_KEEPALIVE): %m");
+ syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
#if defined(BSD) && BSD+0 >= 43
linger.l_onoff = 1;
linger.l_linger = 60; /* XXX */
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger,
sizeof (linger)) < 0)
- syslog(LOG_WARNING , "setsockopt (SO_LINGER): %m");
+ syslog(LOG_WARNING , "setsockopt (SO_LINGER): %m");
#endif
- doit(dup(fd), &from);
+
+ if (checksum_required&&checksum_ignored) {
+ syslog(LOG_CRIT, "Checksums are required and ignored; these options are mutually exclusive--check the documentation.");
+ fatal(fd, "Configuration error: mutually exclusive options specified");
+ }
+
+ doit(dup(fd), (struct sockaddr *) &from);
+ return 0;
}
#ifdef CRAY
char homedir[64] = "HOME=";
char shell[64] = "SHELL=";
char term[64] = "TERM=network";
-char path[128] = "PATH=";
-char path_rest[64] = RPATH;
+char path_rest[] = RPATH;
-#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, username, term, 0};
-#endif /* CRAY */
-#else /* !KERBEROS */
-char *envinit[] =
+char remote_addr[64+NI_MAXHOST]; /* = "KRB5REMOTEADDR=" */
+char remote_port[64+NI_MAXSERV]; /* = "KRB5REMOTEPORT=" */
+char local_addr[64+NI_MAXHOST]; /* = "KRB5LOCALADDR=" */
+char local_port[64+NI_MAXSERV]; /* = "KRB5LOCALPORT=" */
+#define ADDRPAD 0,0,0,0
+#define KRBPAD 0 /* KRB5CCNAME, optional */
+
+/* The following include extra space for TZ and MAXENV pointers... */
+#define COMMONVARS homedir, shell, 0/*path*/, username, term
#ifdef CRAY
-{homedir, shell, path, username, "TZ=GMT0", tmpdir, term, 0};
-#define TZENV 4
-#define TMPDIRENV 5
+char *envinit[] =
+{COMMONVARS, "TZ=GMT0", tmpdir, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
+#define TMPDIRENV 6
char *getenv();
-extern
-#else
-{homedir, shell, path, username, term, 0};
-#endif /* CRAY */
+#else /* CRAY */
+#ifdef KERBEROS
+char *envinit[] =
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, KRBPAD, ADDRPAD, 0};
+#else /* KERBEROS */
+char *envinit[] =
+{COMMONVARS, 0/*tz*/, SAVEENVPAD, ADDRPAD, 0};
#endif /* KERBEROS */
+#endif /* CRAY */
+
+#define TZENV 5
+#define PATHENV 2
extern char **environ;
char ttyn[12]; /* Line string for wtmp entries */
krb5_principal client;
krb5_authenticator *kdata;
-#ifdef SERVE_V4
-#include <kerberosIV/krb.h>
+#ifdef KRB5_KRB4_COMPAT
AUTH_DAT *v4_kdata;
KTEXT v4_ticket;
#endif
#define KRB5_RECVAUTH_V4 4
#define KRB5_RECVAUTH_V5 5
-doit(f, fromp)
+static krb5_sigtype
+cleanup(signumber)
+ int signumber;
+{
+#ifdef POSIX_SIGNALS
+ struct sigaction sa;
+
+ (void)sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_IGN;
+ (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ (void)kill(-pid, SIGTERM);
+#else
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ killpg(pid, SIGTERM);
+#endif
+ wait(0);
+
+ pty_logwtmp(ttyn,"","");
+ syslog(LOG_INFO ,"Daemon terminated via signal %d.", signumber);
+ if (ccache)
+ krb5_cc_destroy(bsd_context, ccache);
+ exit(0);
+}
+
+
+void doit(f, fromp)
int f;
- struct sockaddr_in *fromp;
+ struct sockaddr *fromp;
{
char *cp;
-
#ifdef KERBEROS
- krb5_address peeraddr;
krb5_error_code status;
#endif
-
- int tmpint;
-
- int ioctlval, cnt;
- char *salt, *ttynm, *tty;
- register char *p;
+ int valid_checksum;
+ int cnt;
char *crypt();
-
-#ifndef CRAY
- struct passwd *pwd;
-#else
struct passwd *pwd;
+ char *path;
+#ifdef CRAY
#ifndef NO_UDB
struct udb *ue;
struct udb ue_static;
long packet_compart; /* Packet compartments */
#endif /* CRAY */
- int s;
- struct hostent *hp;
- char *hostname;
+ int s = -1;
+ char hostname[NI_MAXHOST];
+ char *sane_host;
+ char hostaddra[NI_MAXHOST];
+ int aierr;
short port;
- int pv[2], cc;
- long ready, readfrom;
- char buf[BUFSIZ], sig;
- int one = 1;
- krb5_sigtype cleanup();
- int fd;
- struct sockaddr_in fromaddr;
- int non_privileged = 0;
+ int pv[2], pw[2], px[2], cc;
+ fd_set ready, readfrom;
+ char buf[RCMD_BUFSIZ], sig;
+ struct sockaddr_storage localaddr;
+#ifdef POSIX_SIGNALS
+ struct sigaction sa;
+#endif
#ifdef IP_TOS
/* solaris has IP_TOS, but only IPTOS_* values */
#ifdef HAVE_GETTOSBYNAME
struct tosent *tp;
+
if ((tp = gettosbyname("interactive", "tcp")) &&
(setsockopt(f, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0))
#ifdef TOS_WARN
#endif
#endif /* IP_TOS */
- fromaddr = *fromp;
+ {
+ socklen_t sin_len = sizeof (localaddr);
+ if (getsockname(f, (struct sockaddr*)&localaddr, &sin_len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ }
+#ifdef POSIX_SIGNALS
+ (void)sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = SIG_DFL;
+ (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+#else
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
+#endif
#ifdef DEBUG
{ int t = open("/dev/tty", 2);
if (t >= 0) {
}
}
#endif
- fromp->sin_port = ntohs((u_short)fromp->sin_port);
- if (fromp->sin_family != AF_INET) {
+ if (fromp->sa_family != AF_INET
+#if defined(KRB5_USE_INET6) && defined(KERBEROS)
+ && fromp->sa_family != AF_INET6
+#endif
+ ) {
syslog(LOG_ERR , "malformed from address\n");
exit(1);
}
#ifdef KERBEROS
- krb5_init_ets();
- if ((must_pass_rhosts || must_pass_one)
- && (fromp->sin_port >= IPPORT_RESERVED ||
- fromp->sin_port < IPPORT_RESERVED/2))
- non_privileged = 1;
+ netf = f;
#else
- if (fromp->sin_port >= IPPORT_RESERVED ||
- fromp->sin_port < IPPORT_RESERVED/2) {
- syslog(LOG_ERR , "connection from bad port\n");
- exit(1);
+ {
+ struct sockaddr_in *frompin = sa2sin(fromp);
+ frompin->sin_port = ntohs((u_short)frompin->sin_port);
+ if (frompin->sin_port >= IPPORT_RESERVED ||
+ frompin->sin_port < IPPORT_RESERVED/2) {
+ syslog(LOG_ERR , "connection from bad port\n");
+ exit(1);
+ }
}
#endif /* KERBEROS */
}
(void) alarm(0);
if (port != 0) {
- int lport = IPPORT_RESERVED - 1;
- s = rresvport(&lport);
+ if (anyport) {
+ int addrfamily = fromp->sa_family;
+ s = getport(0, &addrfamily);
+ } else {
+ int lport = IPPORT_RESERVED - 1;
+#ifdef HAVE_RRESVPORT_AF
+ s = rresvport_af(&lport, fromp->sa_family);
+#else
+ s = rresvport(&lport);
+#endif
+ }
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)
- non_privileged = 1;
-#else
+#ifndef KERBEROS
if (port >= IPPORT_RESERVED) {
syslog(LOG_ERR , "2nd port not reserved\n");
exit(1);
}
#endif /* KERBEROS */
- fromp->sin_port = htons((u_short)port);
- if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) {
+ switch (fromp->sa_family) {
+ case AF_INET:
+ sa2sin(fromp)->sin_port = htons((u_short)port);
+ break;
+#ifdef KRB5_USE_INET6
+ case AF_INET6:
+ sa2sin6(fromp)->sin6_port = htons((u_short)port);
+ break;
+#endif
+ }
+ if (connect(s, (struct sockaddr *)fromp, socklen(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);
+ aierr = getnameinfo(fromp, socklen(fromp), hostname, sizeof(hostname),
+ 0, 0, 0);
+ if (aierr) {
+ error("failed to get remote host address: %s", gai_strerror(aierr));
+ exit(1);
}
- else {
- hostname = malloc(strlen((char *)inet_ntoa(fromp->sin_addr)) + 1);
- strcpy(hostname,(char *)inet_ntoa(fromp->sin_addr));
+ aierr = getnameinfo(fromp, socklen(fromp), hostaddra, sizeof(hostaddra),
+ 0, 0, NI_NUMERICHOST);
+ if (aierr) {
+ error("failed to get remote host address: %s", gai_strerror(aierr));
+ exit(1);
}
- peeraddr.addrtype = fromp->sin_family;
- peeraddr.length = SIZEOF_INADDR;
- peeraddr.contents = (krb5_octet *)&fromp->sin_addr;
-
#ifdef KERBEROS
- if (status = recvauth(f, fromaddr, peeraddr)) {
+ status = pty_make_sane_hostname((struct sockaddr *) fromp, maxhostlen,
+ stripdomain, always_ip, &sane_host);
+ if (status) {
+ error("failed make_sane_hostname: %s\n", error_message(status));
+ exit(1);
+ }
+
+ if ((status = recvauth(f, fromp, &valid_checksum))) {
error("Authentication failed: %s\n", error_message(status));
exit(1);
}
getstr(f, remuser, sizeof(remuser), "remuser");
getstr(f, locuser, sizeof(locuser), "locuser");
getstr(f, cmdbuf, sizeof(cmdbuf), "command");
+ rcmd_stream_init_normal();
#endif /* KERBEROS */
#ifdef 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);
+ "Principal %s (%s@%s (%s)) for local user %s has no account.\n",
+ kremuser, remuser, hostaddra, hostname,
+ locuser); /* xxx sprintf buffer in syslog*/
error("Login incorrect.\n");
exit(1);
}
endudb();
if (secflag) {
if(getsysv(&sysv, sizeof(struct sysv)) != 0) {
- loglogin(hostname, SLG_LLERR, 0, ue);
+ loglogin(sane_host, 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);
+ loglogin(sane_host, SLG_LLERR, 0, ue);
error("Permission denied.\n");
exit(1);
}
if (ue->ue_disabled != 0) {
- loglogin(hostname,SLG_LOCK,ue->ue_logfails,ue);
+ loglogin(sane_host,SLG_LOCK,ue->ue_logfails,ue);
error("Permission denied.\n");
exit(1);
}
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
+ sprintf(ttyn,"krsh%ld",(long) (getpid() % 9999999));
+ pty_logwtmp(ttyn,locuser,sane_host);
}
/* 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
+ pty_logwtmp(ttyn,locuser,sane_host);
}
#ifdef CRAY
if (getusrv(&usrv)){
syslog(LOG_ERR,"Cannot getusrv");
error("Permission denied.\n");
- loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+ loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
goto signout_please;
}
/*
if((ue->ue_valcat & TFM_TRUSTED) ||
(sysv.sy_oldtfm &&
((ue->ue_comparts & TRUSTED_SUBJECT) == TRUSTED_SUBJECT))) {
- loglogin(hostname, SLG_TRSUB, ue->ue_logfails,ue);
+ loglogin(sane_host, SLG_TRSUB, ue->ue_logfails,ue);
error("Permission denied.\n");
goto signout_please;
}
- loglogin(hostname, SLG_OKLOG, ue->ue_logfails,ue);
+ loglogin(sane_host, SLG_OKLOG, ue->ue_logfails,ue);
/* Setup usrv structure with user udb info and
packet_level and packet_compart. */
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)
+ * 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;
- }
+ 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);
+ loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
error("Permission denied.\n");
goto signout_please;
}
sethost(paddr);
if (setusrv(&usrv) == -1) {
- loglogin(hostname, SLG_LVERR, ue->ue_logfails,ue);
+ loglogin(sane_host, SLG_LVERR, ue->ue_logfails,ue);
error("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");
+ if(chdir("/") < 0) {
+ error("No remote directory.\n");
goto signout_please;
+ }
+ pwd->pw_dir = "/";
}
#ifdef KERBEROS
- if (must_pass_k5 || must_pass_one) {
-#if (defined(ALWAYS_V5_KUSEROK) || !defined(KRB5_KRB4_COMPAT))
- if (!krb5_kuserok(client,locuser)) {
- syslog(LOG_ERR ,
- "Principal %s (%s@%s) for local user %s failed krb5_kuserok.\n",
- kremuser, remuser, hostname, locuser);
- if (must_pass_k5) {
- error("Permission denied.\n");
- goto signout_please;
- }
- failed_k5 = 1;
- }
-#else
+
+#if defined(KRB5_KRB4_COMPAT) && !defined(ALWAYS_V5_KUSEROK)
if (auth_sys == KRB5_RECVAUTH_V4) {
/* kuserok returns 0 if OK */
if (kuserok(v4_kdata, locuser)){
syslog(LOG_ERR ,
- "Principal %s (%s@%s) for local user %s failed kuserok.\n",
- kremuser, remuser, hostname, locuser);
- if (must_pass_k5) {
- error("Permission denied.\n");
- goto signout_please;
+ "Principal %s (%s@%s (%s)) for local user %s failed kuserok.\n",
+ kremuser, remuser, hostaddra, hostname, locuser);
}
- failed_k5 = 1;
- }
- }
- else {
+ else auth_sent |= AUTH_KRB4;
+ } else
+#endif
+ {
/* krb5_kuserok returns 1 if OK */
- if (!krb5_kuserok(client, locuser)){
+ if (!krb5_kuserok(bsd_context, client, locuser)){
syslog(LOG_ERR ,
- "Principal %s (%s@%s) for local user %s failed krb5_kuserok.\n",
- kremuser, remuser, hostname, locuser);
- if (must_pass_k5) {
- error("Permission denied.\n");
- goto signout_please;
- }
- failed_k5 = 1;
+ "Principal %s (%s@%s (%s)) for local user %s failed krb5_kuserok.\n",
+ kremuser, remuser, hostaddra, hostname, locuser);
}
- }
-#endif
- }
-
- if (must_pass_rhosts || (failed_k5 && must_pass_one)) {
- /* Cannot check .rhosts unless connection from privileged port */
- if (non_privileged) {
- syslog(LOG_ERR , "connection from bad port\n");
- exit(1);
+ else
+ auth_sent |=
+ ((auth_sys == KRB5_RECVAUTH_V4) ? AUTH_KRB4 : AUTH_KRB5);
}
- 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) {
+ ruserok(hostname[0] ? hostname : hostaddra,
+ pwd->pw_uid == 0, remuser, locuser) < 0) {
error("Permission denied.\n");
goto signout_please;
}
#endif /* KERBEROS */
+
+
+ if (checksum_required && !valid_checksum) {
+ if (auth_sent & AUTH_KRB5) {
+ syslog(LOG_WARNING, "Client did not supply required checksum--connection rejected.");
+ error( "You are using an old Kerberos5 client without checksum support; only newer clients are authorized.\n");
+ goto signout_please;
+ } else {
+ syslog(LOG_WARNING,
+ "Configuration error: Requiring checksums with -c is inconsistent with allowing Kerberos V4 connections.");
+ }
+ }
+ if (require_encrypt&&(!do_encrypt)) {
+ error("You must use encryption.\n");
+ goto signout_please;
+ }
+ if (!(auth_ok&auth_sent)) {
+ if (auth_sent)
+ error("Another authentication mechanism must be used to access this host.\n");
+ else
+ error("Permission denied.\n");
+ goto signout_please;
+ }
- if (pwd->pw_uid && !access("/etc/nologin", F_OK)) {
+ if (pwd->pw_uid && !access(NOLOGIN, F_OK)) {
error("Logins currently disabled.\n");
goto signout_please;
}
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);
+ syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s (%s)) as ROOT",
+ cmdbuf, kremuser, remuser, hostaddra, hostname);
#else
- syslog(LOG_NOTICE ,"Access as ROOT by principal %s (%s@%s)",
- kremuser, remuser, hostname);
+ syslog(LOG_NOTICE ,"Access as ROOT by principal %s (%s@%s (%s))",
+ kremuser, remuser, hostaddra, hostname);
#endif
}
#if defined(KERBEROS) && defined(LOG_REMOTE_REALM) && !defined(LOG_OTHER_USERS) && !defined(LOG_ALL_LOGINS)
#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);
+ syslog(LOG_NOTICE, "Executing %s for principal %s (%s@%s (%s)) as local user %s",
+ cmdbuf, kremuser, remuser, hostaddra, hostname, locuser);
#else
- syslog(LOG_NOTICE ,"Access as %s by principal %s (%s@%s)",
- locuser, kremuser, remuser, hostname);
+ syslog(LOG_NOTICE ,"Access as %s by principal %s (%s@%s (%s))",
+ locuser, kremuser, remuser, hostaddra, hostname);
#endif
}
#endif
- (void) write(2, "\0", 1);
+ (void) write(2, "", 1);
- if (port) {
- if (pipe(pv) < 0) {
+ if (port||do_encrypt) {
+ if (port&&(pipe(pv) < 0)) {
error("Can't make pipe.\n");
goto signout_please;
}
+ if (pipe(pw) < 0) {
+ error("Can't make pipe 2.\n");
+ goto signout_please;
+ }
+ if (pipe(px) < 0) {
+ error("Can't make pipe 3.\n");
+ goto signout_please;
+ }
pid = fork();
if (pid == -1) {
error("Fork failed.\n");
goto signout_please;
}
if (pid) {
+#ifdef POSIX_SIGNALS
+ sa.sa_handler = cleanup;
+ (void)sigaction(SIGINT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGQUIT, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGTERM, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGHUP, &sa, (struct sigaction *)0);
+
+ sa.sa_handler = SIG_IGN;
+ /* SIGPIPE is a crutch that we don't need if we check
+ the exit status of write. */
+ (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
+ (void)sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+#else
signal(SIGINT, cleanup);
signal(SIGQUIT, cleanup);
signal(SIGTERM, cleanup);
- signal(SIGPIPE, cleanup);
signal(SIGHUP, cleanup);
+ /* SIGPIPE is a crutch that we don't need if we check
+ the exit status of write. */
+ signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD,SIG_IGN);
+#endif
(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! */
+ if(port)
+ (void) close(pv[1]);
+ (void) close(pw[1]);
+ (void) close(px[0]);
+
+
+
+ FD_ZERO(&readfrom);
+ FD_SET(f, &readfrom);
+ if(port) {
+ FD_SET(s, &readfrom);
+ FD_SET(pv[0], &readfrom);
+ }
+ FD_SET(pw[0], &readfrom);
+
+ /* read from f, write to px[1] -- child stdin */
+ /* read from s, signal child */
+ /* read from pv[0], write to s -- child stderr */
+ /* read from pw[0], write to f -- child stdout */
+
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 {
+ if (select(8*sizeof(ready), &ready, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (port&&FD_ISSET(pv[0], &ready)) {
+ /* read from the child stderr, write to the net */
+ errno = 0;
+ cc = read(pv[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ shutdown(s, 1+1);
+ FD_CLR(pv[0], &readfrom);
+ } else {
+ (void) rcmd_stream_write(s, buf, (unsigned) cc, 1);
+ }
+ }
+ if (FD_ISSET(pw[0], &ready)) {
+ /* read from the child stdout, write to the net */
+ errno = 0;
+ cc = read(pw[0], buf, sizeof (buf));
+ if (cc <= 0) {
+ shutdown(f, 1+1);
+ FD_CLR(pw[0], &readfrom);
+ } else {
+ (void) rcmd_stream_write(f, buf, (unsigned) cc, 0);
+ }
+ }
+ if (port&&FD_ISSET(s, &ready)) {
+ /* read from the alternate channel, signal the child */
+ if (rcmd_stream_read(s, &sig, 1, 1) <= 0) {
+ FD_CLR(s, &readfrom);
+ } else {
+#ifdef POSIX_SIGNALS
+ sa.sa_handler = cleanup;
+ (void)sigaction(sig, &sa, (struct sigaction *)0);
+ kill(-pid, sig);
+#else
signal(sig, cleanup);
killpg(pid, sig);
+#endif
}
}
- if (ready & (1L<<pv[0])) {
+ if (FD_ISSET(f, &ready)) {
+ /* read from the net, write to child stdin */
errno = 0;
- cc = read(pv[0], buf, sizeof (buf));
+ cc = rcmd_stream_read(f, buf, sizeof(buf), 0);
if (cc <= 0) {
- shutdown(s, 1+1);
- readfrom &= ~(1L<<pv[0]);
- } else
- (void) write(s, buf, cc);
+ (void) close(px[1]);
+ FD_CLR(f, &readfrom);
+ } else {
+ int wcc;
+ wcc = write(px[1], buf, (unsigned) cc);
+ if (wcc == -1) {
+ /* pipe closed, don't read any more */
+ /* might check for EPIPE */
+ (void) close(px[1]);
+ FD_CLR(f, &readfrom);
+ } else if (wcc != cc) {
+ syslog(LOG_INFO, "only wrote %d/%d to child",
+ wcc, cc);
+ }
}
- } while (readfrom);
+ }
+ } while ((port&&FD_ISSET(s, &readfrom)) ||
+ FD_ISSET(f, &readfrom) ||
+ (port&&FD_ISSET(pv[0], &readfrom) )||
+ FD_ISSET(pw[0], &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
+ pty_logwtmp(ttyn,"","");
+ if (ccache)
+ krb5_cc_destroy(bsd_context, ccache);
exit(0);
}
+#if defined(HAVE_SETSID)&&(!defined(ULTRIX))
+ setsid();
+#else
+#ifdef SETPGRP_TWOARG
setpgrp(0, getpid());
- (void) close(s); (void) close(pv[0]);
- dup2(pv[1], 2);
- (void) close(pv[1]);
+#else
+ setpgrp();
+#endif /*setpgrp_twoarg*/
+#endif /*HAVE_SETSID*/
+ (void) close(s);
+ (void) close(f);
+ (void) close(pw[0]);
+ if (port)
+ (void) close(pv[0]);
+ (void) close(px[1]);
+
+ (void) dup2(px[0], 0);
+ (void) dup2(pw[1], 1);
+ if(port)
+ (void) dup2(pv[1], 2);
+ else dup2(pw[1], 2);
+
+ (void) close(px[0]);
+ (void) close(pw[1]);
+ if(port)
+ (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
+ pty_logwtmp(ttyn,"","");
}
if (*pwd->pw_shell == '\0')
(void) close(f);
(void) setgid((gid_t)pwd->pw_gid);
#ifndef sgi
- initgroups(pwd->pw_name, pwd->pw_gid);
+ if (getuid() == 0 || getuid() != pwd->pw_uid) {
+ /* For testing purposes, we don't call initgroups if we
+ already have the right uid, and it is not root. This is
+ because on some systems initgroups outputs an error message
+ if not called by root. */
+ initgroups(pwd->pw_name, pwd->pw_gid);
+ }
#endif
+#ifdef HAVE_SETLUID
+ /*
+ * If we're on a system which keeps track of login uids, then
+ * set the login uid.
+ */
+ setluid((uid_t) pwd->pw_uid);
+#endif /* HAVE_SETLUID */
(void) setuid((uid_t)pwd->pw_uid);
- environ = envinit;
+ /* if TZ is set in the parent, drag it in */
+ {
+ char **findtz = environ;
+ while(*findtz) {
+ if(!strncmp(*findtz,"TZ=",3)) {
+ envinit[TZENV] = *findtz;
+ break;
+ }
+ findtz++;
+ }
+ }
strncat(homedir, pwd->pw_dir, sizeof(homedir)-6);
strncat(shell, pwd->pw_shell, sizeof(shell)-7);
strncat(username, pwd->pw_name, sizeof(username)-6);
- strcat(path, KPROGDIR);
- strcat(path, ":");
- strcat(path, path_rest);
+ path = (char *) malloc(strlen(kprogdir) + strlen(path_rest) + 7);
+ if (path == NULL) {
+ perror("malloc");
+ _exit(1);
+ }
+ sprintf(path, "PATH=%s:%s", kprogdir, path_rest);
+ envinit[PATHENV] = path;
+
+ /* If we have KRB5CCNAME set, then copy into the
+ * child's environment. This can't really have
+ * a fixed position because tz may or may not be set.
+ */
+ if (getenv("KRB5CCNAME")) {
+ int i;
+ char *buf2 = (char *)malloc(strlen(getenv("KRB5CCNAME"))
+ +strlen("KRB5CCNAME=")+1);
+ if (buf2) {
+ sprintf(buf2, "KRB5CCNAME=%s",getenv("KRB5CCNAME"));
+
+ for (i = 0; envinit[i]; i++);
+ envinit[i] = buf2;
+ }
+ }
+
+ {
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ int i;
+ /* these four are covered by ADDRPAD */
+
+ for (i = 0; envinit[i]; i++);
+
+ aierr = getnameinfo((struct sockaddr *)&localaddr,
+ socklen((struct sockaddr *)&localaddr),
+ hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (aierr)
+ goto skip_localaddr_env;
+ sprintf(local_addr, "KRB5LOCALADDR=%s", hbuf);
+ envinit[i++] =local_addr;
+
+ sprintf(local_port, "KRB5LOCALPORT=%s", sbuf);
+ envinit[i++] =local_port;
+ skip_localaddr_env:
+
+ aierr = getnameinfo(fromp, socklen(fromp),
+ hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (aierr)
+ goto skip_remoteaddr_env;
+ sprintf(remote_addr, "KRB5REMOTEADDR=%s", hbuf);
+ envinit[i++] =remote_addr;
+
+ sprintf(remote_port, "KRB5REMOTEPORT=%s", sbuf);
+ envinit[i++] =remote_port;
+
+ skip_remoteaddr_env:
+ ;
+ }
+
+ /* If we do anything else, make sure there is space in the array. */
+
+ for(cnt=0; cnt < num_env; cnt++) {
+ int i;
+ char *buf2;
+
+ if(getenv(save_env[cnt])) {
+ buf2 = (char *)malloc(strlen(getenv(save_env[cnt]))
+ +strlen(save_env[cnt]+2));
+ if (buf2) {
+ sprintf(buf2, "%s=%s", save_env[cnt],
+ getenv(save_env[cnt]));
+ for (i = 0; envinit[i]; i++);
+ envinit[i] = buf2;
+ }
+ }
+ }
+
+ /* XXX - If we do anything else, make sure there is space in the array. */
+
+ environ = envinit;
+
+#ifdef KERBEROS
+ /* To make Kerberos rcp work correctly, we must ensure that we
+ invoke Kerberos rcp on this end, not normal rcp, even if the
+ shell startup files change PATH. */
+ if (!strncmp(cmdbuf, "rcp ", 4) ||
+ (do_encrypt && !strncmp(cmdbuf, "-x rcp ", 7))) {
+ char *copy;
+ struct stat s2;
+ int offst = 0;
+
+ copy = malloc(strlen(cmdbuf) + 1);
+ if (copy == NULL) {
+ perror("malloc");
+ _exit(1);
+ }
+ strcpy(copy, cmdbuf);
+ if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
+ offst = 3;
+ }
+
+ strcpy((char *) cmdbuf + offst, kprogdir);
+ cp = copy + 3 + offst;
+
+ cmdbuf[sizeof(cmdbuf) - 1] = '\0';
+ if (auth_sys == KRB5_RECVAUTH_V4) {
+ strncat(cmdbuf, "/v4rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+ } else {
+ strncat(cmdbuf, "/rcp", sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+ }
+ if (stat((char *)cmdbuf + offst, &s2) >= 0)
+ strncat(cmdbuf, cp, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+ else
+ strncpy(cmdbuf, copy, sizeof(cmdbuf) - 1 - strlen(cmdbuf));
+ free(copy);
+ }
+#endif
+
cp = strrchr(pwd->pw_shell, '/');
if (cp)
cp++;
else
cp = pwd->pw_shell;
- execl(pwd->pw_shell, cp, "-c", cmdbuf, 0);
+ if (do_encrypt && !strncmp(cmdbuf, "-x ", 3)) {
+ execl(pwd->pw_shell, cp, "-c", (char *)cmdbuf + 3, 0);
+ }
+ else {
+ 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
+ if (ccache)
+ krb5_cc_destroy(bsd_context, ccache);
+ ccache = NULL;
+ pty_logwtmp(ttyn,"","");
exit(1);
}
-
+void
+#ifdef HAVE_STDARG_H
+error(char *fmt, ...)
+#else
/*VARARGS1*/
-error(fmt, a1, a2, a3)
+error(fmt, va_alist)
char *fmt;
- char *a1, *a2, *a3;
+ va_dcl
+#endif
{
- char buf[BUFSIZ];
+ va_list ap;
+ char buf[RCMD_BUFSIZ], *cp = buf;
- buf[0] = 1;
- (void) sprintf(buf+1, "%s: ", progname);
- (void) sprintf(buf+strlen(buf), fmt, a1, a2, a3);
+#ifdef HAVE_STDARG_H
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+
+ *cp++ = 1;
+ (void) sprintf(cp, "%s: ", progname);
+ (void) vsprintf(buf+strlen(buf), fmt, ap);
+ va_end(ap);
(void) write(2, buf, strlen(buf));
syslog(LOG_ERR ,"%s",buf+1);
}
-
-getstr(fd, buf, cnt, err)
- char *buf;
- int cnt;
- char *err;
+void getstr(fd, buf, cnt, err)
+ int fd;
+ char *buf;
+ int cnt;
+ char *err;
{
char c;
} 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;
return;
}
-#endif CRAY
+#endif /* CRAY */
-usage()
+void usage()
{
#ifdef KERBEROS
- syslog(LOG_ERR, "usage: kshd [-rRkK] or [r/R][k/K]shd");
+ syslog(LOG_ERR, "usage: kshd [-54ecikK] ");
#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;
-}
-
#ifdef KERBEROS
#ifndef KRB_SENDAUTH_VLEN
#define KRB_SENDAUTH_VERS "AUTHV0.1" /* MUST be KRB_SENDAUTH_VLEN
chars */
-krb5_error_code
-recvauth(netf, peersin, peeraddr)
- int netf;
- struct sockaddr_in peersin;
- krb5_address peeraddr;
+static krb5_error_code
+recvauth(netfd, peersin, valid_checksum)
+ int netfd;
+ struct sockaddr *peersin;
+ int *valid_checksum;
{
+ krb5_auth_context auth_context = NULL;
krb5_error_code status;
struct sockaddr_in laddr;
- char krb_vers[KRB_SENDAUTH_VLEN + 1];
- int len;
- krb5_principal server;
+ socklen_t len;
krb5_data inbuf;
+#ifdef KRB5_KRB4_COMPAT
char v4_instance[INST_SZ]; /* V4 Instance */
- char v4_version[9];
+#endif
+ krb5_authenticator *authenticator;
krb5_ticket *ticket;
+ krb5_rcache rcache;
+ struct passwd *pwd;
+ uid_t uid;
+ gid_t gid;
+ enum kcmd_proto kcmd_proto;
+ krb5_data version;
+ *valid_checksum = 0;
len = sizeof(laddr);
- if (getsockname(netf, (struct sockaddr *)&laddr, &len)) {
+ if (getsockname(netfd, (struct sockaddr *)&laddr, &len)) {
exit(1);
}
#define SIZEOF_INADDR sizeof(struct in_addr)
#endif
- if (status = krb5_sname_to_principal(NULL, "host", KRB5_NT_SRV_HST,
- &server)) {
- syslog(LOG_ERR, "parse server name %s: %s", "host",
- error_message(status));
- exit(1);
- }
-
+#ifdef KRB5_KRB4_COMPAT
strcpy(v4_instance, "*");
+#endif
+
+ status = krb5_auth_con_init(bsd_context, &auth_context);
+ if (status)
+ return status;
+
+ status = krb5_auth_con_genaddrs(bsd_context, auth_context, netfd,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
+ if (status)
+ return status;
+
+ status = krb5_auth_con_getrcache(bsd_context, auth_context, &rcache);
+ if (status) return status;
+
+ if (! rcache) {
+ krb5_principal server;
+
+ status = krb5_sname_to_principal(bsd_context, 0, 0,
+ KRB5_NT_SRV_HST, &server);
+ if (status) return status;
- status = krb5_compat_recvauth(&netf,
- "KCMDV0.1",
- server, /* Specify daemon principal */
- &peeraddr, /* We do want to match */
- /* this against caddrs in */
- /* the ticket */
- 0, /* use v5srvtab */
- 0, /* no keyproc */
- 0, /* no keyprocarg */
- 0, /* default rc_type */
- 0, /* no flags */
-
- 0, /*v4_opts*/
- "rcmd", /* v4_service */
- v4_instance, /* v4_instance */
- &peersin, /* foriegn address */
- &laddr, /* our local address */
- "", /* use default srvtab */
-
- &auth_sys, /* which authentication system */
- 0, /* no seq number */
- &client, /* return client */
- &ticket, /* return ticket */
- &kdata, /* return authenticator */
-
- &v4_kdata, 0, v4_version);
+ status = krb5_get_server_rcache(bsd_context,
+ krb5_princ_component(bsd_context, server, 0),
+ &rcache);
+ krb5_free_principal(bsd_context, server);
+ if (status) return status;
+ status = krb5_auth_con_setrcache(bsd_context, auth_context, rcache);
+ if (status) return status;
+ }
+
+#ifdef KRB5_KRB4_COMPAT
+ status = krb5_compat_recvauth_version(bsd_context, &auth_context, &netfd,
+ NULL, /* Specify daemon principal */
+ 0, /* no flags */
+ keytab, /* normally NULL to use v5srvtab */
+ 0, /* v4_opts */
+ "rcmd", /* v4_service */
+ v4_instance, /* v4_instance */
+ (struct sockaddr_in *)peersin, /* foreign address */
+ &laddr, /* our local address */
+ "", /* use default srvtab */
+
+ &ticket, /* return ticket */
+ &auth_sys, /* which authentication system*/
+ &v4_kdata, 0, &version);
+#else
+ status = krb5_recvauth_version(bsd_context, &auth_context, &netfd,
+ NULL, /* daemon principal */
+ 0, /* no flags */
+ keytab, /* normally NULL to use v5srvtab */
+ &ticket, /* return ticket */
+ &version); /* application version string */
+ auth_sys = KRB5_RECVAUTH_V5;
+#endif
if (status) {
if (auth_sys == KRB5_RECVAUTH_V5) {
/*
* clean up before exiting
*/
- getstr(netf, locuser, sizeof(locuser), "locuser");
- getstr(netf, cmdbuf, sizeof(cmdbuf), "command");
- getstr(netf, remuser, sizeof(locuser), "remuser");
+ getstr(netfd, locuser, sizeof(locuser), "locuser");
+ getstr(netfd, cmdbuf, sizeof(cmdbuf), "command");
+ getstr(netfd, remuser, sizeof(locuser), "remuser");
}
return status;
}
- getstr(netf, locuser, sizeof(locuser), "locuser");
- getstr(netf, cmdbuf, sizeof(cmdbuf), "command");
+ getstr(netfd, locuser, sizeof(locuser), "locuser");
+ getstr(netfd, cmdbuf, sizeof(cmdbuf), "command");
+#ifdef KRB5_KRB4_COMPAT
if (auth_sys == KRB5_RECVAUTH_V4) {
+ rcmd_stream_init_normal();
+
/* We do not really know the remote user's login name.
* Assume it to be the same as the first component of the
* principal's name.
*/
strcpy(remuser, v4_kdata->pname);
- kremuser = (char *) malloc(strlen(v4_kdata->pname) + 1 +
- strlen(v4_kdata->pinst) + 1 +
- strlen(v4_kdata->prealm) + 1);
- sprintf(kremuser, "%s/%s@%s", v4_kdata->pname,
- v4_kdata->pinst, v4_kdata->prealm);
+
+ status = krb5_425_conv_principal(bsd_context, v4_kdata->pname,
+ v4_kdata->pinst, v4_kdata->prealm,
+ &client);
+ if (status) return status;
+
+ status = krb5_unparse_name(bsd_context, client, &kremuser);
- if (status = krb5_parse_name(kremuser, &client))
- return(status);
- return 0;
+ return status;
}
+#endif /* KRB5_KRB4_COMPAT */
/* Must be V5 */
- getstr(netf, remuser, sizeof(locuser), "remuser");
-
- if (status = krb5_unparse_name(client, &kremuser))
+ kcmd_proto = KCMD_UNKNOWN_PROTOCOL;
+ if (version.length != 9)
+ fatal (netfd, "bad application version length");
+ if (!memcmp (version.data, "KCMDV0.1", 9))
+ kcmd_proto = KCMD_OLD_PROTOCOL;
+ if (!memcmp (version.data, "KCMDV0.2", 9))
+ kcmd_proto = KCMD_NEW_PROTOCOL;
+
+ getstr(netfd, remuser, sizeof(locuser), "remuser");
+
+ if ((status = krb5_unparse_name(bsd_context, ticket->enc_part2->client,
+ &kremuser)))
+ return status;
+
+ if ((status = krb5_copy_principal(bsd_context, ticket->enc_part2->client,
+ &client)))
return status;
+ if ((status = krb5_auth_con_getauthenticator(bsd_context, auth_context,
+ &authenticator)))
+ return status;
- if (status = krb5_read_message((krb5_pointer)&netf, &inbuf)) {
- error("Error reading message: %s\n",
- error_message(status));
+ if (authenticator->checksum && !checksum_ignored) {
+ struct sockaddr_storage adr;
+ unsigned int adr_length = sizeof(adr);
+ int e;
+ unsigned int buflen = strlen(cmdbuf)+strlen(locuser)+32;
+ char * chksumbuf = (char *) malloc(buflen);
+
+ if (chksumbuf == 0)
+ goto error_cleanup;
+ if (getsockname(netfd, (struct sockaddr *) &adr, &adr_length) != 0)
+ goto error_cleanup;
+
+ e = getnameinfo((struct sockaddr *)&adr, adr_length, 0, 0,
+ chksumbuf, buflen, NI_NUMERICSERV);
+ if (e) {
+ free(chksumbuf);
+ fatal(netfd, "local error: can't examine port number");
+ }
+ if (strlen(chksumbuf) > 30) {
+ free(chksumbuf);
+ fatal(netfd, "wacky local port number?!");
+ }
+ strcat(chksumbuf, ":");
+ strcat(chksumbuf,cmdbuf);
+ strcat(chksumbuf,locuser);
+
+ status = krb5_verify_checksum(bsd_context,
+ authenticator->checksum->checksum_type,
+ authenticator->checksum,
+ chksumbuf, strlen(chksumbuf),
+ ticket->enc_part2->session->contents,
+ ticket->enc_part2->session->length);
+
+ error_cleanup:
+ if (chksumbuf)
+ free(chksumbuf);
+ if (status) {
+ krb5_free_authenticator(bsd_context, authenticator);
+ return status;
+ }
+ *valid_checksum = 1;
+ }
+ krb5_free_authenticator(bsd_context, authenticator);
+
+
+ if (!strncmp(cmdbuf, "-x ", 3))
+ do_encrypt = 1;
+
+ {
+ krb5_keyblock *key;
+ status = krb5_auth_con_getrecvsubkey (bsd_context, auth_context,
+ &key);
+ if (status)
+ fatal (netfd, "Server can't get session subkey");
+ if (!key && do_encrypt && kcmd_proto == KCMD_NEW_PROTOCOL)
+ fatal (netfd, "No session subkey sent");
+ if (key && kcmd_proto == KCMD_OLD_PROTOCOL) {
+#ifdef HEIMDAL_FRIENDLY
+ key = 0;
+#else
+ fatal (netfd, "Session subkey not allowed in old kcmd protocol");
+#endif
+ }
+ if (key == 0)
+ key = ticket->enc_part2->session;
+ rcmd_stream_init_krb5 (key, do_encrypt, 0, 0, kcmd_proto);
+ }
+
+ /* Null out the "session" because kcmd.c references the session
+ * key here, and we do not want krb5_free_ticket() to destroy it. */
+ ticket->enc_part2->session = 0;
+
+ if ((status = krb5_read_message(bsd_context, (krb5_pointer)&netfd,
+ &inbuf))) {
+ error("Error reading message: %s\n", error_message(status));
exit(1);
}
if (inbuf.length) { /* Forwarding being done, read creds */
- if (status = rd_and_store_for_creds(&inbuf, ticket, locuser)) {
+ pwd = getpwnam(locuser);
+ if (!pwd) {
+ error("Login incorrect.\n");
+ exit(1);
+ }
+ uid = pwd->pw_uid;
+ gid = pwd->pw_gid;
+ if ((status = rd_and_store_for_creds(bsd_context, auth_context, &inbuf,
+ ticket, &ccache))) {
error("Can't get forwarded credentials: %s\n",
error_message(status));
exit(1);
}
+ if (chown(krb5_cc_get_name(bsd_context, ccache), uid, gid) == -1) {
+ error("Can't chown forwarded credentials: %s\n",
+ error_message(errno));
+ exit(1);
+ }
}
- krb5_free_ticket(ticket);
+ krb5_free_ticket(bsd_context, ticket);
return 0;
}
-
#endif /* KERBEROS */
+
+
+
+void fatal(f, msg)
+ int f;
+ const char *msg;
+{
+ char buf[512];
+#ifndef POSIX_TERMIOS
+ int out = 1 ; /* Output queue of f */
+#endif
+
+ buf[0] = '\01'; /* error indicator */
+ (void) sprintf(buf + 1, "%s: %s.\r\n",progname, msg);
+ if ((f == netf) && (pid > 0))
+ (void) rcmd_stream_write(f, buf, strlen(buf), 0);
+ else
+ (void) write(f, buf, strlen(buf));
+ syslog(LOG_ERR,"%s\n",msg);
+ if (pid > 0) {
+ signal(SIGCHLD,SIG_IGN);
+ kill(pid,SIGKILL);
+#ifdef POSIX_TERMIOS
+ (void) tcflush(1, TCOFLUSH);
+#else
+ (void) ioctl(f, TIOCFLUSH, (char *)&out);
+#endif
+ cleanup(-1);
+ }
+ exit(1);
+}