Readd quota support
[public/pyceo-broken.git] / src / op-adduser.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <signal.h>
5 #include <syslog.h>
6 #include <libgen.h>
7 #include <getopt.h>
8 #include <errno.h>
9 #include <netdb.h>
10 #include <alloca.h>
11 #include <pwd.h>
12 #include <grp.h>
13 #include <sys/wait.h>
14
15 #include "util.h"
16 #include "net.h"
17 #include "ceo.pb-c.h"
18 #include "config.h"
19 #include "gss.h"
20 #include "krb5.h"
21 #include "ldap.h"
22 #include "homedir.h"
23 #include "kadm.h"
24 #include "daemon.h"
25 #include "strbuf.h"
26
27 char *prog;
28
29 static const int MAX_MESSAGES = 32;
30 static const int MAX_MESGSIZE = 512;
31
32 char *user_types[] = {
33     [CEO__ADD_USER__TYPE__MEMBER] = "member",
34     [CEO__ADD_USER__TYPE__CLUB] = "club",
35 };
36
37 Ceo__AddUserResponse *response_create(void) {
38     Ceo__AddUserResponse *r = xmalloc(sizeof(Ceo__AddUserResponse));
39     ceo__add_user_response__init(r);
40     r->n_messages = 0;
41     r->messages = xmalloc(MAX_MESSAGES *  sizeof(Ceo__StatusMessage *));
42     return r;
43 }
44
45 PRINTF_LIKE(2)
46 int32_t response_message(Ceo__AddUserResponse *r, int32_t status, char *fmt, ...) {
47     va_list args;
48     Ceo__StatusMessage *statusmsg = xmalloc(sizeof(Ceo__StatusMessage));
49     char *message = xmalloc(MAX_MESGSIZE);
50
51     va_start(args, fmt);
52     vsnprintf(message, MAX_MESGSIZE, fmt, args);
53     va_end(args);
54
55     ceo__status_message__init(statusmsg);
56     statusmsg->status = status;
57     statusmsg->message = message;
58
59     if (r->n_messages >= MAX_MESSAGES)
60         fatal("too many messages");
61     r->messages[r->n_messages++] = statusmsg;
62
63     if (status)
64         error("%s", message);
65     else
66         notice("%s", message);
67
68     return status;
69 }
70
71 void response_delete(Ceo__AddUserResponse *r) {
72     int i;
73
74     for (i = 0; i < r->n_messages; i++) {
75         free(r->messages[i]->message);
76         free(r->messages[i]);
77     }
78     free(r->messages);
79     free(r);
80 }
81
82
83 static int check_adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) {
84     int office = check_group(client, "office");
85     int syscom = check_group(client, "syscom");
86
87     notice("adding uid=%s cn=%s by %s", in->username, in->realname, client);
88
89     if (!office && !syscom)
90         return response_message(out, EPERM, "%s not authorized to create users", client);
91
92     if (!in->username)
93         return response_message(out, EINVAL, "missing required argument: username");
94     if (!in->realname)
95         return response_message(out, EINVAL, "missing required argument: realname");
96
97     if (in->type == CEO__ADD_USER__TYPE__MEMBER) {
98         if (!in->password)
99             return response_message(out, EINVAL, "missing required argument: password");
100     } else if (in->type == CEO__ADD_USER__TYPE__CLUB) {
101         if (in->password)
102             return response_message(out, EINVAL, "club accounts cannot have passwords");
103         if (in->program)
104             return response_message(out, EINVAL, "club accounts cannot have programs");
105     } else {
106         return response_message(out, EINVAL, "invalid user type: %d", in->type);
107     }
108
109     if (getpwnam(in->username) != NULL)
110         return response_message(out, EEXIST, "user %s already exists", in->username);
111
112     if (getgrnam(in->username) != NULL)
113         return response_message(out, EEXIST, "group %s already exists", in->username);
114
115     if (ceo_user_exists(in->username))
116         return response_message(out, EEXIST, "user %s already exists in LDAP", in->username);
117
118     if (ceo_group_exists(in->username))
119         return response_message(out, EEXIST, "group %s already exists in LDAP", in->username);
120
121     return 0;
122 }
123
124 static void adduser_spam(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client, char *prog, int status) {
125     char *argv[] = {
126         notify_hook, prog, client,
127         in->username, in->realname, in->program ?: "",
128         status ? "failure" : "success", NULL
129     };
130
131     struct strbuf message = STRBUF_INIT;
132     for (int i = 0; i < out->n_messages; i++)
133         strbuf_addf(&message, "%s\n", out->messages[i]->message);
134
135     spawnv_msg(notify_hook, argv, &message);
136     strbuf_release(&message);
137 }
138
139 static int32_t addmember(Ceo__AddUser *in, Ceo__AddUserResponse *out) {
140     char homedir[1024];
141     char principal[1024];
142     int user_stat, group_stat, krb_stat, home_stat, quota_stat;
143     int id;
144
145     if (snprintf(principal, sizeof(principal), "%s@%s",
146                 in->username, krb5_realm) >= sizeof(principal))
147         fatal("principal overflow");
148
149     if (snprintf(homedir, sizeof(homedir), "%s/%s",
150                  member_home, in->username) >= sizeof(homedir))
151         fatal("homedir overflow");
152
153     if ((id = ceo_new_uid(member_min_id, member_max_id)) <= 0)
154         fatal("no available uids in range [%ld, %ld]", member_min_id, member_max_id);
155
156     if ((krb_stat = ceo_del_princ(in->username)))
157         return response_message(out, EEXIST, "unable to overwrite orphaned kerberos principal %s", in->username);
158
159     if ((user_stat = ceo_add_user(in->username, ldap_users_base, "member", in->realname, homedir, principal,
160             member_shell, id, "program", in->program, NULL)))
161         return response_message(out, ELDAP, "unable to create ldap account %s", in->username);
162     response_message(out, 0, "successfully created ldap account");
163
164     /* errors that occur after this point are not fatal  */
165
166     if ((krb_stat = ceo_add_princ(in->username, in->password)))
167         return response_message(out, EKERB, "unable to create kerberos principal %s", in->username);
168     response_message(out, 0, "successfully created principal");
169
170     if ((group_stat = ceo_add_group(in->username, ldap_groups_base, id)))
171         response_message(out, ELDAP, "unable to create ldap group %s", in->username);
172     else
173         response_message(out, 0, "successfully created ldap group");
174
175     if ((home_stat = ceo_create_home(homedir, member_home_skel, id, id, NULL, NULL, in->email)))
176         response_message(out, EHOME, "unable to create home directory for %s", in->username);
177     else
178         response_message(out, 0, "successfully created home directory");
179
180     if ((quota_stat = ceo_set_quota("ctdalek", id)))
181         response_message(out, EQUOTA, "unable to set quota for %s", in->username);
182     else
183         response_message(out, 0, "successfully set quota");
184
185     return krb_stat || user_stat || group_stat || home_stat || quota_stat;
186 }
187
188 static int32_t addclub(Ceo__AddUser *in, Ceo__AddUserResponse *out) {
189     char homedir[1024];
190     char acl[64];
191     int krb_stat, user_stat, group_stat, sudo_stat, home_stat, quota_stat;
192     int id;
193
194     if (snprintf(homedir, sizeof(homedir), "%s/%s", club_home, in->username) >= sizeof(homedir))
195         fatal("homedir overflow");
196
197     if ((id = ceo_new_uid(club_min_id, club_max_id)) <= 0)
198         fatal("no available uids in range [%ld, %ld]", club_min_id, club_max_id);
199
200     if (snprintf(acl, sizeof(acl), CLUB_ACL, id) >= sizeof(acl))
201         fatal("acl overflow");
202
203     if ((krb_stat = ceo_del_princ(in->username)))
204         return response_message(out, EKERB, "unable to clear principal %s", in->username);
205
206     if ((user_stat = ceo_add_user(in->username, ldap_users_base, "club", in->realname, homedir,
207             NULL, club_shell, id, NULL)))
208         return response_message(out, ELDAP, "unable to create ldap account %s", in->username);
209     response_message(out, 0, "successfully created ldap account");
210
211     /* errors that occur after this point are not fatal  */
212
213     if ((group_stat = ceo_add_group(in->username, ldap_groups_base, id)))
214         response_message(out, ELDAP, "unable to create ldap group %s", in->username);
215     else
216         response_message(out, 0, "successfully created ldap group");
217
218     if ((sudo_stat = ceo_add_group_sudo(in->username, ldap_sudo_base)))
219         response_message(out, ELDAP, "unable to create ldap sudoers %s", in->username);
220     else
221         response_message(out, 0, "successfully created ldap sudoers");
222
223     if ((home_stat = ceo_create_home(homedir, club_home_skel, id, id, acl, acl, NULL)))
224         response_message(out, EHOME, "unable to create home directory for %s", in->username);
225     else
226         response_message(out, 0, "successfully created home directory");
227
228     if ((quota_stat = ceo_set_quota("csc", id)))
229         response_message(out, EQUOTA, "unable to set quota for %s", in->username);
230     else
231         response_message(out, 0, "successfully set quota");
232
233     return user_stat || group_stat || sudo_stat || home_stat || quota_stat;
234 }
235
236 static int32_t adduser(Ceo__AddUser *in, Ceo__AddUserResponse *out, char *client) {
237     int32_t chk_stat, status;
238     char *prog;
239
240     chk_stat = check_adduser(in, out, client);
241     if (chk_stat)
242         return chk_stat;
243
244     if (in->type == CEO__ADD_USER__TYPE__MEMBER) {
245         status = addmember(in, out);
246         prog = "addmember";
247     } else if (in->type == CEO__ADD_USER__TYPE__CLUB) {
248         status = addclub(in, out);
249         prog = "addclub";
250     } else {
251         fatal("unknown user type %d", in->type);
252     }
253
254     if (status)
255         response_message(out, 0, "there were failures, please contact systems committee");
256
257     adduser_spam(in, out, client, prog, status);
258
259     return status;
260 }
261
262 void cmd_adduser(void) {
263     Ceo__AddUser *in_proto;
264     Ceo__AddUserResponse *out_proto = response_create();
265     struct strbuf in = STRBUF_INIT;
266     struct strbuf out = STRBUF_INIT;
267
268     if (strbuf_read(&in, STDIN_FILENO, 0) < 0)
269         fatalpe("read");
270
271     in_proto = ceo__add_user__unpack(&protobuf_c_default_allocator,
272             in.len, (uint8_t *)in.buf);
273     if (!in_proto)
274         fatal("malformed add user message");
275
276     char *client = getenv("CEO_USER");
277     if (!client)
278         fatal("environment variable CEO_USER is not set");
279
280     adduser(in_proto, out_proto, client);
281
282     strbuf_grow(&out, ceo__add_user_response__get_packed_size(out_proto));
283     strbuf_setlen(&out, ceo__add_user_response__pack(out_proto, (uint8_t *)out.buf));
284
285     if (full_write(STDOUT_FILENO, out.buf, out.len))
286         fatalpe("write: stdout");
287
288     ceo__add_user__free_unpacked(in_proto, &protobuf_c_default_allocator);
289     response_delete(out_proto);
290
291     strbuf_release(&in);
292     strbuf_release(&out);
293 }
294
295 int main(int argc, char *argv[]) {
296     prog = xstrdup(basename(argv[0]));
297     init_log(prog, LOG_PID, LOG_AUTHPRIV, 0);
298
299     configure();
300
301     if (setenv("KRB5CCNAME", "MEMORY:adduser", 1))
302         fatalpe("setenv");
303
304     ceo_krb5_init();
305     ceo_krb5_auth(ldap_admin_principal);
306     ceo_ldap_init();
307     ceo_kadm_init();
308
309     cmd_adduser();
310
311     ceo_kadm_cleanup();
312     ceo_ldap_cleanup();
313     ceo_krb5_deauth();
314     ceo_krb5_cleanup();
315
316     free_config();
317     free(prog);
318
319     return 0;
320 }