Add mail changing

This allows office staff to update people's .forward files via ceod.
This commit is contained in:
Michael Spang 2009-08-22 20:21:28 -04:00
parent 4c1a7f8ee4
commit 9eefe615c5
6 changed files with 230 additions and 3 deletions

1
etc/ops/mail Normal file
View File

@ -0,0 +1 @@
ginseng mail 0x02

1
src/.gitignore vendored
View File

@ -5,6 +5,7 @@
/addclub
/adduser
/op-adduser
/op-mail
/zfsaddhomedir
/config-test
/ceod

View File

@ -7,7 +7,7 @@ DESTDIR :=
PREFIX := /usr/local
BIN_PROGS := addmember addclub ceod
LIB_PROGS := ceoc op-adduser
LIB_PROGS := ceoc op-adduser op-mail
EXT_PROGS := config-test
LDAP_OBJECTS := ldap.o
@ -24,10 +24,10 @@ NET_LIBS := -lsctp $(shell krb5-config --libs gssapi)
NET_PROGS := ceod ceoc
PROTO_OBJECTS := ceo.pb-c.o
PROTO_LIBS := -lprotobuf-c
PROTO_PROGS := op-adduser addmember addclub
PROTO_PROGS := op-adduser op-mail addmember addclub
CONFIG_OBJECTS := config.o parser.o
CONFIG_LIBS :=
CONFIG_PROGS := $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS)
CONFIG_PROGS := $(LDAP_PROGS) $(KRB5_PROGS) $(NET_PROGS) $(PROTO_PROGS)
UTIL_OBJECTS := util.o strbuf.o
UTIL_PROGS := config-test $(CONFIG_PROGS)
@ -63,6 +63,7 @@ install_daemon:
install -d $(DESTDIR)$(PREFIX)/sbin $(DESTDIR)$(PREFIX)/lib/ceod
install ceod $(DESTDIR)$(PREFIX)/sbin
install op-adduser $(DESTDIR)$(PREFIX)/lib/ceod
install op-mail $(DESTDIR)$(PREFIX)/lib/ceod
install: install_clients install_daemon

View File

@ -22,3 +22,12 @@ message AddUser {
message AddUserResponse {
repeated StatusMessage messages = 1;
}
message UpdateMail {
required string username = 1;
optional string forward = 2;
}
message UpdateMailResponse {
repeated StatusMessage messages = 1;
}

View File

@ -42,6 +42,7 @@ Ceo__AddUserResponse *response_create(void) {
return r;
}
PRINTF_LIKE(2)
int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) {
va_list args;
Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));

214
src/op-mail.c Normal file
View File

