forked from public/pyceo
Add mail changing
This allows office staff to update people's .forward files via ceod.
This commit is contained in:
parent
4c1a7f8ee4
commit
9eefe615c5
|
@ -0,0 +1 @@
|
||||||
|
ginseng mail 0x02
|
|
@ -5,6 +5,7 @@
|
||||||
/addclub
|
/addclub
|
||||||
/adduser
|
/adduser
|
||||||
/op-adduser
|
/op-adduser
|
||||||
|
/op-mail
|
||||||
/zfsaddhomedir
|
/zfsaddhomedir
|
||||||
/config-test
|
/config-test
|
||||||
/ceod
|
/ceod
|
||||||
|
|
|
@ -7,7 +7,7 @@ DESTDIR :=
|
||||||
PREFIX := /usr/local
|
PREFIX := /usr/local
|
||||||
|
|
||||||
BIN_PROGS := addmember addclub ceod
|
BIN_PROGS := addmember addclub ceod
|
||||||
LIB_PROGS := ceoc op-adduser
|
LIB_PROGS := ceoc op-adduser op-mail
|
||||||
EXT_PROGS := config-test
|
EXT_PROGS := config-test
|
||||||
|
|
||||||
LDAP_OBJECTS := ldap.o
|
LDAP_OBJECTS := ldap.o
|
||||||
|
@ -24,10 +24,10 @@ NET_LIBS := -lsctp $(shell krb5-config --libs gssapi)
|
||||||
NET_PROGS := ceod ceoc
|
NET_PROGS := ceod ceoc
|
||||||
PROTO_OBJECTS := ceo.pb-c.o
|
PROTO_OBJECTS := ceo.pb-c.o
|
||||||
PROTO_LIBS := -lprotobuf-c
|
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_OBJECTS := config.o parser.o
|
||||||
CONFIG_LIBS :=
|
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_OBJECTS := util.o strbuf.o
|
||||||
UTIL_PROGS := config-test $(CONFIG_PROGS)
|
UTIL_PROGS := config-test $(CONFIG_PROGS)
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ install_daemon:
|
||||||
install -d $(DESTDIR)$(PREFIX)/sbin $(DESTDIR)$(PREFIX)/lib/ceod
|
install -d $(DESTDIR)$(PREFIX)/sbin $(DESTDIR)$(PREFIX)/lib/ceod
|
||||||
install ceod $(DESTDIR)$(PREFIX)/sbin
|
install ceod $(DESTDIR)$(PREFIX)/sbin
|
||||||
install op-adduser $(DESTDIR)$(PREFIX)/lib/ceod
|
install op-adduser $(DESTDIR)$(PREFIX)/lib/ceod
|
||||||
|
install op-mail $(DESTDIR)$(PREFIX)/lib/ceod
|
||||||
|
|
||||||
install: install_clients install_daemon
|
install: install_clients install_daemon
|
||||||
|
|
||||||
|
|
|
@ -22,3 +22,12 @@ message AddUser {
|
||||||
message AddUserResponse {
|
message AddUserResponse {
|
||||||
repeated StatusMessage messages = 1;
|
repeated StatusMessage messages = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message UpdateMail {
|
||||||
|
required string username = 1;
|
||||||
|
optional string forward = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdateMailResponse {
|
||||||
|
repeated StatusMessage messages = 1;
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ Ceo__AddUserResponse *response_create(void) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRINTF_LIKE(2)
|
||||||
int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) {
|
int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));
|
Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue