Invert the sense of the return from addmember() or addclub()
[mspang/pyceo.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 <ldap.h>
7 #include <krb5.h>
8
9 #include "ldap.h"
10 #include "krb5.h"
11 #include "config.h"
12 #include "util.h"
13
14 extern char *prog;
15
16 LDAP *ld;
17
18 static void ldap_fatal(char *msg) {
19     int errnum;
20     char *errstr, *detail;
21
22     ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum);
23     ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail);
24
25     errstr = ldap_err2string(errnum);
26
27     if (detail && *detail)
28         fatal("%s: %s (%d): %s", msg, errstr, errnum, detail);
29     else
30         fatal("%s: %s (%d)", msg, errstr, errnum);
31 }
32
33 static void ldap_err(char *msg) {
34     int errnum;
35     char *errstr, *detail;
36
37     ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &errnum);
38     ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &detail);
39
40     errstr = ldap_err2string(errnum);
41
42     if (detail && *detail)
43         error("%s: %s (%d): %s", msg, errstr, errnum, detail);
44     else
45         error("%s: %s (%d)", msg, errstr, errnum);
46 }
47
48 int ceo_add_group(char *cn, char *basedn, int no) {
49     if (!cn || !basedn)
50         fatal("addgroup: Invalid argument");
51
52     LDAPMod *mods[8];
53     int i = -1;
54     int ret = 0;
55
56     mods[++i] = xmalloc(sizeof(LDAPMod));
57     mods[i]->mod_op = LDAP_MOD_ADD;
58     mods[i]->mod_type = "objectClass";
59     char *objectClasses[] = { "top", "group", "posixGroup", NULL };
60     mods[i]->mod_values = objectClasses;
61
62     mods[++i] = xmalloc(sizeof(LDAPMod));
63     mods[i]->mod_op = LDAP_MOD_ADD;
64     mods[i]->mod_type = "cn";
65     char *uids[] = { cn, NULL };
66     mods[i]->mod_values = uids;
67
68     mods[++i] = xmalloc(sizeof(LDAPMod));
69     mods[i]->mod_op = LDAP_MOD_ADD;
70     mods[i]->mod_type = "gidNumber";
71     char idno[16];
72     snprintf(idno, sizeof(idno), "%d", no);
73     char *gidNumbers[] = { idno, NULL };
74     mods[i]->mod_values = gidNumbers;
75
76     mods[++i] = NULL;
77
78     char dn[1024];
79     snprintf(dn, sizeof(dn), "cn=%s,%s", cn, basedn);
80
81     if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) {
82         ldap_err("addgroup");
83         ret = -1;
84     }
85
86     i = 0;
87     while (mods[i])
88         free(mods[i++]);
89
90     return ret;
91 }
92
93 int ceo_add_user(char *uid, char *basedn, char *objclass, char *cn, char *home, char *shell, int no, ...) {
94     va_list args;
95
96     if (!uid || !basedn || !cn || !home || !shell)
97         fatal("adduser: Invalid argument");
98
99     LDAPMod *mods[16];
100     int i = -1;
101     int ret = 0;
102
103     mods[++i] = xmalloc(sizeof(LDAPMod));
104     mods[i]->mod_op = LDAP_MOD_ADD;
105     mods[i]->mod_type = "objectClass";
106     char *objectClasses[] = { "top", "account", "posixAccount", "shadowAccount", NULL, NULL };
107     if (objclass != NULL)
108         objectClasses[4] = objclass;
109     mods[i]->mod_values = objectClasses;
110
111     mods[++i] = xmalloc(sizeof(LDAPMod));
112     mods[i]->mod_op = LDAP_MOD_ADD;
113     mods[i]->mod_type = "uid";
114     char *uids[] = { uid, NULL };
115     mods[i]->mod_values = uids;
116
117     mods[++i] = xmalloc(sizeof(LDAPMod));
118     mods[i]->mod_op = LDAP_MOD_ADD;
119     mods[i]->mod_type = "cn";
120     char *cns[] = { cn, NULL };
121     mods[i]->mod_values = cns;
122
123     mods[++i] = xmalloc(sizeof(LDAPMod));
124     mods[i]->mod_op = LDAP_MOD_ADD;
125     mods[i]->mod_type = "loginShell";
126     char *shells[] = { shell, NULL };
127     mods[i]->mod_values = shells;
128
129     mods[++i] = xmalloc(sizeof(LDAPMod));
130     mods[i]->mod_op = LDAP_MOD_ADD;
131     mods[i]->mod_type = "uidNumber";
132     char idno[16];
133     snprintf(idno, sizeof(idno), "%d", no);
134     char *uidNumbers[] = { idno, NULL };
135     mods[i]->mod_values = uidNumbers;
136
137     mods[++i] = xmalloc(sizeof(LDAPMod));
138     mods[i]->mod_op = LDAP_MOD_ADD;
139     mods[i]->mod_type = "gidNumber";
140     mods[i]->mod_values = uidNumbers;
141
142     mods[++i] = xmalloc(sizeof(LDAPMod));
143     mods[i]->mod_op = LDAP_MOD_ADD;
144     mods[i]->mod_type = "homeDirectory";
145     char *homeDirectory[] = { home, NULL };
146     mods[i]->mod_values = homeDirectory;
147
148     va_start(args, no);
149     char *attr;
150     while ((attr = va_arg(args, char *))) {
151         char *val = va_arg(args, char *);
152
153         if (!val || !*val)
154             continue;
155
156         if (i == sizeof(mods) / sizeof(*mods) - 2) {
157             error("too many attributes");
158             return -1;
159         }
160
161         mods[++i] = xmalloc(sizeof(LDAPMod));
162         mods[i]->mod_op = LDAP_MOD_ADD;
163         mods[i]->mod_type = attr;
164         char *vals[] = { val, NULL };
165         mods[i]->mod_values = vals;
166     }
167
168     mods[++i] = NULL;
169
170     char dn[1024];
171     snprintf(dn, sizeof(dn), "uid=%s,%s", uid, basedn);
172
173     if (ldap_add_s(ld, dn, mods) != LDAP_SUCCESS) {
174         ldap_err("adduser");
175         ret = -1;
176     }
177
178     i = 0;
179     while (mods[i])
180         free(mods[i++]);
181
182     return ret;
183 }
184
185 int ceo_new_uid(int min, int max) {
186     char filter[64];
187     char *attrs[] = { LDAP_NO_ATTRS, NULL };
188     LDAPMessage *res;
189     int i;
190
191     for (i = min; i <= max; i++) {
192         // id taken due to passwd
193         if (getpwuid(i) != NULL)
194             continue;
195
196         // id taken due to group
197         if (getgrgid(i) != NULL)
198             continue;
199
200         snprintf(filter, sizeof(filter), "(|(uidNumber=%d)(gidNumber=%d))", i, i);
201         if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 1, &res) != LDAP_SUCCESS) {
202             ldap_err("firstuid");
203             return -1;
204         }
205
206         int count = ldap_count_entries(ld, res);
207         ldap_msgfree(res);
208
209         // id taken due to LDAP
210         if (count)
211             continue;
212
213         return i;
214     }
215
216     return -1;
217 }
218
219 int ceo_add_club(char *uid, char *cn) {
220     int id = ceo_new_uid(club_min_id, club_max_id);
221
222     if (ceo_add_user(uid, users_base, "club", cn, club_home, club_shell, id, NULL))
223         return -1;
224
225     if (ceo_add_group(uid, groups_base, id))
226         return -1;
227
228     return 0;
229 }
230
231 int ceo_user_exists(char *uid) {
232     char *attrs[] = { LDAP_NO_ATTRS, NULL };
233     LDAPMessage *msg = NULL;
234     char filter[128];
235     int count;
236
237     if (!uid)
238         fatal("null uid");
239
240     snprintf(filter, sizeof(filter), "uid=%s", uid);
241
242     if (ldap_search_s(ld, users_base, LDAP_SCOPE_SUBTREE, filter, attrs, 0, &msg) != LDAP_SUCCESS) {
243         ldap_err("user_exists");
244         return -1;
245     }
246
247     count = ldap_count_entries(ld, msg);
248     ldap_msgfree(msg);
249
250     return count > 0;
251 }
252
253 static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in) {
254     sasl_interact_t *interact = in;
255
256     while (interact->id != SASL_CB_LIST_END) {
257         switch (interact->id) {
258
259             // GSSAPI doesn't require any callbacks
260
261             default:
262                 interact->result = "";
263                 interact->len = 0;
264         }
265
266         interact++;
267     }
268
269     return LDAP_SUCCESS;
270 }
271
272 void ceo_ldap_init() {
273     int proto = LDAP_DEFAULT_PROTOCOL;
274
275     if (ldap_initialize(&ld, server_url) != LDAP_SUCCESS)
276         ldap_fatal("ldap_initialize");
277
278     if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) != LDAP_OPT_SUCCESS)
279         ldap_fatal("ldap_set_option");
280
281     ceo_krb5_auth(admin_bind_userid, admin_bind_keytab);
282
283     if (ldap_sasl_interactive_bind_s(ld, NULL, sasl_mech, NULL, NULL,
284                 LDAP_SASL_QUIET, &ldap_sasl_interact, NULL) != LDAP_SUCCESS)
285         ldap_fatal("Bind failed");
286
287     ceo_krb5_deauth();
288 }
289
290 void ceo_ldap_cleanup() {
291     ldap_unbind(ld);
292 }