freebsd-dev/crypto/heimdal/appl/rsh/rshd.c
Stanislav Sedov ae77177087 - Update FreeBSD Heimdal distribution to version 1.5.1. This also brings
several new kerberos related libraries and applications to FreeBSD:
  o kgetcred(1) allows one to manually get a ticket for a particular service.
  o kf(1) securily forwards ticket to another host through an authenticated
    and encrypted stream.
  o kcc(1) is an umbrella program around klist(1), kswitch(1), kgetcred(1)
    and other user kerberos operations. klist and kswitch are just symlinks
    to kcc(1) now.
  o kswitch(1) allows you to easily switch between kerberos credentials if
    you're running KCM.
  o hxtool(1) is a certificate management tool to use with PKINIT.
  o string2key(1) maps a password into key.
  o kdigest(8) is a userland tool to access the KDC's digest interface.
  o kimpersonate(8) creates a "fake" ticket for a service.

  We also now install manpages for some lirbaries that were not installed
  before, libheimntlm and libhx509.

- The new HEIMDAL version no longer supports Kerberos 4.  All users are
  recommended to switch to Kerberos 5.

- Weak ciphers are now disabled by default.  To enable DES support (used
  by telnet(8)), use "allow_weak_crypto" option in krb5.conf.

- libtelnet, pam_ksu and pam_krb5 are now compiled with error on warnings
  disabled due to the function they use (krb5_get_err_text(3)) being
  deprecated.  I plan to work on this next.

- Heimdal's KDC now require sqlite to operate.  We use the bundled version
  and install it as libheimsqlite.  If some other FreeBSD components will
  require it in the future we can rename it to libbsdsqlite and use for these
  components as well.

- This is not a latest Heimdal version, the new one was released while I was
  working on the update.  I will update it to 1.5.2 soon, as it fixes some
  important bugs and security issues.
2012-03-22 08:48:42 +00:00

980 lines
24 KiB
C

