/* $Cambridge: hermes/src/prayer/accountd/accountd.c,v 1.3 2004/07/16 09:36:11 dpc22 Exp $ */
/************************************************
* Prayer - a Webmail Interface *
************************************************/
/* Copyright (c) University of Cambridge 2000 - 2002 */
/* See the file NOTICE for conditions of use and distribution. */
/* Prayer account management daemon */
#include "accountd.h"
static void ucase(char *s)
{
while (*s) {
*s = Utoupper(*s);
s++;
}
}
/* ====================================================================== */
/* Process line of input once authenicated */
static BOOL
do_authenicated_line(struct config *config,
char *line, struct iostream *stream, char *username)
{
char *cmd;
if (!(cmd = string_get_token(&line))) {
ioputs(stream, "BAD Invalid command" CRLF);
ioflush(stream);
return (T);
}
ucase(cmd);
log_misc("%s: %s", username, cmd);
switch (cmd[0]) {
case 'C':
if (!strcmp(cmd, "CHECKFULLNAME")) {
fullname_check(config, stream);
return (T);
}
break;
case 'F':
if (!strcmp(cmd, "FULLNAME"))
return (fullname_change(config, stream, line));
break;
case 'G':
if (!strcmp(cmd, "GET"))
return (file_get(config, stream, line));
break;
case 'M':
/* Merge into single mail_upload command */
if (!strcmp(cmd, "MAILSTATUS"))
return (mail_status(config, stream, line));
else if (!strcmp(cmd, "MAILCHANGE"))
return (mail_change(config, stream, line));
break;
case 'L':
if (!strcmp(cmd, "LOGOUT")) {
ioputs(stream, "OK Logout" CRLF);
ioflush(stream);
return (NIL);
} else if (!strcmp(cmd, "LOGIN")) {
ioputs(stream, "NO Already authenicated" CRLF);
ioflush(stream);
return (T);
}
break;
case 'P':
if (!strcmp(cmd, "PASSWORD"))
return (password_change(config, stream, line));
else if (!strcmp(cmd, "PUT"))
return (file_put(config, stream, line));
break;
case 'Q':
if (!strcmp(cmd, "QUOTA")) {
quota_check(config, stream);
return (T);
}
break;
case 'V':
if (!strcmp(cmd, "VACATION_CLEAR"))
return (mail_vacclear(config, stream, line));
break;
}
ioputs(stream, "BAD Unknown command" CRLF);
ioflush(stream);
return (T);
}
/* ====================================================================== */
/* Run a single accountd session on the nominated socket descriptor */
static BOOL session(struct config *config, int sockfd)
{
char buffer[MAXLENGTH];
char *username = NIL;
struct iostream *stream;
if (!
(stream =
iostream_create(NIL, sockfd, IOSTREAM_PREFERRED_BLOCK_SIZE)))
return (NIL);
iostream_set_timeout(stream, ACCOUNTD_TIMEOUT);
if (config->use_ssl && !iostream_ssl_start_server(stream))
return (NIL);
if (getuid() == 0) {
if (!authenicate(config, stream, &username))
return (NIL);
} else {
if (!authenicate_preauth(config, stream, &username))
return (NIL);
}
if (getuid() == 0) {
log_fatal("Authenicated session running with root privilege");
/* NOTREACHED */
exit(1);
}
log_misc("User login: %s", username);
while (1) {
if (!iostream_getline(stream, buffer, MAXLENGTH))
break;
if (buffer[0] == '\0') /* Ignore empty lines */
continue;
if (!do_authenicated_line(config, buffer, stream, username))
break;
}
log_misc("User logout: %s", username);
return (T);
}
/* ====================================================================== */
/* Code for running accountd as standalone daemon rather than from inetd */
void rundaemon(struct config *config, int use_fork, int port)
{
struct ipaddr ipaddr;
int sockfd, newsockfd;
pid_t childpid;
if ((sockfd = os_bind_inet_socket(port)) < 0)
log_fatal("socket() failed");
os_signal_child_init(os_child_reaper);
if (config->use_ssl)
iostream_init(config); /* Required for SSL stuff */
for (;;) {
if ((newsockfd = os_accept_inet(sockfd, &ipaddr)) < 0)
log_fatal("accept() failed: errno = %d", errno);
log_misc("Incoming connection from %s", ipaddr_text(&ipaddr));
if (use_fork) {
if ((childpid = fork()) < 0)
log_fatal("fork() failed: errno = %d", errno);
if (childpid == 0) {
/* Child process */
close(sockfd);
os_signal_child_clear();
session(config, newsockfd);
close(newsockfd);
exit(0);
}
} else
session(config, newsockfd);
/* Parent */
close(newsockfd);
#ifdef USE_SSL
/* Replace (shared) RSA key every few minutes */
if (config->use_ssl)
iostream_check_rsakey(config);
#endif
}
}
/* ====================================================================== */
/* Main routine:
* Parse command line options, then run as either permanant daemon or
* single shot login using file descriptor 0 for input and output */
int main(int argc, char **argv)
{
BOOL use_fork = T;
BOOL hermes = NIL;
char *config_filename = ACCOUNTD_CONFIG_FILE;
struct config *config = config_create();
int i;
/* Disable supplementary groups, switch to group "other" (if it exists) */
if (getuid() == 0) {
struct group *group;
setgroups(0, NIL);
if ((group = getgrnam("hermes")) != NIL)
setgid(group->gr_gid);
if ((group = getgrnam("other")) != NIL)
setgid(group->gr_gid);
else if ((group = getgrnam("user")) != NIL)
setgid(group->gr_gid);
}
if (getenv("ACCOUNTD_CONFIG_FILE"))
config_filename = getenv("ACCOUNTD_CONFIG_FILE");
log_init(config, argv[0]);
/* Minimal Umask: don't want anyone reading user .forward files. */
umask(0077);
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--enable-fork"))
use_fork = T;
else if (!strcmp(argv[i], "--disable-fork"))
use_fork = NIL;
else if (!strcmp(argv[i], "--hermes")) {
hermes = T;
} else if (!strncmp(argv[i],
"--config-file=", strlen("--config-file=")))
config_filename = strdup(argv[i] + strlen("--config-file="));
else if (!strcmp(argv[i], "--config-option"))
i++; /* Processes next argv */
else if (!strcmp(argv[i], "--help")) {
fprintf(stderr, "Command Line Options:\n");
fprintf(stderr,
(" --config-file=x "
"Define location of configuration file\n"));
fprintf(stderr,
(" --config-option x=y "
"Override option from configuration file\n"));
fprintf(stderr,
(" --enable-fork "
"Allow fork if running as daemon (default)\n"));
fprintf(stderr,
(" --disable-fork "
"Disable fork if running as daemon (debugging)\n"));
exit(0);
} else
log_fatal("Unknown command line option");
}
if (!config_parse_file(config, config_filename))
exit(1);
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--config-option")) {
if (++i < argc) {
if (!config_parse_option(config, strdup(argv[i])))
exit(1);
} else
fprintf(stderr, "--config processes following option");
}
}
if (!config_check(config))
exit(1);
if (hermes)
config->filter_restricted = T;
/* Required for SSL stuff */
#ifdef USE_SSL
if (config->use_ssl)
iostream_init(config);
#endif
/* Run as daemon on nominated port */
if (config->accountd_port) {
rundaemon(config, use_fork, config->accountd_port);
/* NOTREACHED */
exit(0);
}
/* Otherwise run as single shot session on file descriptor 0
* i.e: inetd service */
session(config, 0);
exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1