]> jfr.im git - irc/evilnet/x3.git/blame - src/x3ldap.c
fixing small memory leak
[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
AS
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
AS
30#ifdef WITH_LDAP
31
e166c31b
AS
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
AS
39#include "x3ldap.h"
40
e166c31b
AS
41extern struct nickserv_config nickserv_conf;
42
43
e166c31b
AS
44LDAP *ld = NULL;
45
46int ldap_do_init()
47{
39edf54a
AS
48 if(!nickserv_conf.ldap_enable)
49 return false;
e166c31b
AS
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
AS
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
AS
58 return true;
59}
60
ea02341b 61
e166c31b
AS
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
AS
68 int q;
69
e166c31b
AS
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
AS
77 }
78 else if(q == LDAP_INVALID_CREDENTIALS) {
a5a8a781 79 return q;
e166c31b
AS
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
AS
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
AS
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}
24e9e6c3
AS
97int ldap_do_admin_bind()
98{
99 if(!(nickserv_conf.ldap_admin_dn && *nickserv_conf.ldap_admin_dn &&
100 nickserv_conf.ldap_admin_pass && *nickserv_conf.ldap_admin_pass)) {
101 log_module(MAIN_LOG, LOG_ERROR, "Tried to admin bind, but no admin credentials configured in config file. ldap_admin_dn/ldap_admin_pass");
102 return LDAP_OTHER; /* not configured to do this */
103 }
104 return(ldap_do_bind(nickserv_conf.ldap_admin_dn, nickserv_conf.ldap_admin_pass));
105}
106
ea02341b
AS
107
108unsigned int ldap_check_auth( char *account, char *pass)
109{
110 char buff[MAXLEN];
111
112 if(!nickserv_conf.ldap_enable)
a5a8a781 113 return LDAP_OTHER;
ea02341b
AS
114
115 memset(buff, 0, MAXLEN);
116 snprintf(buff, sizeof(buff)-1, nickserv_conf.ldap_dn_fmt /*"uid=%s,ou=Users,dc=afternet,dc=org"*/, account);
117 return ldap_do_bind(buff, pass);
e166c31b
AS
118
119}
120
24e9e6c3 121int ldap_search_user(char *account, LDAPMessage **entry)
e166c31b
AS
122{
123
24e9e6c3
AS
124 char filter[MAXLEN+1];
125 int rc;
126 LDAPMessage *res;
e166c31b
AS
127
128 struct timeval timeout;
24e9e6c3
AS
129
130 memset(filter, 0, MAXLEN+1);
131 snprintf(filter, MAXLEN, "%s=%s", nickserv_conf.ldap_field_account, account);
e166c31b
AS
132 /*
133 Now we do a search;
134 */
135 timeout.tv_usec = 0;
136 timeout.tv_sec = 5;
24e9e6c3
AS
137 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
138 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
139 return rc;
140 }
141 if( (rc = ldap_search_st(ld, nickserv_conf.ldap_base, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &timeout, &res)) != LDAP_SUCCESS) {
142 log_module(MAIN_LOG, LOG_ERROR, "search failed: %s %s: %s", nickserv_conf.ldap_base, filter, ldap_err2string(rc));
143 return(rc);
e166c31b 144 }
24e9e6c3
AS
145 log_module(MAIN_LOG, LOG_DEBUG, "Search successfull! %s %s\n", nickserv_conf.ldap_base, filter);
146 if(ldap_count_entries(ld, res) != 1) {
147 log_module(MAIN_LOG, LOG_ERROR, "LDAP search got %d entries when looking for %s", ldap_count_entries(ld, res), account);
148 return(LDAP_OTHER); /* Search was a success, but user not found.. */
e166c31b 149 }
24e9e6c3
AS
150 log_module(MAIN_LOG, LOG_DEBUG, "LDAP search got %d entries", ldap_count_entries(ld, res));
151 *entry = ldap_first_entry(ld, res);
152 return(rc);
153}
154
155/* queries the ldap server for account..
156 * if a single account match is found,
157 * email is allocated and set to the email address
158 * and returns LDAP_SUCCESS. returns LDAP_OTHER if
159 * 0 or 2+ entries are matched, or the proper ldap error
160 * code for other errors.
161 */
162int ldap_get_user_info(char *account, char **email)
163{
164 int rc;
165 char **value;
166 LDAPMessage *entry, *res;
167 *email = NULL;
168 if( (rc = ldap_search_user(account, &res)) == LDAP_SUCCESS) {
169 entry = ldap_first_entry(ld, res);
170 value = ldap_get_values(ld, entry, nickserv_conf.ldap_field_email);
171 if(!value) {
172 return(LDAP_OTHER);
173 }
174 *email = strdup(value[0]);
175 log_module(MAIN_LOG, LOG_DEBUG, "%s: %s\n", nickserv_conf.ldap_field_email, value[0]);
176 /*
177 value = ldap_get_values(ld, entry, "description");
178 log_module(MAIN_LOG, LOG_DEBUG, "Description: %s\n", value[0]);
179 value = ldap_get_values(ld, entry, "userPassword");
180 log_module(MAIN_LOG, LOG_DEBUG, "pass: %s\n", value ? value[0] : "error");
181 */
182 }
183 return(rc);
184}
185
e166c31b
AS
186 /*
187 ldap_result();
188 ldap_first_entry();
189 ldap_first_attribute();
190 for(;;) {
191 ldap_get_values();
192 ldap_next_attribute();
193 }
194
195 ldap_parse_result();
196
197 ldap_unbind_ext();
e166c31b
AS
198 */
199 /* get errors with ldap_err2string(); */
e166c31b 200
e166c31b 201
4b8ccfeb
AS
202/********* base64 stuff ***********/
203
204unsigned char *pack(char *str, unsigned int *len)
205{
206 int nibbleshift = 4;
207 int first = 1;
208 char *v;
209 static unsigned char buf[MAXLEN+1];
210 int outputpos = -1;
211
212 memset(buf, 0, MAXLEN+1);
213 v = str;
214 while(*v) {
215 char n = *(v++);
216
217 if((n >= '0') && (n <= '9')) {
218 n -= '0';
219 } else if ((n >= 'A') && (n <= 'F')) {
220 n -= ('A' - 10);
221 } else if ((n >= 'a') && (n <= 'f')) {
222 n -= ('a' - 10);
223 } else {
224 printf("pack type H: illegal hex digit %c", n);
225 n = 0;
226 }
227
228 if (first--) {
229 buf[++outputpos] = 0;
230 } else {
231 first = 1;
232 }
233
234 buf[outputpos] |= (n << nibbleshift);
235 nibbleshift = (nibbleshift + 4) & 7;
236 }
237 *len = outputpos+1;
238 return(buf);
239}
240
241
242/* from php5 sources */
243static char base64_table[] =
244 { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
245 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
246 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
247 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
248 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
249 };
250static char base64_pad = '=';
251
252char *base64_encode(const unsigned char *str, int length, int *ret_length)
253{
254 const unsigned char *current = str;
255 char *p;
256 char *result;
257
258 if ((length + 2) < 0 || ((length + 2) / 3) >= (1 << (sizeof(int) * 8 - 2))) {
259 if (ret_length != NULL) {
260 *ret_length = 0;
261 }
262 return NULL;
263 }
264
265 result = (char *)calloc(((length + 2) / 3) * 4, sizeof(char));
266 p = result;
267
268 while (length > 2) { /* keep going until we have less than 24 bits */
269 *p++ = base64_table[current[0] >> 2];
270 *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
271 *p++ = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
272 *p++ = base64_table[current[2] & 0x3f];
273
274 current += 3;
275 length -= 3; /* we just handle 3 octets of data */
276 }
277
278 /* now deal with the tail end of things */
279 if (length != 0) {
280 *p++ = base64_table[current[0] >> 2];
281 if (length > 1) {
282 *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
283 *p++ = base64_table[(current[1] & 0x0f) << 2];
284 *p++ = base64_pad;
285 } else {
286 *p++ = base64_table[(current[0] & 0x03) << 4];
287 *p++ = base64_pad;
288 *p++ = base64_pad;
289 }
290 }
291 if (ret_length != NULL) {
292 *ret_length = (int)(p - result);
293 }
294 *p = '\0';
295 return result;
296}
297
298
73d4cc91
AS
299char **make_object_vals()
300{
301 unsigned int y;
302 static char **object_vals = NULL;
303
304 if(object_vals)
305 free(object_vals);
306
307 object_vals = malloc(sizeof( *object_vals ) * nickserv_conf.ldap_object_classes->used);
308
309 for(y = 0; y < nickserv_conf.ldap_object_classes->used; y++) {
310 object_vals[y] = nickserv_conf.ldap_object_classes->list[y];
311 }
312 object_vals[y] = NULL;
313 return object_vals;
314}
315
4b8ccfeb
AS
316char *make_password(const char *password)
317{
318 char *base64pass;
319 char crypted[MD5_CRYPT_LENGTH+1];
320 unsigned char *packed;
321 unsigned int len;
322 char *passbuf;
323 cryptpass(password, crypted);
324
325 packed = pack(crypted, &len);
326 base64pass = base64_encode(packed, len, NULL);
327 passbuf = malloc(strlen(base64pass) + 1 + 5);
328 strcpy(passbuf, "{MD5}");
329 strcat(passbuf, base64pass);
330 //log_module(MAIN_LOG, LOG_DEBUG, "Encoded password is: '%s'", passbuf);
331 free(base64pass);
332 return passbuf;
333
334}
335
73d4cc91 336LDAPMod **make_mods_add(const char *account, const char *password, const char *email, int *num_mods_ret)
ea02341b
AS
337{
338 static char *account_vals[] = { NULL, NULL };
339 static char *password_vals[] = { NULL, NULL };
340 static char *email_vals[] = { NULL, NULL };
ea02341b
AS
341 int num_mods = 3;
342 int i;
343 /* TODO: take this from nickserv_conf.ldap_add_objects */
ea02341b 344 LDAPMod **mods;
73d4cc91
AS
345 static char **object_vals;
346 object_vals = make_object_vals();
ea02341b
AS
347
348 account_vals[0] = (char *) account;
349 password_vals[0] = (char *) password;
350 email_vals[0] = (char *) email;
351
352 if(!(nickserv_conf.ldap_field_account && *nickserv_conf.ldap_field_account))
353 return 0; /* account required */
354 if(!(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password))
355 return 0; /* password required */
356 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
357 num_mods++;
358
359 mods = ( LDAPMod ** ) malloc(( num_mods + 1 ) * sizeof( LDAPMod * ));
360 for( i = 0; i < num_mods; i++) {
361 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
362 memset(mods[i], 0, sizeof(LDAPMod));
363 }
364
ea02341b
AS
365 mods[0]->mod_op = LDAP_MOD_ADD;
366 mods[0]->mod_type = strdup("objectclass");
367 mods[0]->mod_values = object_vals;
368
369 mods[1]->mod_op = LDAP_MOD_ADD;
370 mods[1]->mod_type = strdup(nickserv_conf.ldap_field_account);
371 mods[1]->mod_values = account_vals;
372
373 mods[2]->mod_op = LDAP_MOD_ADD;
374 mods[2]->mod_type = strdup(nickserv_conf.ldap_field_password);
375 mods[2]->mod_values = password_vals;
376
377 if(nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email && email && *email) {
378 mods[3]->mod_op = LDAP_MOD_ADD;
379 mods[3]->mod_type = strdup(nickserv_conf.ldap_field_email);
380 mods[3]->mod_values = email_vals;
381 mods[4] = NULL;
382 }
383 else
384 mods[3] = NULL;
385 *num_mods_ret = num_mods;
386 return mods;
387}
388
ea02341b
AS
389int ldap_do_add(const char *account, const char *password, const char *email)
390{
391 char newdn[MAXLEN];
392 LDAPMod **mods;
393 int rc, i;
394 int num_mods;
4b8ccfeb 395 char *passbuf;
ea02341b 396
a5a8a781 397 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
ea02341b 398 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
a5a8a781 399 return rc;
ea02341b
AS
400 }
401
4b8ccfeb 402 passbuf = make_password(password);
ea02341b 403 snprintf(newdn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
73d4cc91 404 mods = make_mods_add(account, password, email, &num_mods);
ea02341b
AS
405 if(!mods) {
406 log_module(MAIN_LOG, LOG_ERROR, "Error building mods for ldap_add");
a5a8a781 407 return LDAP_OTHER;
ea02341b
AS
408 }
409 rc = ldap_add_ext_s(ld, newdn, mods, NULL, NULL);
410 if(rc != LDAP_SUCCESS) {
411 log_module(MAIN_LOG, LOG_ERROR, "Error adding ldap account: %s -- %s", account, ldap_err2string(rc));
a5a8a781 412 return rc;
ea02341b
AS
413 }
414 //ldap_unbind_s(ld);
415 for(i = 0; i < num_mods; i++) {
416 free(mods[i]->mod_type);
417 free(mods[i]);
418 }
419 free(mods);
4b8ccfeb 420 free(passbuf);
a5a8a781 421 return rc;
ea02341b
AS
422}
423
73d4cc91
AS
424int ldap_delete_account(char *account)
425{
426 char dn[MAXLEN];
427 memset(dn, 0, MAXLEN);
428 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
429 return(ldap_delete_s(ld, dn));
430}
431
432int ldap_rename_account(char *oldaccount, char *newaccount)
433{
434 char dn[MAXLEN], newdn[MAXLEN];
435 int rc;
436
437 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
438 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
439 return rc;
440 }
441
442 memset(dn, 0, MAXLEN);
443 memset(newdn, 0, MAXLEN);
444 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, oldaccount);
445 strcat(newdn, nickserv_conf.ldap_field_account);
446 strcat(newdn, "=");
447 strcat(newdn, newaccount);
448 rc = ldap_modrdn2_s(ld, dn, newdn, true);
449 if(rc != LDAP_SUCCESS) {
450 log_module(MAIN_LOG, LOG_ERROR, "Error modifying ldap account: %s -- %s", oldaccount, ldap_err2string(rc));
451 return rc;
452 }
453 return rc;
454
455}
456
457LDAPMod **make_mods_modify(const char *password, const char *email, int *num_mods_ret)
458{
459 static char *password_vals[] = { NULL, NULL };
460 static char *email_vals[] = { NULL, NULL };
461 int num_mods = 1;
462 int i;
463 /* TODO: take this from nickserv_conf.ldap_add_objects */
464 LDAPMod **mods;
465
466 password_vals[0] = (char *) password;
467 email_vals[0] = (char *) email;
468
469 if(!(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password))
470 return 0; /* password required */
471 if(email && *email && nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email)
472 num_mods++;
473
474 mods = ( LDAPMod ** ) malloc(( num_mods + 1 ) * sizeof( LDAPMod * ));
475 for( i = 0; i < num_mods; i++) {
476 mods[i] = (LDAPMod *) malloc(sizeof(LDAPMod));
477 memset(mods[i], 0, sizeof(LDAPMod));
478 }
479
480 i = 0;
481 if(nickserv_conf.ldap_field_password && *nickserv_conf.ldap_field_password &&
482 password) {
483 mods[i]->mod_op = LDAP_MOD_REPLACE;
484 mods[i]->mod_type = strdup(nickserv_conf.ldap_field_password);
485 mods[i]->mod_values = password_vals;
486 i++;
487 }
488
489 if(nickserv_conf.ldap_field_email && *nickserv_conf.ldap_field_email && email) {
490 mods[i]->mod_op = LDAP_MOD_REPLACE;
491 mods[i]->mod_type = strdup(nickserv_conf.ldap_field_email);
492 mods[i]->mod_values = email_vals;
493 i++;
494 }
495 mods[i] = NULL;
496 *num_mods_ret = num_mods;
497 return mods;
498}
499
73d4cc91
AS
500
501/* Save email or password to server
502 *
503 * password - UNENCRYPTED password. This function encrypts if libs are available
504 * email - email address
505 *
506 * NULL to make no change
507 */
508int ldap_do_modify(const char *account, const char *password, const char *email)
509{
510 char dn[MAXLEN];
511 LDAPMod **mods;
512 int rc, i;
513 int num_mods;
514 char *passbuf = NULL;
515
516 if(LDAP_SUCCESS != ( rc = ldap_do_admin_bind())) {
517 log_module(MAIN_LOG, LOG_ERROR, "failed to bind as admin");
518 return rc;
519 }
520
521 if(password) {
4b8ccfeb 522 passbuf = make_password(password);
73d4cc91
AS
523 }
524
525 snprintf(dn, MAXLEN-1, nickserv_conf.ldap_dn_fmt, account);
526 mods = make_mods_modify(passbuf, email, &num_mods);
527 if(!mods) {
528 log_module(MAIN_LOG, LOG_ERROR, "Error building mods for ldap_add");
529 return LDAP_OTHER;
530 }
531 rc = ldap_modify_s(ld, dn, mods);
532 if(rc != LDAP_SUCCESS) {
533 log_module(MAIN_LOG, LOG_ERROR, "Error adding ldap account: %s -- %s", account, ldap_err2string(rc));
534 return rc;
535 }
536 for(i = 0; i < num_mods; i++) {
537 free(mods[i]->mod_type);
538 free(mods[i]);
539 }
540 free(mods);
541 if(passbuf)
542 free(passbuf);
543 return rc;
544}
545
e166c31b
AS
546void ldap_close()
547{
548 ldap_unbind(ld);
549}
550
551#endif