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