home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
misc
/
volume36
/
log_tcp
/
part03
/
options.c
< prev
Wrap
C/C++ Source or Header
|
1993-03-07
|
10KB
|
361 lines
/*
* General skeleton for adding options to the access control language. The
* Makefile describes how this alternative language is enabled. Shell
* commands will still be available, be it with a slightly different syntax.
*
* The code uses a slightly different format of access control rules. It
* assumes that an access control rule looks like this:
*
* daemon_list : client_list : option : option ...
*
* An option is of the form "keyword" or "keyword = value". Option fields are
* processed from left to right. Blanks around keywords, "=" and values are
* optional. Blanks within values are left alone.
*
* Diagnostics are reported through syslog(3).
*
* Examples of options that are already implemented by the current skeleton:
*
* user = nobody
*
* Causes the process to switch its user id to that of "nobody". This normally
* requires root privilege.
*
* group = tty
*
* Causes the process to change its group id to that of the "tty" group. In
* order to switch both user and group ids you should normally switch the
* group id before switching the user id.
*
* setenv = name value
*
* places a name,value pair into the environment. The value is subjected to
* %<character> expansions.
*
* spawn = (/usr/ucb/finger -l @%h | /usr/ucb/mail root) &
*
* Executes (in a background child process) the shell command "finger -l @%h |
* mail root" after doing the %<character> expansions described in the
* hosts_access(5) manual page. The command is executed with stdin, stdout
* and stderr connected to the null device. Because options are processed in
* order, multiple spawn comands can be specified within the same access
* control rule, though "spawn = command1; command2" would be more
* efficient.
*
* in.ftpd : ... : twist = /bin/echo 421 Some customized bounce message
*
* Sends some custmized bounce message to the remote client instead of running
* the real ftp daemon. The command is subjected to %<character> expansion
* before execution by /bin/sh. Stdin, stdout and stderr are connected to the
* remote client process. The twist'ed command overlays the current process;
* it makes no sense to specify other options on the same line after a
* "twist". The "twist" option was inspired by Dan Bernstein's shuctl daemon
* wrapper control language.
*
* umask = value
*
* Sets the process file creation mask. Value must be an octal number.
*
* If you compile with -DRFC_OPTION, code is enabled for the following option
* that does selective rfc931 lookups.
*
* rfc931
*
* Causes the daemon front ends to look up the remote user name with the RFC
* 931 protocol.
*
* Warnings:
*
* This module uses the non-reentrant strtok() library routine. The options
* argument to process_options() is destroyed.
*
* There cannot be a ":" character in keywords or values. Backslash sequences
* are not yet recognized.
*
* In case of UDP connections, do not "twist" commands that use the standard
* I/O or read(2)/write(2) routines to communicate with the client process;
* UDP requires other communications primitives.
*
* In case of errors, use clean_exit() instead of directly calling exit(), or
* your inetd may loop on an UDP request.
*/
/* System libraries. */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <pwd.h>
#include <grp.h>
#include <ctype.h>
extern char *strtok();
extern char *strchr();
extern void closelog();
/* Local stuff. */
#include "log_tcp.h"
/* List of functions that implement the options. Add yours here. */
static void user_option(); /* execute "user=name" option */
static void group_option(); /* execute "group=name" option */
static void umask_option(); /* execute "umask=mask" option */
static void twist_option(); /* execute "twist=command" option */
#ifdef RFC931_OPTION
static void rfc931_option(); /* execute "rfc931" option */
#endif
static void setenv_option(); /* execute "setenv=name value" */
static char *chop_string(); /* strip leading and trailing blanks */
/* Structure of the options table. */
struct option {
char *name; /* keyword name, case does not matter */
int need_value; /* value required or not */
void (*func) (); /* function that does the real work */
};
/* List of known keywords. Add yours here. */
static struct option option_table[] = {
"user", 1, user_option, /* switch user id */
"group", 1, group_option, /* switch group id */
"umask", 1, umask_option, /* change umask */
"spawn", 1, shell_cmd, /* spawn shell command */
"twist", 1, twist_option, /* replace current process */
#ifdef RFC931_OPTION
"rfc931", 0, rfc931_option, /* do RFC 931 lookup */
#endif
"setenv", 1, setenv_option, /* update environment */
0,
};
static char whitespace[] = " \t\r\n";
/* process_options - process optional access control information */
process_options(options, daemon, client)
char *options;
char *daemon;
struct from_host *client;
{
char *key;
char *value;
struct option *op;
/*
* Light-weight parser. Remember, we may be running as root so we need
* code that is easy to comprehend.
*/
for (key = strtok(options, ":"); key; key = strtok((char *) 0, ":")) {
if (value = strchr(key, '=')) { /* keyword=value */
*value++ = 0;
value = chop_string(value); /* strip blanks around value */
if (*value == 0)
value = 0; /* no value left */
}
key = chop_string(key); /* strip blanks around key */
for (op = option_table; op->name; op++) /* find keyword */
if (strcasecmp(op->name, key) == 0)
break;
if (op->name == 0) {
syslog(LOG_ERR, "bad option or syntax: \"%s\"", key);
} else if (value == 0 && op->need_value) {
syslog(LOG_ERR, "option \"%s\" requires value", key);
} else if (value && op->need_value == 0) {
syslog(LOG_ERR, "option \"%s\" requires no value", key);
} else {
(*(op->func)) (value, daemon, client);
}
}
}
/* user_option - switch user id */
/* ARGSUSED */
static void user_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
struct passwd *pwd;
struct passwd *getpwnam();
if ((pwd = getpwnam(value)) == 0) {
syslog(LOG_ERR, "unknown user: \"%s\"", value);
clean_exit(client);
} else if (setuid(pwd->pw_uid)) {
syslog(LOG_ERR, "setuid(%s): %m", value);
clean_exit(client);
}
}
/* group_option - switch group id */
/* ARGSUSED */
static void group_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
struct group *grp;
struct group *getgrnam();
if ((grp = getgrnam(value)) == 0) {
syslog(LOG_ERR, "unknown group: \"%s\"", value);
clean_exit(client);
} else if (setgid(grp->gr_gid)) {
syslog(LOG_ERR, "setgid(%s): %m", value);
clean_exit(client);
}
}
/* umask_option - set file creation mask */
/* ARGSUSED */
static void umask_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
unsigned mask;
char junk;
if (sscanf(value, "%o%c", &mask, &junk) != 1 || (mask & 0777) != mask) {
syslog(LOG_ERR, "bad umask: \"%s\"", value);
clean_exit(client);
}
(void) umask(mask);
}
/* twist_option - replace process by shell command */
static void twist_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
char buf[BUFSIZ];
int pid = getpid();
char *error;
percent_x(buf, sizeof(buf), value, daemon, client, pid);
/* Since we will not be logging in the usual way, do it here and now. */
syslog(SEVERITY, "twist from %s to %s", hosts_info(client), buf);
closelog();
/*
* Before switching to the shell, set up stdout and stderr in case the
* Ultrix inetd didn't.
*/
(void) close(1);
(void) close(2);
if (dup(0) != 1 || dup(0) != 2) {
error = "dup: %m";
} else {
(void) execl("/bin/sh", "sh", "-c", buf, (char *) 0);
error = "/bin/sh: %m";
}
/* Can get here only in case of errors. */
#ifdef LOG_MAIL
(void) openlog(daemon, LOG_PID, FACILITY);
#else
(void) openlog(daemon, LOG_PID);
#endif
syslog(LOG_ERR, error);
clean_exit(client);
}
#ifdef RFC931_OPTION
/* rfc931_option - look up remote user name */
/* ARGSUSED */
static void rfc931_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
if (client->sock_type == FROM_CONNECTED) {
if (client->sin == 0) {
syslog(LOG_ERR, "no socket info for username lookup");
} else {
client->user = rfc931_name(client->sin);
}
}
}
#endif
/* setenv_option - set environment variable */
/* ARGSUSED */
static void setenv_option(value, daemon, client)
char *value;
char *daemon;
struct from_host *client;
{
char *var_name;
char *var_value;
char buf[BUFSIZ];
int pid;
/*
* What we get is one string with the name and the value separated by
* whitespace. Find the end of the name. If that is also the end of the
* string, the value is empty.
*/
var_value = value + strcspn(value, whitespace);
if (*var_value == 0) { /* just a name, that's all */
var_name = value;
} else { /* expand %stuff in value */
*var_value++ = 0;
var_name = chop_string(value);
pid = getpid();
percent_x(buf, sizeof(buf), var_value, daemon, client, pid);
var_value = chop_string(buf);
}
if (setenv(var_name, var_value, 1)) {
syslog(LOG_ERR, "memory allocation failure");
clean_exit(client);
}
}
/* chop_string - strip leading and trailing blanks from string */
static char *chop_string(start)
register char *start;
{
register char *end;
while (*start && isspace(*start))
start++;
for (end = start + strlen(start); end > start && isspace(end[-1]); end--)
/* void */ ;
*end = 0;
return (start);
}