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