/*
* Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "rsh_locl.h"
#include "login_locl.h"
RCSID("$Id$");
int
login_access( struct passwd *user, char *from);
int
read_limits_conf(const char *file, const struct passwd *pwd);
#ifdef NEED_IRUSEROK_PROTO
int iruserok(uint32_t, int, const char *, const char *);
#endif
enum auth_method auth_method;
#ifdef KRB5
krb5_context context;
krb5_keyblock *keyblock;
krb5_crypto crypto;
#endif
#ifdef KRB5
krb5_ccache ccache, ccache2;
int kerberos_status = 0;
#endif
int do_encrypt = 0;
static int do_unique_tkfile = 0;
static char tkfile[MAXPATHLEN] = "";
static int do_inetd = 1;
static char *port_str;
static int do_rhosts = 1;
static int do_kerberos = 0;
#define DO_KRB5 4
static int do_vacuous = 0;
static int do_log = 1;
static int do_newpag = 1;
static int do_addr_verify = 0;
static int do_keepalive = 1;
static int do_version;
static int do_help = 0;
static void
syslog_and_die (const char *m, ...)
__attribute__ ((format (printf, 1, 2)));
static void
syslog_and_die (const char *m, ...)
{
va_list args;
va_start(args, m);
vsyslog (LOG_ERR, m, args);
va_end(args);
exit (1);
}
static void
fatal (int, const char*, const char *, ...)
__attribute__ ((noreturn, format (printf, 3, 4)));
static void
fatal (int sock, const char *what, const char *m, ...)
{
va_list args;
char buf[BUFSIZ];
size_t len;
*buf = 1;
va_start(args, m);
len = vsnprintf (buf + 1, sizeof(buf) - 1, m, args);
len = min(len, sizeof(buf) - 1);
va_end(args);
if(what != NULL)
syslog (LOG_ERR, "%s: %s: %s", what, strerror(errno), buf + 1);
else
syslog (LOG_ERR, "%s", buf + 1);
net_write (sock, buf, len + 1);
exit (1);
}
static char *
read_str (int s, size_t sz, char *expl)
{
char *str = malloc(sz);
char *p = str;
if(str == NULL)
fatal(s, NULL, "%s too long", expl);
while(p < str + sz) {
if(net_read(s, p, 1) != 1)
syslog_and_die("read: %s", strerror(errno));
if(*p == '\0')
return str;
p++;
}
fatal(s, NULL, "%s too long", expl);
}
static int
recv_bsd_auth (int s, u_char *buf,
struct sockaddr_in *thisaddr,
struct sockaddr_in *thataddr,
char **client_username,
char **server_username,
char **cmd)
{
struct passwd *pwd;
*client_username = read_str (s, USERNAME_SZ, "local username");
*server_username = read_str (s, USERNAME_SZ, "remote username");
*cmd = read_str (s, ARG_MAX + 1, "command");
pwd = getpwnam(*server_username);
if (pwd == NULL)
fatal(s, NULL, "Login incorrect.");
if (iruserok(thataddr->sin_addr.s_addr, pwd->pw_uid == 0,
*client_username, *server_username))
fatal(s, NULL, "Login incorrect.");
return 0;
}
#ifdef KRB5
static int
save_krb5_creds (int s,
krb5_auth_context auth_context,
krb5_principal client)
{
int ret;
krb5_data remote_cred;
krb5_data_zero (&remote_cred);
ret= krb5_read_message (context, (void *)&s, &remote_cred);
if (ret) {
krb5_data_free(&remote_cred);
return 0;
}
if (remote_cred.length == 0)
return 0;
ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
if (ret) {
krb5_data_free(&remote_cred);
return 0;
}
krb5_cc_initialize(context,ccache,client);
ret = krb5_rd_cred2(context, auth_context, ccache, &remote_cred);
if(ret != 0)
syslog(LOG_INFO|LOG_AUTH,
"reading creds: %s", krb5_get_err_text(context, ret));
krb5_data_free (&remote_cred);
if (ret)
return 0;
return 1;
}
static void
krb5_start_session (void)
{
krb5_error_code ret;
char *estr;
ret = krb5_cc_resolve (context, tkfile, &ccache2);
if (ret) {
estr = krb5_get_error_string(context);
syslog(LOG_WARNING, "resolve cred cache %s: %s",
tkfile,
estr ? estr : krb5_get_err_text(context, ret));
free(estr);
krb5_cc_destroy(context, ccache);
return;
}
ret = krb5_cc_copy_cache (context, ccache, ccache2);
if (ret) {
estr = krb5_get_error_string(context);
syslog(LOG_WARNING, "storing credentials: %s",
estr ? estr : krb5_get_err_text(context, ret));
free(estr);
krb5_cc_destroy(context, ccache);
return ;
}
krb5_cc_close(context, ccache2);
krb5_cc_destroy(context, ccache);
return;
}
static int protocol_version;
static krb5_boolean
match_kcmd_version(const void *data, const char *version)
{
if(strcmp(version, KCMD_NEW_VERSION) == 0) {
protocol_version = 2;
return TRUE;
}
if(strcmp(version, KCMD_OLD_VERSION) == 0) {
protocol_version = 1;
key_usage = KRB5_KU_OTHER_ENCRYPTED;
return TRUE;
}
return FALSE;
}
static int
recv_krb5_auth (int s, u_char *buf,
struct sockaddr *thisaddr,
struct sockaddr *thataddr,
char **client_username,
char **server_username,
char **cmd)
{
uint32_t len;
krb5_auth_context auth_context = NULL;
krb5_ticket *ticket;
krb5_error_code status;
krb5_data cksum_data;
krb5_principal server;
char *str;
if (memcmp (buf, "\x00\x00\x00\x13", 4) != 0)
return -1;
len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);
if (net_read(s, buf, len) != len)
syslog_and_die ("reading auth info: %s", strerror(errno));
if (len != sizeof(KRB5_SENDAUTH_VERSION)
|| memcmp (buf, KRB5_SENDAUTH_VERSION, len) != 0)
syslog_and_die ("bad sendauth version: %.8s", buf);
status = krb5_sock_to_principal (context,
s,
"host",
KRB5_NT_SRV_HST,
&server);
if (status)
syslog_and_die ("krb5_sock_to_principal: %s",
krb5_get_err_text(context, status));
status = krb5_recvauth_match_version(context,
&auth_context,
&s,
match_kcmd_version,
NULL,
server,
KRB5_RECVAUTH_IGNORE_VERSION,
NULL,
&ticket);
krb5_free_principal (context, server);
if (status)
syslog_and_die ("krb5_recvauth: %s",
krb5_get_err_text(context, status));
*server_username = read_str (s, USERNAME_SZ, "remote username");
*cmd = read_str (s, ARG_MAX + 1, "command");
*client_username = read_str (s, ARG_MAX + 1, "local username");
if(protocol_version == 2) {
status = krb5_auth_con_getremotesubkey(context, auth_context,
&keyblock);
if(status != 0 || keyblock == NULL)
syslog_and_die("failed to get remote subkey");
} else if(protocol_version == 1) {
status = krb5_auth_con_getkey (context, auth_context, &keyblock);
if(status != 0 || keyblock == NULL)
syslog_and_die("failed to get key");
}
if (status != 0 || keyblock == NULL)
syslog_and_die ("krb5_auth_con_getkey: %s",
krb5_get_err_text(context, status));
status = krb5_crypto_init(context, keyblock, 0, &crypto);
if(status)
syslog_and_die("krb5_crypto_init: %s",
krb5_get_err_text(context, status));
cksum_data.length = asprintf (&str,
"%u:%s%s",
ntohs(socket_get_port (thisaddr)),
*cmd,
*server_username);
if (str == NULL)
syslog_and_die ("asprintf: out of memory");
cksum_data.data = str;
status = krb5_verify_authenticator_checksum(context,
auth_context,
cksum_data.data,
cksum_data.length);
if (status)
syslog_and_die ("krb5_verify_authenticator_checksum: %s",
krb5_get_err_text(context, status));
free (cksum_data.data);
if (strncmp (*client_username, "-u ", 3) == 0) {
do_unique_tkfile = 1;
memmove (*client_username, *client_username + 3,
strlen(*client_username) - 2);
}
if (strncmp (*client_username, "-U ", 3) == 0) {
char *end, *temp_tkfile;
do_unique_tkfile = 1;
if (strncmp (*client_username + 3, "FILE:", 5) == 0) {
temp_tkfile = tkfile;
} else {
strlcpy (tkfile, "FILE:", sizeof(tkfile));
temp_tkfile = tkfile + 5;
}
end = strchr(*client_username + 3,' ');
if (end == NULL)
syslog_and_die("missing argument after -U");
snprintf(temp_tkfile, sizeof(tkfile) - (temp_tkfile - tkfile),
"%.*s",
(int)(end - *client_username - 3),
*client_username + 3);
memmove (*client_username, end + 1, strlen(end+1)+1);
}
kerberos_status = save_krb5_creds (s, auth_context, ticket->client);
if(!krb5_kuserok (context,
ticket->client,
*server_username))
fatal (s, NULL, "Permission denied.");
if (strncmp (*cmd, "-x ", 3) == 0) {
do_encrypt = 1;
memmove (*cmd, *cmd + 3, strlen(*cmd) - 2);
} else {
if(do_encrypt)
fatal (s, NULL, "Encryption is required.");
do_encrypt = 0;
}
{
char *name;
if (krb5_unparse_name (context, ticket->client, &name) == 0) {
char addr_str[256];
if (inet_ntop (thataddr->sa_family,
socket_get_address (thataddr),
addr_str, sizeof(addr_str)) == NULL)
strlcpy (addr_str, "unknown address",
sizeof(addr_str));
syslog(LOG_INFO|LOG_AUTH,
"kerberos v5 shell from %s on %s as %s, cmd '%.80s'",
name,
addr_str,
*server_username,
*cmd);
free (name);
}
}
krb5_auth_con_free(context, auth_context);
return 0;
}
#endif /* KRB5 */
static void
rshd_loop (int from0, int to0,
int to1, int from1,
int to2, int from2,
int have_errsock)
{
fd_set real_readset;
int max_fd;
int count = 2;
char *buf;
if(from0 >= FD_SETSIZE || from1 >= FD_SETSIZE || from2 >= FD_SETSIZE)
errx (1, "fd too large");
#ifdef KRB5
if(auth_method == AUTH_KRB5 && protocol_version == 2)
init_ivecs(0, have_errsock);
#endif
FD_ZERO(&real_readset);
FD_SET(from0, &real_readset);
FD_SET(from1, &real_readset);
FD_SET(from2, &real_readset);
max_fd = max(from0, max(from1, from2)) + 1;
buf = malloc(max(RSHD_BUFSIZ, RSH_BUFSIZ));
if (buf == NULL)
syslog_and_die("out of memory");
for (;;) {
int ret;
fd_set readset = real_readset;
ret = select (max_fd, &readset, NULL, NULL, NULL);
if (ret < 0) {
if (errno == EINTR)
continue;
else
syslog_and_die ("select: %s", strerror(errno));
}
if (FD_ISSET(from0, &readset)) {
ret = do_read (from0, buf, RSHD_BUFSIZ, ivec_in[0]);
if (ret < 0)
syslog_and_die ("read: %s", strerror(errno));
else if (ret == 0) {
close (from0);
close (to0);
FD_CLR(from0, &real_readset);
} else
net_write (to0, buf, ret);
}
if (FD_ISSET(from1, &readset)) {
ret = read (from1, buf, RSH_BUFSIZ);
if (ret < 0)
syslog_and_die ("read: %s", strerror(errno));
else if (ret == 0) {
close (from1);
close (to1);
FD_CLR(from1, &real_readset);
if (--count == 0)
exit (0);
} else
do_write (to1, buf, ret, ivec_out[0]);
}
if (FD_ISSET(from2, &readset)) {
ret = read (from2, buf, RSH_BUFSIZ);
if (ret < 0)
syslog_and_die ("read: %s", strerror(errno));
else if (ret == 0) {
close (from2);
close (to2);
FD_CLR(from2, &real_readset);
if (--count == 0)
exit (0);
} else
do_write (to2, buf, ret, ivec_out[1]);
}
}
}
/*
* Used by `setup_copier' to create some pipe-like means of
* communcation. Real pipes would probably be the best thing, but
* then the shell doesn't understand it's talking to rshd. If
* socketpair doesn't work everywhere, some autoconf magic would have
* to be added here.
*
* If it fails creating the `pipe', it aborts by calling fatal.
*/
static void
pipe_a_like (int fd[2])
{
if (socketpair (AF_UNIX, SOCK_STREAM, 0, fd) < 0)
fatal (STDOUT_FILENO, "socketpair", "Pipe creation failed.");
}
/*
* Start a child process and leave the parent copying data to and from it. */
static void
setup_copier (int have_errsock)
{
int p0[2], p1[2], p2[2];
pid_t pid;
pipe_a_like(p0);
pipe_a_like(p1);
pipe_a_like(p2);
pid = fork ();
if (pid < 0)
fatal (STDOUT_FILENO, "fork", "Could not create child process.");
if (pid == 0) { /* child */
close (p0[1]);
close (p1[0]);
close (p2[0]);
dup2 (p0[0], STDIN_FILENO);
dup2 (p1[1], STDOUT_FILENO);
dup2 (p2[1], STDERR_FILENO);
close (p0[0]);
close (p1[1]);
close (p2[1]);
} else { /* parent */
close (p0[0]);
close (p1[1]);
close (p2[1]);
if (net_write (STDOUT_FILENO, "", 1) != 1)
fatal (STDOUT_FILENO, "net_write", "Write failure.");
rshd_loop (STDIN_FILENO, p0[1],
STDOUT_FILENO, p1[0],
STDERR_FILENO, p2[0],
have_errsock);
}
}
/*
* Is `port' a ``reserverd'' port?
*/
static int
is_reserved(u_short port)
{
return ntohs(port) < IPPORT_RESERVED;
}
/*
* Set the necessary part of the environment in `env'.
*/
static void
setup_environment (char ***env, const struct passwd *pwd)
{
int i, j, path;
char **e;
i = 0;
path = 0;
*env = NULL;
i = read_environment(_PATH_ETC_ENVIRONMENT, env);
e = *env;
for (j = 0; j < i; j++) {
if (!strncmp(e[j], "PATH=", 5)) {
path = 1;
}
}
e = *env;
e = realloc(e, (i + 7) * sizeof(char *));
if (asprintf (&e[i++], "USER=%s", pwd->pw_name) == -1)
syslog_and_die ("asprintf: out of memory");
if (asprintf (&e[i++], "HOME=%s", pwd->pw_dir) == -1)
syslog_and_die ("asprintf: out of memory");
if (asprintf (&e[i++], "SHELL=%s", pwd->pw_shell) == -1)
syslog_and_die ("asprintf: out of memory");
if (! path) {
if (asprintf (&e[i++], "PATH=%s", _PATH_DEFPATH) == -1)
syslog_and_die ("asprintf: out of memory");
}
asprintf (&e[i++], "SSH_CLIENT=only_to_make_bash_happy");
if (do_unique_tkfile)
if (asprintf (&e[i++], "KRB5CCNAME=%s", tkfile) == -1)
syslog_and_die ("asprintf: out of memory");
e[i++] = NULL;
*env = e;
}
static void
doit (void)
{
u_char buf[BUFSIZ];
u_char *p;
struct sockaddr_storage thisaddr_ss;
struct sockaddr *thisaddr = (struct sockaddr *)&thisaddr_ss;
struct sockaddr_storage thataddr_ss;
struct sockaddr *thataddr = (struct sockaddr *)&thataddr_ss;
struct sockaddr_storage erraddr_ss;
struct sockaddr *erraddr = (struct sockaddr *)&erraddr_ss;
socklen_t thisaddr_len, thataddr_len;
int port;
int errsock = -1;
char *client_user = NULL, *server_user = NULL, *cmd = NULL;
struct passwd *pwd;
int s = STDIN_FILENO;
char **env;
int ret;
char that_host[NI_MAXHOST];
thisaddr_len = sizeof(thisaddr_ss);
if (getsockname (s, thisaddr, &thisaddr_len) < 0)
syslog_and_die("getsockname: %s", strerror(errno));
thataddr_len = sizeof(thataddr_ss);
if (getpeername (s, thataddr, &thataddr_len) < 0)
syslog_and_die ("getpeername: %s", strerror(errno));
/* check for V4MAPPED addresses? */
if (do_kerberos == 0 && !is_reserved(socket_get_port(thataddr)))
fatal(s, NULL, "Permission denied.");
p = buf;
port = 0;
for(;;) {
if (net_read (s, p, 1) != 1)
syslog_and_die ("reading port number: %s", strerror(errno));
if (*p == '\0')
break;
else if (isdigit(*p))
port = port * 10 + *p - '0';
else
syslog_and_die ("non-digit in port number: %c", *p);
}
if (do_kerberos == 0 && !is_reserved(htons(port)))
fatal(s, NULL, "Permission denied.");
if (port) {
int priv_port = IPPORT_RESERVED - 1;
/*
* There's no reason to require a ``privileged'' port number
* here, but for some reason the brain dead rsh clients
* do... :-(
*/
erraddr->sa_family = thataddr->sa_family;
socket_set_address_and_port (erraddr,
socket_get_address (thataddr),
htons(port));
/*
* we only do reserved port for IPv4
*/
if (erraddr->sa_family == AF_INET)
errsock = rresvport (&priv_port);
else
errsock = socket (erraddr->sa_family, SOCK_STREAM, 0);
if (errsock < 0)
syslog_and_die ("socket: %s", strerror(errno));
if (connect (errsock,
erraddr,
socket_sockaddr_size (erraddr)) < 0) {
syslog (LOG_WARNING, "connect: %s", strerror(errno));
close (errsock);
}
}
if(do_kerberos) {
if (net_read (s, buf, 4) != 4)
syslog_and_die ("reading auth info: %s", strerror(errno));
#ifdef KRB5
if((do_kerberos & DO_KRB5) &&
recv_krb5_auth (s, buf, thisaddr, thataddr,
&client_user,
&server_user,
&cmd) == 0)
auth_method = AUTH_KRB5;
else
#endif /* KRB5 */
syslog_and_die ("unrecognized auth protocol: %x %x %x %x",
buf[0], buf[1], buf[2], buf[3]);
} else {
if(recv_bsd_auth (s, buf,
(struct sockaddr_in *)thisaddr,
(struct sockaddr_in *)thataddr,
&client_user,
&server_user,
&cmd) == 0) {
auth_method = AUTH_BROKEN;
if(do_vacuous) {
printf("Remote host requires Kerberos authentication\n");
exit(0);
}
} else
syslog_and_die("recv_bsd_auth failed");
}
if (client_user == NULL || server_user == NULL || cmd == NULL)
syslog_and_die("mising client/server/cmd");
pwd = getpwnam (server_user);
if (pwd == NULL)
fatal (s, NULL, "Login incorrect.");
if (*pwd->pw_shell == '\0')
pwd->pw_shell = _PATH_BSHELL;
if (pwd->pw_uid != 0 && access (_PATH_NOLOGIN, F_OK) == 0)
fatal (s, NULL, "Login disabled.");
ret = getnameinfo_verified (thataddr, thataddr_len,
that_host, sizeof(that_host),
NULL, 0, 0);
if (ret)
fatal (s, NULL, "getnameinfo: %s", gai_strerror(ret));
if (login_access(pwd, that_host) == 0) {
syslog(LOG_NOTICE, "Kerberos rsh denied to %s from %s",
server_user, that_host);
fatal(s, NULL, "Permission denied.");
}
#ifdef HAVE_GETSPNAM
{
struct spwd *sp;
long today;
sp = getspnam(server_user);
if (sp != NULL) {
today = time(0)/(24L * 60 * 60);
if (sp->sp_expire > 0)
if (today > sp->sp_expire)
fatal(s, NULL, "Account has expired.");
}
}
#endif
#ifdef HAVE_SETLOGIN
if (setlogin(pwd->pw_name) < 0)
syslog(LOG_ERR, "setlogin() failed: %s", strerror(errno));
#endif
#ifdef HAVE_SETPCRED
if (setpcred (pwd->pw_name, NULL) == -1)
syslog(LOG_ERR, "setpcred() failure: %s", strerror(errno));
#endif /* HAVE_SETPCRED */
/* Apply limits if not root */
if(pwd->pw_uid != 0) {
const char *file = _PATH_LIMITS_CONF;
read_limits_conf(file, pwd);
}
if (initgroups (pwd->pw_name, pwd->pw_gid) < 0)
fatal (s, "initgroups", "Login incorrect.");
if (setgid(pwd->pw_gid) < 0)
fatal (s, "setgid", "Login incorrect.");
if (setuid (pwd->pw_uid) < 0)
fatal (s, "setuid", "Login incorrect.");
if (chdir (pwd->pw_dir) < 0)
fatal (s, "chdir", "Remote directory.");
if (errsock >= 0) {
if (dup2 (errsock, STDERR_FILENO) < 0)
fatal (s, "dup2", "Cannot dup stderr.");
close (errsock);
} else {
if (dup2 (STDOUT_FILENO, STDERR_FILENO) < 0)
fatal (s, "dup2", "Cannot dup stderr.");
}
#ifdef KRB5
{
int fd;
if (!do_unique_tkfile)
snprintf(tkfile,sizeof(tkfile),"FILE:/tmp/krb5cc_%lu",
(unsigned long)pwd->pw_uid);
else if (*tkfile=='\0') {
snprintf(tkfile,sizeof(tkfile),"FILE:/tmp/krb5cc_XXXXXX");
fd = mkstemp(tkfile+5);
close(fd);
unlink(tkfile+5);
}
if (kerberos_status)
krb5_start_session();
}
#endif
setup_environment (&env, pwd);
if (do_encrypt) {
setup_copier (errsock >= 0);
} else {
if (net_write (s, "", 1) != 1)
fatal (s, "net_write", "write failed");
}
#if defined(KRB5)
if(k_hasafs()) {
char cell[64];
if(do_newpag)
k_setpag();
/* XXX */
if (kerberos_status) {
krb5_ccache ccache;
krb5_error_code status;
status = krb5_cc_resolve (context, tkfile, &ccache);
if (!status) {
if (k_afs_cell_of_file (pwd->pw_dir, cell, sizeof(cell)) == 0)
krb5_afslog_uid_home(context, ccache, cell, NULL,
pwd->pw_uid, pwd->pw_dir);
krb5_afslog_uid_home(context, ccache, NULL, NULL,
pwd->pw_uid, pwd->pw_dir);
krb5_cc_close (context, ccache);
}
}
}
#endif /* KRB5 */
execle (pwd->pw_shell, pwd->pw_shell, "-c", cmd, NULL, env);
err(1, "exec %s", pwd->pw_shell);
}
struct getargs args[] = {
{ NULL, 'a', arg_flag, &do_addr_verify },
{ "keepalive", 'n', arg_negative_flag, &do_keepalive },
{ "inetd", 'i', arg_negative_flag, &do_inetd,
"Not started from inetd" },
#if defined(KRB5)
{ "kerberos", 'k', arg_flag, &do_kerberos,
"Implement kerberised services" },
{ "encrypt", 'x', arg_flag, &do_encrypt,
"Implement encrypted service" },
#endif
{ "rhosts", 'l', arg_negative_flag, &do_rhosts,
"Don't check users .rhosts" },
{ "port", 'p', arg_string, &port_str, "Use this port",
"port" },
{ "vacuous", 'v', arg_flag, &do_vacuous,
"Don't accept non-kerberised connections" },
#if defined(KRB5)
{ NULL, 'P', arg_negative_flag, &do_newpag,
"Don't put process in new PAG" },
#endif
/* compatibility flag: */
{ NULL, 'L', arg_flag, &do_log },
{ "version", 0, arg_flag, &do_version },
{ "help", 0, arg_flag, &do_help }
};
static void
usage (int ret)
{
if(isatty(STDIN_FILENO))
arg_printusage (args,
sizeof(args) / sizeof(args[0]),
NULL,
"");
else
syslog (LOG_ERR, "Usage: %s [-ikxlvPL] [-p port]", getprogname());
exit (ret);
}
int
main(int argc, char **argv)
{
int optind = 0;
int on = 1;
setprogname (argv[0]);
roken_openlog ("rshd", LOG_ODELAY | LOG_PID, LOG_AUTH);
if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv,
&optind))
usage(1);
if(do_help)
usage (0);
if (do_version) {
print_version(NULL);
exit(0);
}
#if defined(KRB5)
if (do_encrypt)
do_kerberos = 1;
if(do_kerberos)
do_kerberos = DO_KRB5;
#endif
#ifdef KRB5
if((do_kerberos & DO_KRB5) && krb5_init_context (&context) != 0)
do_kerberos &= ~DO_KRB5;
#endif
if (!do_inetd) {
int error;
struct addrinfo *ai = NULL, hints;
char portstr[NI_MAXSERV];
memset (&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
if(port_str != NULL) {
error = getaddrinfo (NULL, port_str, &hints, &ai);
if (error)
errx (1, "getaddrinfo: %s", gai_strerror (error));
}
if (ai == NULL) {
#if defined(KRB5)
if (do_kerberos) {
if (do_encrypt) {
error = getaddrinfo(NULL, "ekshell", &hints, &ai);
if(error == EAI_NONAME) {
snprintf(portstr, sizeof(portstr), "%d", 545);
error = getaddrinfo(NULL, portstr, &hints, &ai);
}
if(error)
errx (1, "getaddrinfo: %s", gai_strerror (error));
} else {
error = getaddrinfo(NULL, "kshell", &hints, &ai);
if(error == EAI_NONAME) {
snprintf(portstr, sizeof(portstr), "%d", 544);
error = getaddrinfo(NULL, portstr, &hints, &ai);
}
if(error)
errx (1, "getaddrinfo: %s", gai_strerror (error));
}
} else
#endif
{
error = getaddrinfo(NULL, "shell", &hints, &ai);
if(error == EAI_NONAME) {
snprintf(portstr, sizeof(portstr), "%d", 514);
error = getaddrinfo(NULL, portstr, &hints, &ai);
}
if(error)
errx (1, "getaddrinfo: %s", gai_strerror (error));
}
}
mini_inetd_addrinfo (ai, NULL);
freeaddrinfo(ai);
}
if (do_keepalive &&
setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %s", strerror(errno));
/* set SO_LINGER? */
signal (SIGPIPE, SIG_IGN);
doit ();
return 0;
}