Forbid adding users who have a group's name
[public/pyceo-broken.git] / src / ldap.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pwd.h>
4 #include <grp.h>
5 #include <sasl/sasl.h>
6 #include <krb5.h>
7
8 #define LDAP_DEPRECATED 1
9 #include <ldap.h>
10
11 #include "ldap.h"
12 #include "krb5.h"
13 #include "config.h"
14 #include "util.h"
15
16 extern char *prog;
17
18 LDAP *ld;
19
20 static void ldap_fatal(char *msg) {
21     int errnum;
22     char *errstr, *detail;
23
24     ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum);
25     ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail);
26
27     errstr = ldap_err2string(errnum);
28
29     if (detail && *detail)
30         fatal("%s: %s (%d): %s", msg, errstr, errnum, detail);
31     else
32         fatal("%s: %s (%d)", msg, errstr, errnum);
33 }
34
35 static void ldap_err(char *msg) {
36     int errnum;
37     char *errstr, *detail;
38
39     ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum);
40     ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail);
41
42     errstr = ldap_err2string(errnum);
43
44     if (detail && *detail)
45         error("%s: %s (%d): %s", msg, errstr, errnum, detail);
46     else
47         error("%s: %s (%d)", msg, errstr, errnum);
48 }
49
50 int ceo_add_group(char *cn, char *basedn, int no) {
51     if (!cn || !basedn)
52         fatal("addgroup: Invalid argument");
53
54     LDAPMod *mods[8];
55     int i = -1;
56     int ret = 0;
57
58     mods[++i] = xmalloc(sizeof(LDAPMod));
59     mods[i]->mod_op = LDAP_MOD_ADD;
60     mods[i]->mod_type = "objectClass";
61     char *objectClasses[] = { "top", "group", "posixGroup", NULL };
62     mods[i]->mod_values = objectClasses;
63
64     mods[++i] = xmalloc(sizeof(LDAPMod));
65     mods[i]->mod_op = LDAP_MOD_ADD;
66     mods[i]->mod_type = "cn";
67     char *uids[] = { cn, NULL };
68     mods[i]->mod_values = uids;
69
70     mods[++i] = xmalloc(sizeof(LDAPMod));
71     mods[i]->mod_op = LDAP_MOD_ADD;
72     mods[i]->mod_type = "gidNumber";
73     char idno[16];
74     snprintf(idno, sizeof(idno), "%d", no);
75     char *gidNumbers[] = { idno, NULL };
76     mods[i]->mod_values = gidNumbers;
77
78     mods[++i] = NULL;
79
80     char dn[1024];
81     snprintf(dn, sizeof(dn), "cn=%s,%s", cn, basedn);
82
83     if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) {
84         ldap_err("addgroup");
85         ret = -1;
86     }
87
88     i = 0;
89     while (mods[i])
90         free(mods[i++]);
91
92     return ret;
93 }
94
95 int ceo_add_group_sudo(char *group, char *basedn) {
96     if (!group || !basedn)
97         fatal("addgroup: Invalid argument");
98
99     LDAPMod *mods[8];
100     int i = -1;
101     int ret = 0;
102
103     char cn[17];
104     snprintf(cn, sizeof(cn), "%%%s", group);
105
106     mods[++i] = xmalloc(sizeof(LDAPMod));
107     mods[i]->mod_op = LDAP_MOD_ADD;
108     mods[i]->mod_type = "objectClass";
109     char *objectClasses[] = { "top", "sudoRole", NULL };
110     mods[i]->mod_values = objectClasses;
111
112     mods[++i] = xmalloc(sizeof(LDAPMod));
113     mods[i]->mod_op = LDAP_MOD_ADD;
114     mods[i]->mod_type = "cn";
115     char *uids[] = { cn, NULL };
116     mods[i]->mod_values = uids;
117
118     mods[++i] = xmalloc(sizeof(LDAPMod));
119     mods[i]->mod_op = LDAP_MOD_ADD;
120     mods[i]->mod_type = "sudoUser";
121     char *sudouser[] = { cn, NULL };
122     mods[i]->mod_values = sudouser;
123
124     mods[++i] = xmalloc(sizeof(LDAPMod));
125     mods[i]->mod_op = LDAP_MOD_ADD;
126     mods[i]->mod_type = "sudoHost";
127     char *sudohost[] = { "ALL", NULL };
128     mods[i]->mod_values = sudohost;
129
130     mods[++i] = xmalloc(sizeof(LDAPMod));
131     mods[i]->mod_op = LDAP_MOD_ADD;
132     mods[i]->mod_type = "sudoCommand";
133     char *sudocommand[] = { "ALL", NULL };
134     mods[i]->mod_values = sudocommand;
135
136     mods[++i] = xmalloc(sizeof(LDAPMod));
137     mods[i]->mod_op = LDAP_MOD_ADD;
138     mods[i]->mod_type = "sudoOption";
139     char *sudooption[] = { "!authenticate", NULL };
140     mods[i]->mod_values = sudooption;
141
142     mods[++i] = xmalloc(sizeof(LDAPMod));
143     mods[i]->mod_op = LDAP_MOD_ADD;
144     mods[i]->mod_type = "sudoRunAs";
145     char *sudorunas[] = { group, NULL };
146     mods[i]->mod_values = sudorunas;
147
148     char dn[1024];
149     snprintf(dn, sizeof(dn), "cn=%%%s,%s", group, basedn);
150
151     mods[++i] = NULL;
152
153     if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) {
154         ldap_err("addgroup");
155         ret = -1;
156     }
157
158     i = 0;
159     while (mods[i])
160         free(mods[i++]);
161
162     return ret;
163 }
164
165 int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *shell, int no, ...) {
166     va_list args;
167
168     if (!uid || !basedn || !cn || !home || !shell)
169         fatal("adduser: Invalid argument");
170
171     LDAPMod *mods[16];
172     int i = -1;
173     int ret = 0;
174
175     mods[++i] = xmalloc(sizeof(LDAPMod));
176     mods[i]->mod_op = LDAP_MOD_ADD;
177     mods[i]->mod_type = "objectClass";
178     char *objectClasses[] = { "top", "account", "posixAccount", "shadowAccount", NULL, NULL };
179     if (objclass != NULL)
180         objectClasses[4] = objclass;
181     mods[i]->mod_values = objectClasses;
182
183     mods[++i] = xmalloc(sizeof(LDAPMod));
184     mods[i]->mod_op = LDAP_MOD_ADD;
185     mods[i]->mod_type = "uid";
186     char *uids[] = { uid, NULL };
187     mods[i]->mod_values = uids;
188
189     mods[++i] = xmalloc(sizeof(LDAPMod));
190     mods[i]->mod_op = LDAP_MOD_ADD;
191     mods[i]->mod_type = "cn";
192     char *cns[] = { cn, NULL };
193     mods[i]->mod_values = cns;
194
195     mods[++i] = xmalloc(sizeof(LDAPMod));
196     mods[i]->mod_op = LDAP_MOD_ADD;
197     mods[i]->mod_type = "loginShell";
198     char *shells[] = { shell, NULL };
199     mods[i]->mod_values = shells;
200
201     mods[++i] = xmalloc(sizeof(LDAPMod));
202     mods[i]->mod_op = LDAP_MOD_ADD;
203     mods[i]->mod_type = "uidNumber";
204     char idno[16];
205     snprintf(idno, sizeof(idno), "%d", no);
206     char *uidNumbers[] = { idno, NULL };
207     mods[i]->mod_values = uidNumbers;
208
209     mods[++i] = xmalloc(sizeof(LDAPMod));
210     mods[i]->mod_op = LDAP_MOD_ADD;
211     mods[i]->mod_type = "gidNumber";
212     mods[i]->mod_values = uidNumbers;
213
214     mods[++i] = xmalloc(sizeof(LDAPMod));
215     mods[i]->mod_op = LDAP_MOD_ADD;
216     mods[i]->mod_type = "homeDirectory";
217     char *homeDirectory[] = { home, NULL };
218     mods[i]->mod_values = homeDirectory;
219
220     va_start(args, no);
221     char *attr;
222     while ((attr = va_arg(args, char *))) {
223         char *val = va_arg(args, char *);
224
225         if (!val || !*val)
226             continue;
227
228         if (i == sizeof(mods) / sizeof(*mods) - 2) {
229             error("too many attributes");
230             return -1;
231         }
232
233         mods[++i] = xmalloc(sizeof(LDAPMod));
234         mods[i]->mod_op = LDAP_MOD_ADD;
235         mods[i]->mod_type = attr;
236         char *vals[] = { val, NULL };
237         mods[i]->mod_values = vals;
238     }
239
240     mods[++i] = NULL;
241
242     char dn[1024];
243     snprintf(dn, sizeof(dn), "uid=%s,%s", uid, basedn);
244
245     if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) {
246         ldap_err("adduser");
247         ret = -1;
248     }
249
250     i = 0;
251     while (mods[i])
252         free(mods[i++]);
253
254     return ret;
255 }
256
257 int ceo_new_uid(int min, int max) {
258     char filter[64];
259     char *attrs[] = { LDAP_NO_ATTRS, NULL };
260     LDAPMessage *res;
261     int i;
262
263     for (i = min; i <= max; i++) {
264         // id taken due to passwd
265         if (getpwuid(i) != NULL)
266             continue;
267
268         // id taken due to group
269         if (getgrgid(i) != NULL)
270             continue;
271
272         snprintf(filter, sizeof(filter), "(|(uidNumber=%d)(gidNumber=%d))", i, i);
273         if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) {
274             ldap_err("firstuid");
275             return -1;
276         }
277
278         int count = ldap_count_entries(ld, res);
279         ldap_msgfree(res);
280
281         // id taken due to LDAP
282         if (count)
283             continue;
284
285         return i;
286     }
287
288     return -1;
289 }
290
291 int ceo_user_exists(char *uid) {
292     char *attrs[] = { LDAP_NO_ATTRS, NULL };
293     LDAPMessage *msg = NULL;
294     char filter[128];
295     int count;
296
297     if (!uid)
298         fatal("null uid");
299
300     snprintf(filter, sizeof(filter), "uid=%s", uid);
301
302     if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) {
303         ldap_err("user_exists");
304         return -1;
305     }
306
307     count = ldap_count_entries(ld, msg);
308     ldap_msgfree(msg);
309
310     return count > 0;
311 }
312
313 int ceo_group_exists(char *cn) {
314     char *attrs[] = { LDAP_NO_ATTRS, NULL };
315     LDAPMessage *msg = NULL;
316     char filter[128];
317     int count;
318
319     if (!cn)
320         fatal("null cd");
321
322     snprintf(filter, sizeof(filter), "cn=%s", cn);
323
324     if (ldap_search_s(ld, groups_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) {
325         ldap_err("group_exists");
326         return -1;
327     }
328
329     count = ldap_count_entries(ld, msg);
330     ldap_msgfree(msg);
331
332     return count > 0;
333 }
334
335 static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in) {
336     sasl_interact_t *interact = in;
337
338     while (interact->id != SASL_CB_LIST_END) {
339         switch (interact->id) {
340
341             // GSSAPI doesn't require any callbacks
342
343             default:
344                 interact->result = "";
345                 interact->len = 0;
346         }
347
348         interact++;
349     }
350
351     return LDAP_SUCCESS;
352 }
353
354 void ceo_ldap_init() {
355     int proto = LDAP_DEFAULT_PROTOCOL;
356
357     if (ldap_initialize(&ld, server_url) != LDAP_SUCCESS)
358         ldap_fatal("ldap_initialize");
359
360     if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) != LDAP_OPT_SUCCESS)
361         ldap_fatal("ldap_set_option");
362
363     ceo_krb5_auth(admin_bind_userid, admin_bind_keytab);
364
365     if (ldap_sasl_interactive_bind_s(ld, NULL, sasl_mech, NULL, NULL,
366                 LDAP_SASL_QUIET, &ldap_sasl_interact, NULL) != LDAP_SUCCESS)
367         ldap_fatal("Bind failed");
368
369     ceo_krb5_deauth();
370 }
371
372 void ceo_ldap_cleanup() {
373     ldap_unbind(ld);
374 }