26 static const int MAX_MESSAGES = 32;
27 static const int MAX_MESGSIZE = 512;
29 Ceo__UpdateMailResponse *response_create(void) {
30 Ceo__UpdateMailResponse *r = xmalloc(sizeof(Ceo__UpdateMailResponse));
31 ceo__update_mail_response__init(r);
33 r->messages = xmalloc(MAX_MESSAGES * sizeof(Ceo__StatusMessage *));
38 int32_t response_message(Ceo__UpdateMailResponse *r, int32_t status, char *fmt, ...) {
40 Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));
41 char *message = xmalloc(MAX_MESGSIZE);
44 vsnprintf(message, MAX_MESGSIZE, fmt, args);
47 ceo__status_message__init(statusmsg);
48 statusmsg->status = status;
49 statusmsg->message = message;
51 if (r->n_messages >= MAX_MESSAGES)
52 fatal("too many messages");
53 r->messages[r->n_messages++] = statusmsg;
58 notice("%s", message);
63 void response_delete(Ceo__UpdateMailResponse *r) {
66 for (i = 0; i < r->n_messages; i++) {
67 free(r->messages[i]->message);
74 static int check_update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) {
75 int client_office = check_group(client, "office");
76 int client_syscom = check_group(client, "syscom");
78 notice("update mail uid=%s mail=%s by %s", in->username, in->forward, client);
81 return response_message(out, EINVAL, "missing required argument: username");
83 int recipient_syscom = check_group(in->username, "syscom");
85 if (!client_syscom && !client_office && strcmp(in->username, client))
86 return response_message(out, EPERM, "%s not authorized to update mail", client);
88 if (recipient_syscom && !client_syscom)
89 return response_message(out, EPERM, "denied, recipient is on systems committee");
91 /* don't allow office staff to set complicated forwards; in particular | is a security hole */
93 for (char *p = in->forward; *p; p++) {
103 return response_message(out, EINVAL, "invalid character in forward: %c", *p);
109 return response_message(out, EINVAL, "invalid character in forward: %c", *p);
116 static int32_t update_mail(Ceo__UpdateMail *in, Ceo__UpdateMailResponse *out, char *client) {
120 chk_stat = check_update_mail(in, out, client);
127 struct passwd *user = getpwnam(in->username);
130 return response_message(out, errno, "getpwnam: %s: %s", in->username, strerror(errno));
132 if (setregid(user->pw_gid, user->pw_gid))
133 return response_message(out, errno, "setregid: %s: %s", in->username, strerror(errno));
134 if (setreuid(user->pw_uid, user->pw_uid))
135 return response_message(out, errno, "setreuid: %s: %s", in->username, strerror(errno));
139 if (snprintf(path, sizeof(path), "%s/.forward", user->pw_dir) >= sizeof(path))
140 return response_message(out, ENAMETOOLONG, "homedir is too long");
142 if (unlink(path) && errno != ENOENT)
143 return response_message(out, errno, "unlink: %s: %s", path, strerror(errno));
146 int fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0644);
148 return response_message(out, errno, "open: %s: %s", path, strerror(errno));
150 struct strbuf file_contents = STRBUF_INIT;
151 strbuf_addf(&file_contents, "%s\n", in->forward);
153 if (full_write(fd, file_contents.buf, file_contents.len))
154 response_message(out, errno, "write: %s: %s", path, strerror(errno));
156 strbuf_release(&file_contents);
159 return response_message(out, errno, "close: %s: %s", path, strerror(errno));
161 response_message(out, 0, "successfully updated forward for %s", in->username);
163 response_message(out, 0, "successfully cleared forward for %s", in->username);
169 return response_message(out, 0, "finished updating mail for %s", in->username);
172 void cmd_update_mail(void) {
173 Ceo__UpdateMail *in_proto;
174 Ceo__UpdateMailResponse *out_proto = response_create();
175 struct strbuf in = STRBUF_INIT;
176 struct strbuf out = STRBUF_INIT;
178 if (strbuf_read(&in, STDIN_FILENO, 0) < 0)
181 in_proto = ceo__update_mail__unpack(&protobuf_c_default_allocator,
182 in.len, (uint8_t *)in.buf);
184 fatal("malformed update mail message");
186 char *client = getenv("CEO_USER");
188 fatal("environment variable CEO_USER is not set");
190 update_mail(in_proto, out_proto, client);
192 strbuf_grow(&out, ceo__update_mail_response__get_packed_size(out_proto));
193 strbuf_setlen(&out, ceo__update_mail_response__pack(out_proto, (uint8_t *)out.buf));
195 if (full_write(STDOUT_FILENO, out.buf, out.len))
196 fatalpe("write: stdout");
198 ceo__update_mail__free_unpacked(in_proto, &protobuf_c_default_allocator);
199 response_delete(out_proto);
202 strbuf_release(&out);
205 int main(int argc, char *argv[]) {
206 prog = xstrdup(basename(argv[0]));
207 init_log(prog, LOG_PID, LOG_AUTHPRIV, 0);