]> jfr.im git - irc/evilnet/x3.git/blame - src/x3ldap.c
cleanup
[irc/evilnet/x3.git] / src / x3ldap.c
CommitLineData
50895889 1/* x3ldap.c - LDAP functionality for x3, by Rubin
2 * Copyright 2002-2007 x3 Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
e166c31b 19 *
e166c31b 20 *
21 * TODO:
22 * * get queries working in static existance, so i understand how it works
23 * * get ldap enabled in ./configure
24 * * x3.conf settings to enable/configure its use
25 * * generic functions to enable ldap
26 * * nickserv.c work to use said functions.
27 */
28
39edf54a 29#include "config.h"
db9a9a8a 30#ifdef WITH_LDAP
31
e166c31b 32#include <stdio.h>
33#include <stdlib.h>
34#include <ldap.h>
73d4cc91 35
e166c31b 36#include "conf.h"
e166c31b 37#include "global.h"
50895889 38#include "log.h"
e166c31b 39#include "x3ldap.h"
40
e166c31b 41extern struct nickserv_config nickserv_conf;
42
43
e166c31b 44LDAP *ld = NULL;
45
46int ldap_do_init()
47{
39edf54a 48 if(!nickserv_conf.ldap_enable)
49 return false;
e166c31b 50 /* TODO: check here for all required config options and exit() out if not present */
51 ld = ldap_init(nickserv_conf.ldap_host, nickserv_conf.ldap_port);
52 if(ld == NULL) {
50895889 53 log_module(MAIN_LOG, LOG_ERROR, "LDAP initilization failed!\n");
e166c31b 54 exit(1);
55 }
56 ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &nickserv_conf.ldap_version);
50895889 57 log_module(MAIN_LOG, LOG_INFO, "Success! ldap_init() was successfull in connecting to %s port %d\n", nickserv_conf.ldap_host, nickserv_conf.ldap_port );
e166c31b 58 return true;
59}
60
ea02341b 61
e166c31b 62/* Try to auth someone. If theres problems, try reconnecting
63 * once every 10 seconds for 1 minute.
64 * TODO: move this stuff to config file
65 */
ea02341b 66unsigned int ldap_do_bind( const char *dn, const char *pass)
e166c31b 67{
e166c31b 68 int q;
69
e166c31b 70 int n = 0;
71 while(1) {
ea02341b 72 q = ldap_simple_bind_s(ld, dn, pass);
e166c31b 73 if(q == LDAP_SUCCESS) {
4b8ccfeb 74 log_module(MAIN_LOG, LOG_DEBUG, "bind() successfull! You are bound as %s", dn);
ea02341b 75 /* unbind now */
a5a8a781 76 return q;
e166c31b 77 }
78 else if(q == LDAP_INVALID_CREDENTIALS) {
a5a8a781 79 return q;
e166c31b 80 }
81 else {
4b8ccfeb 82 log_module(MAIN_LOG, LOG_ERROR, "Bind failed: %s/****** (%d)", dn, q);
e166c31b 83 ldap_perror(ld, "ldap");
ea02341b 84 ldap_do_init();
e166c31b 85 }
39edf54a 86 if(n++ > 1) {
87 /* TODO: return to the user that this is a connection error and not a problem
88 * with their password
89 */
90 log_module(MAIN_LOG, LOG_ERROR, "Failing to reconnect to ldap server. Auth failing.");
a5a8a781 91 return q;
e166c31b 92 }
93 }
ea02341b 94 log_module(MAIN_LOG, LOG_ERROR, "ldap_do_bind falling off the end. this shouldnt happen");
a5a8a781 95 return q;
ea02341b 96}
97
98unsigned int ldap_check_auth( char *account, char *pass)
99{
100 char buff[MAXLEN];
101
102 if(!nickserv_conf.ldap_enable)
a5a8a781 103 return LDAP_OTHER;
ea02341b 104
105 memset(buff, 0, MAXLEN);
106 snprintf(buff, sizeof(buff)-1, nickserv_conf.ldap_dn_fmt /*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account);
107 return ldap_do_bind(buff, pass);
e166c31b 108
109}
110
111#ifdef notdef /* not used yet - will be used to pull email etc out of ldap */
112LDAPMessage ldap_search_user(char uid)
113{
114
115 char filter[] = "cn=admin";
116
117 struct timeval timeout;
118 /*
119 Now we do a search;
120 */
121 timeout.tv_usec = 0;
122 timeout.tv_sec = 5;
123 if( ldap_search_st(ld, base, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &timeout, &res) != LDAP_SUCCESS) {
50895889 124 log_module(MAIN_LOG, LOG_ERROR, "search failed: %s %s\n", base, filter);
e166c31b 125 exit(1);
126 }
50895889 127 log_module(MAIN_LOG, LOG_DEBUG, "Search successfull! %s %s\n", base, filter);
128 log_module(MAIN_LOG, LOG_DEBUG, "Got %d entries\n", ldap_count_entries(ld, res));
e166c31b 129 {
130 LDAPMessage *entry;
131 char **value;
132 entry = ldap_first_entry(ld, res);
133 value = ldap_get_values(ld, entry, "cn");
50895889 134 log_module(MAIN_LOG, LOG_DEBUG, "cn: %s\n", value[0]);
e166c31b 135 value = ldap_get_values(ld, entry, "description");
50895889 136 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
e166c31b 137 value = ldap_get_values(ld, entry, "userPassword");
50895889 138 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
e166c31b 139 }
140 /*
141 ldap_result();
142 ldap_first_entry();
143 ldap_first_attribute();
144 for(;;) {
145 ldap_get_values();
146 ldap_next_attribute();
147 }
148
149 ldap_parse_result();
150
151 ldap_unbind_ext();
152
153 */
154 /* get errors with ldap_err2string(); */
155}
156
157#endif
158
4b8ccfeb 159/********* base64 stuff ***********/
160
161unsigned char *pack(char *str, unsigned int *len)
162{
163 int nibbleshift = 4;
164 int first = 1;
165 char *v;
166 static unsigned char buf[MAXLEN+1];
167 int outputpos = -1;
168
169 memset(buf, 0, MAXLEN+1);
170 v = str;
171 while(*v) {
172 char n = *(v++);
173
174 if((n >= '0') && (n <= '9')) {
175 n -= '0';
176 } else if ((n >= 'A') && (n <= 'F')) {
177 n -= ('A' - 10);
178 } else if ((n >= 'a') && (n <= 'f')) {
179 n -= ('a' - 10);
180 } else {
181 printf("pack type H: illegal hex digit %c", n);
182 n = 0;
183 }
184
185 if (first--) {
186 buf[++outputpos] = 0;
187 } else {
188 first = 1;
189 }
190
191 buf[outputpos] |= (n << nibbleshift);
192 nibbleshift = (nibbleshift + 4) & 7;
193 }
194 *len = outputpos+1;
195 return(buf);
196}
197
198
199/* from php5 sources */
200static char base64_table[] =
201 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
202 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
203 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
204 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
205 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
206 };
207static char base64_pad = '=';
208
209char *base64_encode(const unsigned char *str, int length, int *ret_length)
210{
211 const unsigned char *current = str;
212 char *p;
213 char *result;
214
215 if ((length + 2) < 0 || ((length + 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
216 if (ret_length != NULL) {
217 *ret_length = 0;
218 }
219 return NULL;
220 }
221
222 result = (char *)calloc(((length + 2) / 3) * 4, sizeof(char));
223 p = result;
224
225 while (length > 2) { /* keep going until we have less than 24 bits */
226 *p++ = base64_table[current[0] >> 2];
227 *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
228 *p++ = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
229 *p++ = base64_table[current[2] & 0x3f];
230
231 current += 3;
232 length -= 3; /* we just handle 3 octets of data */
233 }
234
235 /* now deal with the tail end of things */
236 if (length != 0) {
237 *p++ = base64_table[current[0] >> 2];
238 if (length > 1) {
239 *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
240 *p++ = base64_table[(current[1] & 0x0f) << 2];
241 *p++ = base64_pad;
242 } else {
243 *p++ = base64_table[(current[0] & 0x03) << 4];
244 *p++ = base64_pad;
245 *p++ = base64_pad;
246 }
247 }
248 if (ret_length != NULL) {
249 *ret_length = (int)(p - result);
250 }
251 *p = '\0';
252 return result;
253}
254
255
73d4cc91 256char **make_object_vals()
257{
258 unsigned int y;
259 static char **object_vals = NULL;
260
261 if(object_vals)
262 free(object_vals);
263
264 object_vals = malloc(sizeof( *object_vals ) * nickserv_conf.ldap_object_classes->used);
265
266 for(y = 0; y < nickserv_conf.ldap_object_classes->used; y++) {
267 object_vals[y] = nickserv_conf.ldap_object_classes->list[y];
268 }
269 object_vals[y] = NULL;
270 return object_vals;
271}
272
4b8ccfeb 273char *make_password(const char *password)
274{
275 char *base64pass;
276 char crypted[MD5_CRYPT_LENGTH+1];
277 unsigned char *packed;
278 unsigned int len;
279 char *passbuf;
280 cryptpass(password, crypted);
281
282 packed = pack(crypted, &len);
283 base64pass = base64_encode(packed, len, NULL);
284 passbuf = malloc(strlen(base64pass) + 1 + 5);
285 strcpy(passbuf, "{MD5}");
286 strcat(passbuf, base64pass);
287 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
288 free(base64pass);
289 return passbuf;
290
291}
292
73d4cc91 293LDAPMod **make_mods_add(const char *account, const char *password, const char *email, int *num_mods_ret)
ea02341b 294{
295 static char *account_vals[] = { NULL, NULL };
296 static char *password_vals[] = { NULL, NULL };
297 static char *email_vals[] = { NULL, NULL };
ea02341b 298 int num_mods = 3;
299 int i;
300 /* TODO: take this from nickserv_conf.ldap_add_objects */
ea02341b 301 LDAPMod **mods;
73d4cc91 302 static char **object_vals;
303 object_vals = make_object_vals();
ea02341b 304
305 account_vals[0] = (char *) account;
306 password_vals[0] = (char *) password;
307 email_vals[0] = (char *) email;
308
309 if(!(nickserv_conf.ldap_field_account && *nickserv_conf.ldap_field_account))
310 return 0; /* account required */
311 if(!(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password))
312 return 0; /* password required */
313 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
314 num_mods++;
315
316 mods = ( LDAPMod ** ) malloc(( num_mods + 1 ) * sizeof( LDAPMod * ));
317 for( i = 0; i < num_mods; i++) {
318 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
319 memset(mods[i], 0, sizeof(LDAPMod));
320 }
321
ea02341b 322 mods[0]->mod_op = LDAP_MOD_ADD;
323 mods[0]->mod_type = strdup("objectclass");
324 mods[0]->mod_values = object_vals;
325
326 mods[1]->mod_op = LDAP_MOD_ADD;
327 mods[1]->mod_type = strdup(nickserv_conf.ldap_field_account);
328 mods[1]->mod_values = account_vals;
329
330 mods[2]->mod_op = LDAP_MOD_ADD;
331 mods[2]->mod_type = strdup(nickserv_conf.ldap_field_password);
332 mods[2]->mod_values = password_vals;
333
334 if(nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email && email && *email) {
335 mods[3]->mod_op = LDAP_MOD_ADD;
336 mods[3]->mod_type = strdup(nickserv_conf.ldap_field_email);
337 mods[3]->mod_values = email_vals;
338 mods[4] = NULL;
339 }
340 else
341 mods[3] = NULL;
342 *num_mods_ret = num_mods;
343 return mods;
344}
345
346int ldap_do_admin_bind()
347{
348 if(!(nickserv_conf.ldap_admin_dn && *nickserv_conf.ldap_admin_dn &&
349 nickserv_conf.ldap_admin_pass && *nickserv_conf.ldap_admin_pass)) {
350 log_module(MAIN_LOG, LOG_ERROR, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
a5a8a781 351 return LDAP_OTHER; /* not configured to do this */
ea02341b 352 }
353 return(ldap_do_bind(nickserv_conf.ldap_admin_dn, nickserv_conf.ldap_admin_pass));
354}
355
356int ldap_do_add(const char *account, const char *password, const char *email)
357{
358 char newdn[MAXLEN];
359 LDAPMod **mods;
360 int rc, i;
361 int num_mods;
4b8ccfeb 362 char *passbuf;
ea02341b 363
a5a8a781 364 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
ea02341b 365 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
a5a8a781 366 return rc;
ea02341b 367 }
368
4b8ccfeb 369 passbuf = make_password(password);
ea02341b 370 snprintf(newdn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
73d4cc91 371 mods = make_mods_add(account, password, email, &num_mods);
ea02341b 372 if(!mods) {
373 log_module(MAIN_LOG, LOG_ERROR, "Error building mods for ldap_add");
a5a8a781 374 return LDAP_OTHER;
ea02341b 375 }
376 rc = ldap_add_ext_s(ld, newdn, mods, NULL, NULL);
377 if(rc != LDAP_SUCCESS) {
378 log_module(MAIN_LOG, LOG_ERROR, "Error adding ldap account: %s -- %s", account, ldap_err2string(rc));
a5a8a781 379 return rc;
ea02341b 380 }
381 //ldap_unbind_s(ld);
382 for(i = 0; i < num_mods; i++) {
383 free(mods[i]->mod_type);
384 free(mods[i]);
385 }
386 free(mods);
4b8ccfeb 387 free(passbuf);
a5a8a781 388 return rc;
ea02341b 389}
390
73d4cc91 391int ldap_delete_account(char *account)
392{
393 char dn[MAXLEN];
394 memset(dn, 0, MAXLEN);
395 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
396 return(ldap_delete_s(ld, dn));
397}
398
399int ldap_rename_account(char *oldaccount, char *newaccount)
400{
401 char dn[MAXLEN], newdn[MAXLEN];
402 int rc;
403
404 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
405 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
406 return rc;
407 }
408
409 memset(dn, 0, MAXLEN);
410 memset(newdn, 0, MAXLEN);
411 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, oldaccount);
412 strcat(newdn, nickserv_conf.ldap_field_account);
413 strcat(newdn, "=");
414 strcat(newdn, newaccount);
415 rc = ldap_modrdn2_s(ld, dn, newdn, true);
416 if(rc != LDAP_SUCCESS) {
417 log_module(MAIN_LOG, LOG_ERROR, "Error modifying ldap account: %s -- %s", oldaccount, ldap_err2string(rc));
418 return rc;
419 }
420 return rc;
421
422}
423
424LDAPMod **make_mods_modify(const char *password, const char *email, int *num_mods_ret)
425{
426 static char *password_vals[] = { NULL, NULL };
427 static char *email_vals[] = { NULL, NULL };
428 int num_mods = 1;
429 int i;
430 /* TODO: take this from nickserv_conf.ldap_add_objects */
431 LDAPMod **mods;
432
433 password_vals[0] = (char *) password;
434 email_vals[0] = (char *) email;
435
436 if(!(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password))
437 return 0; /* password required */
438 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
439 num_mods++;
440
441 mods = ( LDAPMod ** ) malloc(( num_mods + 1 ) * sizeof( LDAPMod * ));
442 for( i = 0; i < num_mods; i++) {
443 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
444 memset(mods[i], 0, sizeof(LDAPMod));
445 }
446
447 i = 0;
448 if(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password &&
449 password) {
450 mods[i]->mod_op = LDAP_MOD_REPLACE;
451 mods[i]->mod_type = strdup(nickserv_conf.ldap_field_password);
452 mods[i]->mod_values = password_vals;
453 i++;
454 }
455
456 if(nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email && email) {
457 mods[i]->mod_op = LDAP_MOD_REPLACE;
458 mods[i]->mod_type = strdup(nickserv_conf.ldap_field_email);
459 mods[i]->mod_values = email_vals;
460 i++;
461 }
462 mods[i] = NULL;
463 *num_mods_ret = num_mods;
464 return mods;
465}
466
73d4cc91 467
468/* Save email or password to server
469 *
470 * password - UNENCRYPTED password. This function encrypts if libs are available
471 * email - email address
472 *
473 * NULL to make no change
474 */
475int ldap_do_modify(const char *account, const char *password, const char *email)
476{
477 char dn[MAXLEN];
478 LDAPMod **mods;
479 int rc, i;
480 int num_mods;
481 char *passbuf = NULL;
482
483 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
484 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
485 return rc;
486 }
487
488 if(password) {
4b8ccfeb 489 passbuf = make_password(password);
73d4cc91 490 }
491
492 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
493 mods = make_mods_modify(passbuf, email, &num_mods);
494 if(!mods) {
495 log_module(MAIN_LOG, LOG_ERROR, "Error building mods for ldap_add");
496 return LDAP_OTHER;
497 }
498 rc = ldap_modify_s(ld, dn, mods);
499 if(rc != LDAP_SUCCESS) {
500 log_module(MAIN_LOG, LOG_ERROR, "Error adding ldap account: %s -- %s", account, ldap_err2string(rc));
501 return rc;
502 }
503 for(i = 0; i < num_mods; i++) {
504 free(mods[i]->mod_type);
505 free(mods[i]);
506 }
507 free(mods);
508 if(passbuf)
509 free(passbuf);
510 return rc;
511}
512
e166c31b 513void ldap_close()
514{
515 ldap_unbind(ld);
516}
517
518#endif