1
0
Files
irix-657m-src/eoe/cmd/bsd/rshd.c
2022-09-29 17:59:04 +03:00

920 lines
20 KiB
C

/*
* Copyright (c) 1983, 1988, 1989 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983, 1988, 1989 The Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)rshd.c 5.34 (Berkeley) 6/29/90";
#endif /* not lint */
/*
* remote shell server:
* [port]\0
* remuser\0
* locuser\0
* command\0
* data
*/
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <ia.h>
#include <netdb.h>
#include <syslog.h>
#include <limits.h>
#include <paths.h>
#include <proj.h>
#include <strings.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sat.h>
#include <sys/capability.h>
#include <sys/mac.h>
#include <clearance.h>
#include <capability.h>
#include <unistd.h>
#ifdef _SHAREII
#include <dlfcn.h>
#include <shareIIhooks.h>
SH_DECLARE_HOOK(SETMYNAME);
SH_DECLARE_HOOK(RSH);
#endif /* _SHAREII */
#ifdef sgi
#define AUX_LANG /* switch LANG handling on */
#endif /* sgi */
#ifdef AUX_LANG
#include <locale.h>
#define LANG_FILE "/.lang" /* default lang file */
#define TZ_FILE "/.timezone" /* default timezone file */
#define MAX_LUSER 256 /* max size of user name + args */
#define MAXARGS 64 /* max # of args */
char envbuf[MAX_LUSER], envblk[MAX_LUSER];
char *args[MAXARGS];
#endif /* AUX_LANG */
#if sgi && !defined(NCARGS)
#define NCARGS 10240 /* # characters in exec arglist */
#endif
void getstr( char *, int, char *, int);
#define setpgrp(a,b) BSDsetpgrp(a,b)
static int log_success = 0; /* If TRUE, log all successful accesses */
extern struct hostent * __getverfhostent(struct in_addr, int);
int keepalive = 1;
int check_all = 0;
void error(const char *, ...);
void doit(struct sockaddr_in *);
int sent_null;
#define NBUFS 3
#define BUFSIZE (64 * 1024)
char *rshdbuf[NBUFS];
/*ARGSUSED*/
main(argc, argv)
int argc;
char **argv;
{
extern int opterr, optind;
extern int _check_rhosts_file;
struct linger linger;
int ch, on = 1, fromlen;
int i;
struct sockaddr_in from;
openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
if (cap_envl(0, CAP_SETUID, CAP_SETGID, CAP_AUDIT_WRITE,
CAP_PRIV_PORT, CAP_SETPCAP, CAP_NOT_A_CID) == -1) {
syslog(LOG_ERR, "insufficient privilege");
exit(1);
}
/*
* use memalign to set up page-aligned buffer
*/
for (i = 0; i < NBUFS; i++) {
rshdbuf[i] = memalign(16*1024, BUFSIZE);
if (!rshdbuf[i]) {
syslog(LOG_ERR, "memalign(): %m");
exit(1);
}
}
opterr = 0;
while ((ch = getopt(argc, argv, "alnL")) != EOF)
switch (ch) {
case 'a':
check_all = 1;
break;
case 'l':
_check_rhosts_file = 0;
break;
case 'n':
keepalive = 0;
break;
case 'L':
log_success = 1;
break;
case '?':
default:
syslog(LOG_ERR, "usage: rshd [-alnL]");
break;
}
argc -= optind;
argv += optind;
fromlen = sizeof (from);
if (getpeername(0, &from, &fromlen) < 0) {
syslog(LOG_ERR, "getpeername: %m");
_exit(1);
}
if (keepalive &&
setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
sizeof(on)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
linger.l_onoff = 1;
linger.l_linger = 60; /* XXX */
if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger,
sizeof (linger)) < 0)
syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
doit(&from);
/*NOTREACHED*/
}
#ifdef AUX_LANG
/*
* handle quoted chars
*/
static char *
quotec(s, cp)
register char *s;
int *cp;
{
register int c, i, num;
switch(c = *s++) {
case 'n' :
c = '\n';
break;
case 'r' :
c = '\r';
break;
case 'v' :
c = '\013';
break;
case 'b' :
c = '\b';
break;
case 't' :
c = '\t';
break;
case 'f' :
c = '\f';
break;
case '0' : case '1' : case '2' : case '3' :
case '4' : case '5' : case '6' : case '7' :
for(num=0, i=0; i<3; i++) {
num = num * 8 + (c - '0');
c = *s++;
if(c < '0' || c > '7')
break;
}
s--;
c = num & 0377;
break;
default :
break;
}
*cp = c;
return(s);
}
/*
* get the args from login name
*/
static char **
getargs(s)
register char *s;
{
int c, llen = MAX_LUSER - 1;
register char *ptr = envbuf, **reply = args;
enum {
WHITESPACE, ARGUMENT
} state = WHITESPACE;
while(c = *s++) {
if(llen > 0)
--llen;
switch(c) {
case ' ' :
case '\t' :
if(state == ARGUMENT) {
*ptr++ = '\0';
state = WHITESPACE;
}
break;
case '\\' :
s = quotec(s, &c);
default:
if(state == WHITESPACE) {
*reply++ = ptr;
state = ARGUMENT;
}
*ptr++ = (char)c;
}
/* chec kfor overflow */
if(ptr >= envbuf + MAX_LUSER - 1
|| reply >= args + MAXARGS - 1 && state == WHITESPACE) {
break;
}
}
*ptr = '\0';
*reply = NULL;
return((reply == args)? NULL : args);
}
/*
* list of illegal environment variables
*/
static char *illegal[] = {
"HOME=", "SHELL=", "PATH=", "USER=", "LOGNAME=",
"REMOTEHOST=", "REMOTEUSER=", "TZ=",
0,
};
/*
* check if variable is legal
*/
static int
legalenvvar(s)
register char *s;
{
register char **p;
for(p = illegal; *p; p++)
if( !strncmp(s, *p, strlen(*p)))
return(0);
return(1);
}
/*
* search for LANG= in environment list and switch to that locale
*/
char *
set_lang(envp)
char **envp;
{
register char **ulp, *langp = (char *)0;
if(ulp = envp) {
for(; *ulp; ulp++) {
if( !strncmp(*ulp, "LANG=", 5))
langp = *ulp + 5;
}
}
if(langp)
(void)setlocale(LC_ALL, langp);
return(langp);
}
#endif /* AUX_LANG */
#define NMAX 64
char tz[256] = "TZ=";
char logname[8+NMAX] = "LOGNAME=";
char username[5+NMAX] = "USER=";
char homedir[5+PATH_MAX] = "HOME=";
char shell[6+PATH_MAX] = "SHELL=";
char env_hostname[12+MAXHOSTNAMELEN] = { "REMOTEHOST=" };
char env_remuser[12+NMAX] = { "REMOTEUSER=" };
char path[5+PATH_MAX] = "PATH=";
#define TZ_INDEX 7
#ifdef AUX_LANG
char lang[NL_LANGMAX + 6] = "LANG=";
char *envinit[9 + MAXARGS] =
{ homedir, shell, path, username, logname,
env_hostname, env_remuser, tz, lang, 0 };
#else /* AUX_LANG */
char *envinit[10 + MAXARGS] =
{homedir, shell, path, username,
logname, env_hostname, env_remuser, tz, 0};
#endif /* AUX_LANG */
extern char **environ;
#ifdef AUX_LANG
/*
* setup environment from extended user name
*/
void
setup_env(envp, home)
register char **envp;
char *home;
{
register char *ep, *ptr;
register int benv, i, k;
int fd, j, li, length;
char uhcfg[MAXPATHLEN + 11];
/*
* read LANG_FILE
*/
(void)strncpy(uhcfg, home, MAXPATHLEN);
(void)strcat(uhcfg, LANG_FILE);
if((fd = open(uhcfg, O_RDONLY)) >= 0) {
int nrd;
if((nrd=read(fd, uhcfg, NL_LANGMAX + 2)) > 0) {
if(ptr = strchr(uhcfg, '\n'))
*ptr = 0;
else uhcfg[nrd]= 0;
uhcfg[NL_LANGMAX] = 0;
(void)strcpy(lang + 5, uhcfg);
}
(void)close(fd);
}
/*
* read TZ_FILE
*/
(void)strncpy(uhcfg, home, MAXPATHLEN);
(void)strcat(uhcfg, TZ_FILE);
if((fd = open(uhcfg, O_RDONLY)) >= 0) {
int nrd;
if((nrd=read(fd, uhcfg, sizeof(tz)-1)) > 0) {
if(ptr = strchr(uhcfg, '\n'))
*ptr = 0;
else uhcfg[nrd]= 0;
uhcfg[sizeof(tz)-1]= 0;
(void)strcpy(tz + 3, uhcfg);
}
(void)close(fd);
envinit[TZ_INDEX]= tz;
}
/*
* seek to end of basic environment
* add all the environment variables from login name
*/
for(benv = 0; envinit[benv]; benv++);
ptr = envblk;
for(j = k = li = 0; *envp && (j < (MAXARGS-1)); j++, envp++) {
if( !(ep = strchr(*envp, '='))) {
envinit[benv + k] = ptr;
i = sprintf(ptr, "L%d=%s", li, *envp);
if(i >= 0) {
ptr += i;
*ptr++ = 0;
}
k++;
li++;
} else if( !legalenvvar(*envp))
continue;
/*
* check if this overwrites previous definition
* if not place it at the end
*/
else {
for(i = 0, length = ep + 1 - *envp; i < benv + k; i++) {
if( !strncmp(*envp, envinit[i], length)) {
envinit[i] = *envp;
break;
}
}
if(i == (benv + k)) {
envinit[benv + k] = *envp;
k++;
}
}
}
}
#endif /* AUX_LANG */
int
connectfromresvport(struct sockaddr_in *sin, int sinlen)
{
int s;
cap_t ocap;
cap_value_t capv = CAP_PRIV_PORT;
int on = 1;
struct sockaddr_in sinb;
u_short lport = IPPORT_RESERVED - 1;
int r;
sinb.sin_family = AF_INET;
sinb.sin_addr.s_addr = INADDR_ANY;
for (;;) {
sinb.sin_port = htons(lport);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0) {
return (-1);
}
r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (r < 0) {
close(s);
return r;
}
ocap = cap_acquire (1, &capv);
if (bind(s, (caddr_t)&sinb, sizeof (sinb)) >= 0) {
cap_surrender (ocap);
r = connect(s, sin, sinlen);
if (r >= 0) {
return s;
}
} else {
cap_surrender (ocap);
}
if (_oserror() != EADDRINUSE) {
(void) close(s);
return (-1);
}
(void) close(s);
lport--;
if (lport == IPPORT_RESERVED/2) {
_setoserror(EAGAIN); /* close */
return (-1);
}
}
}
void
doit(fromp)
struct sockaddr_in *fromp;
{
char cmdbuf[NCARGS+1], *cp;
char locuser[16], remuser[16];
uinfo_t uinfo;
int s;
char *hostname;
u_short port;
int pv[2], pid, cc;
int nfd;
fd_set ready, readfrom;
char sig;
int one = 1;
char luser[MAX_LUSER];
#ifdef AUX_LANG
char *envlang, **remenvp;
#endif /* AUX_LANG */
int b = 0;
cap_t ocap;
const cap_value_t cap_setuid = CAP_SETUID;
const cap_value_t cap_setgid = CAP_SETGID;
const cap_value_t cap_audit_write = CAP_AUDIT_WRITE;
const cap_value_t cap_setpcap = CAP_SETPCAP;
(void) signal(SIGINT, SIG_DFL);
(void) signal(SIGQUIT, SIG_DFL);
(void) signal(SIGTERM, SIG_DFL);
#ifdef DEBUG
{ int t = open(_PATH_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 (af %d)\n",
fromp->sin_family);
exit(1);
}
#ifdef IP_OPTIONS
{
u_char optbuf[BUFSIZ/3], *cp;
char lbuf[BUFSIZ], *lp;
int optsize = sizeof(optbuf), ipproto;
struct protoent *ip;
if ((ip = getprotobyname("ip")) != NULL)
ipproto = ip->p_proto;
else
ipproto = IPPROTO_IP;
if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 &&
optsize != 0) {
lp = lbuf;
for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
sprintf(lp, " %2.2x", *cp);
syslog(LOG_NOTICE,
"Connection received from %s using IP options (ignored):%s",
inet_ntoa(fromp->sin_addr), lbuf);
if (setsockopt(0, ipproto, IP_OPTIONS,
(char *)NULL, optsize) != 0) {
syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
exit(1);
}
}
}
#endif
if (fromp->sin_port >= IPPORT_RESERVED ||
fromp->sin_port < IPPORT_RESERVED/2) {
char msg[256];
sprintf(msg, "Connection from %s on illegal port %u",
inet_ntoa(fromp->sin_addr), fromp->sin_port);
syslog(LOG_NOTICE|LOG_AUTH, msg);
ocap = cap_acquire(1, &cap_audit_write);
satvwrite(SAT_AE_IDENTITY, SAT_FAILURE, "rshd|-|?|%s", msg);
cap_surrender(ocap);
exit(1);
}
(void) alarm(60);
port = 0;
for (;;) {
char c;
if ((cc = read(0, &c, 1)) != 1) {
if (cc < 0)
syslog(LOG_NOTICE, "read: %m");
shutdown(0, 1+1);
exit(1);
}
if (c == 0)
break;
port = port * 10 + c - '0';
}
(void) alarm(0);
if (port != 0) {
#if 1
fromp->sin_port = htons((u_short)port);
s = connectfromresvport(fromp, sizeof(*fromp));
if (s < 0) {
#else
int lport = IPPORT_RESERVED - 1;
s = rresvport(&lport);
if (s < 0) {
syslog(LOG_ERR, "can't get stderr port: %m");
exit(1);
}
if (port >= IPPORT_RESERVED) {
syslog(LOG_ERR, "2nd port not reserved\n");
exit(1);
}
fromp->sin_port = htons((u_short)port);
if (connect(s, fromp, sizeof (*fromp)) < 0) {
#endif
syslog(LOG_INFO, "connect second port %d: %m", port);
exit(1);
}
} else
s = 0;
#ifdef notdef
/* from inetd, socket is already on 0, 1, 2 */
dup2(f, 0);
dup2(f, 1);
dup2(f, 2);
#endif
hostname = __getverfhostent(fromp->sin_addr, check_all)->h_name;
getstr(remuser, sizeof(remuser), "remuser", s);
getstr(luser, sizeof(luser), "locuser", s);
getstr(cmdbuf, sizeof(cmdbuf), "command", s);
#ifdef AUX_LANG
/*
* handle args to login name
*/
if(envlang = getenv("LANG"))
(void)strncpy(lang + 5, envlang, NL_LANGMAX);
if( !(remenvp = getargs(luser)))
locuser[0] = 0;
else
(void)strcpy(locuser, *remenvp++);
(void)set_lang(remenvp);
#else /* AUX_LANG */
(void)strncpy(locuser, luser, sizeof(luser));
#endif /* AUX_LANG */
if (ia_openinfo(locuser, &uinfo)) {
syslog(LOG_NOTICE|LOG_AUTH,
"%s@%s as %s: unknown login. cmd='%.80s'",
remuser, hostname, locuser, cmdbuf);
error("Login incorrect.\n");
exit(1);
}
/*
* first try chdir as root user.
*/
if (chdir(uinfo->ia_dirp) < 0) {
/*
* now try as the user
*/
setregid(-1, uinfo->ia_gid);
setreuid(-1, uinfo->ia_uid);
if (chdir(uinfo->ia_dirp) < 0) {
syslog(LOG_NOTICE|LOG_AUTH,
"%s@%s as %s: no home directory. cmd='%.80s'",
remuser, hostname, locuser, cmdbuf);
error("No remote directory.\n");
exit(1);
}
setregid(-1, 0);
setreuid(-1, 0);
}
if (uinfo->ia_pwdp != 0 && *uinfo->ia_pwdp != '\0' &&
ruserok(hostname, uinfo->ia_uid == 0, remuser, locuser) < 0) {
syslog(LOG_NOTICE|LOG_AUTH,
"%s@%s as %s: permission denied. cmd='%.80s'",
remuser, hostname, locuser, cmdbuf);
error("Permission denied.\n");
exit(1);
}
if (uinfo->ia_uid && !access(_PATH_NOLOGIN, F_OK)) {
error("Logins currently disabled.\n");
exit(1);
}
ocap = cap_acquire(1, &cap_setgid);
if (setgid(uinfo->ia_gid) < 0) {
cap_surrender(ocap);
error("Bad group id.\n");
syslog(LOG_NOTICE|LOG_AUTH, "user '%s' has invalid gid %d",
uinfo->ia_name, uinfo->ia_gid);
exit(1);
}
#ifdef _SHAREII
/*
* Perform Share II resource limit checks.
*/
if (sgidladd(SH_LIMITS_LIB, RTLD_LAZY))
{
static const char *Myname = "rshd";
SH_HOOK_SETMYNAME(Myname);
if (SH_HOOK_RSH(uinfo->ia_uid))
{
syslog(LOG_NOTICE|LOG_AUTH,
"Share II resource limit checks for user '%s' failed",
uinfo->ia_name);
exit(1);
}
}
#endif /* _SHAREII */
cap_surrender(ocap);
/*
* Start a new array session and set up this user's default
* project ID while we still have root privileges
*/
ocap = cap_acquire(1, &cap_setuid);
newarraysess();
setprid(getdfltprojuser(locuser));
cap_surrender(ocap);
/* audit remote shell */
ocap = cap_acquire(1, &cap_audit_write);
satvwrite(SAT_AE_IDENTITY, SAT_SUCCESS,
"RSHD|+|%s|%s@%s: cmd='%.80s'",
locuser, remuser, hostname, cmdbuf);
cap_surrender(ocap);
ocap = cap_acquire(1, &cap_setgid);
(void) initgroups(uinfo->ia_name, uinfo->ia_gid);
cap_surrender(ocap);
ocap = cap_acquire(1, &cap_setuid);
if (setuid(uinfo->ia_uid) < 0) {
cap_surrender(ocap);
error("Bad user id.\n");
syslog(LOG_NOTICE|LOG_AUTH, "user '%s' has invalid uid %d",
uinfo->ia_name, uinfo->ia_uid);
exit(1);
}
cap_surrender(ocap);
if (log_success) {
syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
remuser, hostname, locuser, cmdbuf);
}
(void) write(2, "\0", 1);
closelog();
sent_null = 1;
if (port) {
if (pipe(pv) < 0) {
error("Can't make pipe.\n");
exit(1);
}
pid = fork();
if (pid == -1) {
error("Can't fork; try again.\n");
exit(1);
}
if (pv[0] > s)
nfd = pv[0];
else
nfd = s;
nfd++;
if (pid) {
(void) close(0); (void) close(1); (void) close(2);
(void) close(pv[1]);
FD_ZERO(&readfrom);
FD_SET(s, &readfrom);
FD_SET(pv[0], &readfrom);
ioctl(pv[0], FIONBIO, (char *)&one);
/* should set s nbio! */
do {
ready = readfrom;
if (select(nfd, &ready, (fd_set *)0,
(fd_set *)0, (struct timeval *)0) < 0)
break;
if (FD_ISSET(s, &ready)) {
if (read(s, &sig, 1) <= 0)
FD_CLR(s, &readfrom);
else
killpg(pid, sig);
}
if (FD_ISSET(pv[0], &ready)) {
errno = 0;
cc = read(pv[0], rshdbuf[b], BUFSIZE);
if (cc <= 0) {
shutdown(s, 1+1);
FD_CLR(pv[0], &readfrom);
} else
(void) write(s, rshdbuf[b], cc);
b++;
if (b == NBUFS) {
b = 0;
}
}
} while (FD_ISSET(s, &readfrom) ||
FD_ISSET(pv[0], &readfrom));
exit(0);
}
setpgrp(0, getpid());
(void) close(s); (void) close(pv[0]);
dup2(pv[1], 2);
close(pv[1]);
}
if (*uinfo->ia_shellp == '\0')
uinfo->ia_shellp = _PATH_BSHELL;
for (nfd = getdtablehi(); --nfd > 2; )
(void)close(nfd);
strncat(path, uinfo->ia_uid == 0 ? _PATH_ROOTPATH : _PATH_USERPATH,
sizeof(path)-5);
strncat(logname, uinfo->ia_name, sizeof(logname)-9);
strncat(env_hostname, hostname, sizeof(env_hostname)-12);
strncat(env_remuser, remuser, sizeof(env_remuser)-12);
cp = getenv("TZ");
if (!cp)
envinit[TZ_INDEX] = 0;
else
strncat(tz, cp, sizeof(tz)-4);
environ = envinit;
strncat(homedir, uinfo->ia_dirp, sizeof(homedir)-6);
strncat(shell, uinfo->ia_shellp, sizeof(shell)-7);
strncat(username, uinfo->ia_name, sizeof(username)-6);
#ifdef AUX_LANG
(void)setup_env(remenvp, uinfo->ia_dirp);
#endif /* AUX_LANG */
cp = rindex(uinfo->ia_shellp, '/');
if (cp)
cp++;
else
cp = uinfo->ia_shellp;
if (sysconf(_SC_MAC) > 0) {
mac_t u_mac;
struct clearance *clp;
/* get clearance information for this user */
clp = sgi_getclearancebyname(uinfo->ia_name);
if (clp == NULL)
exit(1);
/* mac label of current process is used for clearance check */
u_mac = mac_get_proc();
if (u_mac == NULL)
exit(1);
/* perform clearance check */
if (mac_clearedlbl(clp, u_mac) != MAC_CLEARED) {
mac_free(u_mac);
exit(1);
}
mac_free(u_mac);
}
{
cap_t u_cap;
struct user_cap *clp;
/* get capability range information for this user */
clp = sgi_getcapabilitybyname(uinfo->ia_name);
if (clp != NULL)
u_cap = cap_from_text(clp->ca_default);
else
u_cap = cap_init();
if (u_cap == NULL)
exit(1);
/* set our capability set to the default for this user */
ocap = cap_acquire(1, &cap_setpcap);
if (cap_set_proc(u_cap) == -1) {
cap_surrender(ocap);
cap_free(u_cap);
exit(1);
}
cap_free(ocap);
cap_free(u_cap);
}
execl(uinfo->ia_shellp, cp, "-c", cmdbuf, 0);
perror(uinfo->ia_shellp);
exit(1);
}
/*
* Report error to client.
* Note: can't be used until second socket has connected
* to client, or older clients will hang waiting
* for that connection first.
*/
void
error(const char *fmt, ...)
{
va_list args;
char buf[BUFSIZ], *bp = buf;
va_start(args, fmt);
if (sent_null == 0)
*bp++ = 1;
(void) vsprintf(bp, fmt, args);
(void) write(2, buf, strlen(buf));
va_end(args);
}
void
getstr(
char *buf, /* buffer to receive string */
int cnt, /* maximum string length */
char *err, /* string name string */
int sfd) /* second socket fd, zero if none */
{
char c;
fd_set reads;
if (sfd != 0) { /* two ports to track */
FD_ZERO(&reads);
FD_SET(0, &reads);
FD_SET(sfd, &reads);
if (select(sfd + 1, &reads, 0,0,0) < 1 ||
FD_ISSET(sfd, &reads)) {
/*
* Detect loss of the second connection
* instead of hanging.
*/
syslog(LOG_INFO, "select: second connection failure.");
exit(1);
}
}
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);
}