@ -0,0 +1,214 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#include <netdb.h>
#include <alloca.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <ctype.h>
#include "util.h"
#include "net.h"
#include "ceo.pb-c.h"
#include "config.h"
#include "strbuf.h"
char *prog;
static const int MAX_MESSAGES = 32;
static const int MAX_MESGSIZE = 512;
Ceo__UpdateMailResponse *response_create(void) {
Ceo__UpdateMailResponse *r = xmalloc(sizeof(Ceo__UpdateMailResponse));
ceo__update_mail_response__init(r);
r->n_messages = 0;
r->messages = xmalloc(MAX_MESSAGES * sizeof(Ceo__StatusMessage *));
return r;
}
PRINTF_LIKE(2)
int32_t response_message(Ceo__UpdateMailResponse *r, int32_t status, char *fmt, ...) {
va_list args;
Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));
char *message = xmalloc(MAX_MESGSIZE);
va_start(args, fmt);
vsnprintf(message, MAX_MESGSIZE, fmt, args);
va_end(args);
ceo__status_message__init(statusmsg);
statusmsg->status = status;
statusmsg->message = message;
if (r->n_messages >= MAX_MESSAGES)
fatal("too many messages");
r->messages[r->n_messages++] = statusmsg;
if (status)
error("%s", message);
else
notice("%s", message);
return status;
}
void response_delete(Ceo__UpdateMailResponse *r) {
int i;
for (i = 0; i < r->n_messages; i++) {
free(r->messages[i]->message);
free(r->messages[i]);
}
free(r->messages);
free(r);
}
static int check_update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) {
int client_office = check_group(client, "office");
int client_syscom = check_group(client, "syscom");
notice("update mail uid=%s mail=%s by %s", in->username, in->forward, client);
if (!in->username)
return response_message(out, EINVAL, "missing required argument: username");
int recipient_syscom = check_group(in->username, "syscom");
if (!client_syscom && !client_office && strcmp(in->username, client))
return response_message(out, EPERM, "%s not authorized to update mail", client);
if (recipient_syscom && !client_syscom)
return response_message(out, EPERM, "denied, recipient is on systems committee");
/* don't allow office staff to set complicated forwards; in particular | is a security hole */
if (in->forward) {
for (char *p = in->forward; *p; p++) {
switch (*p) {
case '"':
case '\'':
case ',':
case '|':
case '$':
case '/':
case '#':
case ':':
return response_message(out, EINVAL, "invalid character in forward: %c", *p);
default:
break;
}
if (isspace(*p))
return response_message(out, EINVAL, "invalid character in forward: %c", *p);
}
}
return 0;
}
static int32_t update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) {
int32_t chk_stat;
mode_t mask;
chk_stat = check_update_mail(in, out, client);
if (chk_stat)
return chk_stat;
mask = umask(0);
if (in->forward) {
struct passwd *user = getpwnam(in->username);
if (!user)
return response_message(out, errno, "getpwnam: %s: %s", in->username, strerror(errno));
if (setregid(user->pw_gid, user->pw_gid))
return response_message(out, errno, "setregid: %s: %s", in->username, strerror(errno));
if (setreuid(user->pw_uid, user->pw_uid))
return response_message(out, errno, "setreuid: %s: %s", in->username, strerror(errno));
char path[1024];
if (snprintf(path, sizeof(path), "%s/.forward", user->pw_dir) >= sizeof(path))
return response_message(out, ENAMETOOLONG, "homedir is too long");
if (unlink(path) && errno != ENOENT)
return response_message(out, errno, "unlink: %s: %s", path, strerror(errno));
if (*in->forward) {
int fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0644);
if (fd < 0)
return response_message(out, errno, "open: %s: %s", path, strerror(errno));
struct strbuf file_contents = STRBUF_INIT;
strbuf_addf(&file_contents, "%s\n", in->forward);
full_write(fd, file_contents.buf, file_contents.len);
strbuf_release(&file_contents);
if (close(fd))
return response_message(out, errno, "close: %s: %s", path, strerror(errno));
response_message(out, 0, "successfully updated forward for %s", in->username);
} else {
response_message(out, 0, "successfully cleared forward for %s", in->username);
}
}
umask(mask);
return response_message(out, 0, "finished updating mail for %s", in->username);
}
void cmd_update_mail(void) {
Ceo__UpdateMail *in_proto;
Ceo__UpdateMailResponse *out_proto = response_create();
struct strbuf in = STRBUF_INIT;
struct strbuf out = STRBUF_INIT;
if (strbuf_read(&in, STDIN_FILENO, 0) < 0)
fatalpe("read");
in_proto = ceo__update_mail__unpack(&protobuf_c_default_allocator,
in.len, (uint8_t *)in.buf);
if (!in_proto)
fatal("malformed update mail message");
char *client = getenv("CEO_USER");
if (!client)
fatal("environment variable CEO_USER is not set");
update_mail(in_proto, out_proto, client);
strbuf_grow(&out, ceo__update_mail_response__get_packed_size(out_proto));
strbuf_setlen(&out, ceo__update_mail_response__pack(out_proto, (uint8_t *)out.buf));
full_write(STDOUT_FILENO, out.buf, out.len);
ceo__update_mail__free_unpacked(in_proto, &protobuf_c_default_allocator);
response_delete(out_proto);
strbuf_release(&in);
strbuf_release(&out);
}
int main(int argc, char *argv[]) {
prog = xstrdup(basename(argv[0]));
init_log(prog, LOG_PID, LOG_AUTHPRIV, 0);
configure();
cmd_update_mail();
free_config();
free(prog);
return 0;
}