1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <ctype.h>
25
26 #include <mailutils/mu_auth.h>
27 #include <mailutils/cstr.h>
28 #include <mailutils/io.h>
29
30 #ifdef WITH_LDAP
31 #include "mailutils/argcv.h"
32 #include "mailutils/wordsplit.h"
33 #include "mailutils/assoc.h"
34 #include "mailutils/list.h"
35 #include "mailutils/iterator.h"
36 #include "mailutils/mailbox.h"
37 #include "mailutils/sql.h"
38 #include "mailutils/mu_auth.h"
39 #include "mailutils/error.h"
40 #include "mailutils/errno.h"
41 #include "mailutils/nls.h"
42 #include "mailutils/util.h"
43 #include "mailutils/stream.h"
44 #include "mailutils/filter.h"
45 #include "mailutils/md5.h"
46 #include "mailutils/sha1.h"
47 #include "mailutils/ldap.h"
48
49 #include <ldap.h>
50 #include <lber.h>
51
52 const char *default_field_map =
53 "name=uid:"
54 "passwd=userPassword:"
55 "uid=uidNumber:"
56 "gid=gidNumber:"
57 "gecos=gecos:"
58 "dir=homeDirectory:"
59 "shell=loginShell";
60
61 static struct mu_ldap_module_config ldap_param;
62
63 static int
cb_field_map(void * data,mu_config_value_t * val)64 cb_field_map (void *data, mu_config_value_t *val)
65 {
66 char *err_term;
67 int rc = mu_cfg_field_map (val, &ldap_param.field_map, &err_term);
68
69 if (rc)
70 {
71 if (err_term)
72 mu_error (_("error near %s: %s"), err_term, mu_strerror (rc));
73 else
74 mu_error ("%s", mu_strerror (rc));
75 }
76
77 return rc;
78 }
79
80 static struct mu_cfg_param mu_ldap_param[] = {
81 { "enable", mu_c_bool, &ldap_param.enable, 0, NULL,
82 N_("Enable LDAP lookups.") },
83 { "url", mu_c_string, &ldap_param.url, 0, NULL,
84 N_("Set URL of the LDAP server."),
85 N_("url") },
86 { "base", mu_c_string, &ldap_param.base, 0, NULL,
87 N_("Base DN for LDAP lookups."),
88 N_("dn") },
89 { "binddn", mu_c_string, &ldap_param.binddn, 0, NULL,
90 N_("DN for accessing LDAP database."),
91 N_("dn") },
92 { "passwd", mu_c_string, &ldap_param.passwd, 0, NULL,
93 N_("Password for use with binddn.") },
94 { "tls", mu_c_bool, &ldap_param.tls, 0, NULL,
95 N_("Use TLS encryption.") },
96 { "debug", mu_c_int, &ldap_param.debug, 0, NULL,
97 N_("Set LDAP debugging level.") },
98 { "field-map", mu_cfg_callback, NULL, 0, cb_field_map,
99 N_("Set a field-map for parsing LDAP replies. The map is a "
100 "column-separated list of definitions. Each definition has the "
101 "following form:\n"
102 " <name: string>=<attr: string>\n"
103 "where <name> is one of the following: name, passwd, uid, gid, "
104 "gecos, dir, shell, mailbox, quota, and <attr> is the name of "
105 "the corresponding LDAP attribute."),
106 N_("map: definition") },
107 { "getpwnam", mu_c_string, &ldap_param.getpwnam_filter, 0, NULL,
108 N_("LDAP filter to use for getpwnam requests."),
109 N_("filter") },
110 { "getpwuid", mu_c_string, &ldap_param.getpwuid_filter, 0, NULL,
111 N_("LDAP filter to use for getpwuid requests."),
112 N_("filter") },
113 { NULL }
114 };
115
116 int
mu_ldap_section_parser(enum mu_cfg_section_stage stage,const mu_cfg_node_t * node,const char * section_label,void ** section_data,void * call_data,mu_cfg_tree_t * tree)117 mu_ldap_section_parser
118 (enum mu_cfg_section_stage stage, const mu_cfg_node_t *node,
119 const char *section_label, void **section_data,
120 void *call_data, mu_cfg_tree_t *tree)
121 {
122 switch (stage)
123 {
124 case mu_cfg_section_start:
125 ldap_param.enable = 1;
126 break;
127 default:
128 break;
129 }
130 return 0;
131 }
132
133
134 static void
module_init(void * ptr)135 module_init (void *ptr)
136 {
137 if (ldap_param.enable)
138 {
139 if (!ldap_param.getpwnam_filter)
140 ldap_param.getpwnam_filter =
141 "(&(objectClass=posixAccount) (uid=$user))";
142 if (!ldap_param.getpwuid_filter)
143 ldap_param.getpwuid_filter =
144 "(&(objectClass=posixAccount) (uidNumber=$user))";
145 if (!ldap_param.field_map)
146 {
147 struct mu_config_value val;
148 val.type = MU_CFG_STRING;
149 val.v.string = default_field_map;
150 if (mu_cfg_field_map (&val, &ldap_param.field_map, NULL))
151 abort ();
152 }
153 }
154 }
155
156 static int
_mu_conn_setup(LDAP ** pld)157 _mu_conn_setup (LDAP **pld)
158 {
159 int rc;
160 LDAPURLDesc *ludlist, **ludp;
161 char **urls = NULL;
162 int nurls = 0;
163 char *ldapuri = NULL;
164 LDAP *ld = NULL;
165 int protocol = LDAP_VERSION3; /* FIXME: must be configurable */
166
167 if (ldap_param.debug)
168 {
169 if (ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &ldap_param.debug)
170 != LBER_OPT_SUCCESS )
171 mu_error (_("cannot set LBER_OPT_DEBUG_LEVEL %d"), ldap_param.debug);
172
173 if (ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_param.debug)
174 != LDAP_OPT_SUCCESS )
175 mu_error (_("could not set LDAP_OPT_DEBUG_LEVEL %d"),
176 ldap_param.debug);
177 }
178
179 if (ldap_param.url)
180 {
181 rc = ldap_url_parse (ldap_param.url, &ludlist);
182 if (rc != LDAP_URL_SUCCESS)
183 {
184 mu_error (_("cannot parse LDAP URL(s)=%s (%d)"),
185 ldap_param.url, rc);
186 return 1;
187 }
188
189 for (ludp = &ludlist; *ludp; )
190 {
191 LDAPURLDesc *lud = *ludp;
192 char **tmp;
193
194 if (lud->lud_dn && lud->lud_dn[0]
195 && (lud->lud_host == NULL || lud->lud_host[0] == '\0'))
196 {
197 /* if no host but a DN is provided, try DNS SRV to gather the
198 host list */
199 char *domain = NULL, *hostlist = NULL;
200 size_t i;
201 struct mu_wordsplit ws;
202
203 if (ldap_dn2domain (lud->lud_dn, &domain) || !domain)
204 {
205 mu_error (_("DNS SRV: cannot convert DN=\"%s\" into a domain"),
206 lud->lud_dn );
207 goto dnssrv_free;
208 }
209
210 rc = ldap_domain2hostlist (domain, &hostlist);
211 if (rc)
212 {
213 mu_error (_("DNS SRV: cannot convert domain=%s into a hostlist"),
214 domain);
215 goto dnssrv_free;
216 }
217
218 if (mu_wordsplit (hostlist, &ws, MU_WRDSF_DEFFLAGS))
219 {
220 mu_error (_("DNS SRV: could not parse hostlist=\"%s\": %s"),
221 hostlist, mu_wordsplit_strerror (&ws));
222 goto dnssrv_free;
223 }
224
225 tmp = realloc (urls, sizeof(char *) * (nurls + ws.ws_wordc + 1));
226 if (!tmp)
227 {
228 mu_error ("DNS SRV %s", mu_strerror (errno));
229 goto dnssrv_free;
230 }
231
232 urls = tmp;
233 urls[nurls] = NULL;
234
235 for (i = 0; i < ws.ws_wordc; i++)
236 {
237 urls[nurls + i + 1] = NULL;
238 rc = mu_asprintf (&urls[nurls + i],
239 "%s://%s",
240 lud->lud_scheme, ws.ws_wordv[i]);
241 if (rc)
242 {
243 mu_error ("DNS SRV %s", mu_strerror (rc));
244 goto dnssrv_free;
245 }
246 }
247
248 nurls += i;
249
250 dnssrv_free:
251 mu_wordsplit_free (&ws);
252 ber_memfree (hostlist);
253 ber_memfree (domain);
254 }
255 else
256 {
257 tmp = realloc (urls, sizeof(char *) * (nurls + 2));
258 if (!tmp)
259 {
260 mu_error ("DNS SRV %s", mu_strerror (errno));
261 break;
262 }
263 urls = tmp;
264 urls[nurls + 1] = NULL;
265
266 urls[nurls] = ldap_url_desc2str (lud);
267 if (!urls[nurls])
268 {
269 mu_error ("DNS SRV %s", mu_strerror (errno));
270 break;
271 }
272 nurls++;
273 }
274
275 *ludp = lud->lud_next;
276
277 lud->lud_next = NULL;
278 ldap_free_urldesc (lud);
279 }
280
281 if (ludlist)
282 {
283 ldap_free_urldesc (ludlist);
284 return 1;
285 }
286 else if (!urls)
287 return 1;
288
289 rc = mu_argcv_string (nurls, urls, &ldapuri);
290 if (rc)
291 {
292 mu_error ("%s", mu_strerror (rc));
293 return 1;
294 }
295
296 ber_memvfree ((void **)urls);
297 }
298
299 mu_diag_output (MU_DIAG_INFO,
300 "constructed LDAP URI: %s", ldapuri ? ldapuri : "<DEFAULT>");
301
302 rc = ldap_initialize (&ld, ldapuri);
303 if (rc != LDAP_SUCCESS)
304 {
305 mu_error (_("cannot create LDAP session handle for URI=%s (%d): %s"),
306 ldapuri, rc, ldap_err2string (rc));
307
308 free (ldapuri);
309 return 1;
310 }
311 free (ldapuri);
312
313 ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
314
315 if (ldap_param.tls)
316 {
317 rc = ldap_start_tls_s (ld, NULL, NULL);
318 if (rc != LDAP_SUCCESS)
319 {
320 char *msg = NULL;
321 ldap_get_option (ld,
322 LDAP_OPT_DIAGNOSTIC_MESSAGE,
323 (void*)&msg);
324
325 mu_error (_("ldap_start_tls failed: %s"), ldap_err2string (rc));
326 mu_error (_("TLS diagnostics: %s"), msg);
327 ldap_memfree (msg);
328
329 ldap_unbind_ext (ld, NULL, NULL);
330
331 return 1;
332 }
333 }
334
335 /* FIXME: Timeouts, SASL, etc. */
336 *pld = ld;
337 return 0;
338 }
339
340 static int
_mu_ldap_bind(LDAP * ld)341 _mu_ldap_bind (LDAP *ld)
342 {
343 int msgid, err, rc;
344 LDAPMessage *result;
345 LDAPControl **ctrls;
346 char msgbuf[256];
347 char *matched = NULL;
348 char *info = NULL;
349 char **refs = NULL;
350 static struct berval passwd;
351
352 passwd.bv_val = ldap_param.passwd;
353 passwd.bv_len = passwd.bv_val ? strlen (passwd.bv_val) : 0;
354
355
356 msgbuf[0] = 0;
357
358 rc = ldap_sasl_bind (ld, ldap_param.binddn, LDAP_SASL_SIMPLE, &passwd,
359 NULL, NULL, &msgid);
360 if (msgid == -1)
361 {
362 mu_error ("ldap_sasl_bind(SIMPLE) failed: %s", ldap_err2string (rc));
363 return 1;
364 }
365
366 if (ldap_result (ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1)
367 {
368 mu_error ("ldap_result failed");
369 return 1;
370 }
371
372 rc = ldap_parse_result (ld, result, &err, &matched, &info, &refs,
373 &ctrls, 1);
374 if (rc != LDAP_SUCCESS)
375 {
376 mu_error ("ldap_parse_result failed: %s", ldap_err2string (rc));
377 return 1;
378 }
379
380 if (ctrls)
381 ldap_controls_free (ctrls);
382
383 if (err != LDAP_SUCCESS
384 || msgbuf[0]
385 || (matched && matched[0])
386 || (info && info[0])
387 || refs)
388 {
389 /* FIXME: Use debug output for that */
390 mu_error ("ldap_bind: %s (%d)%s", ldap_err2string (err), err, msgbuf);
391
392 if (matched && *matched)
393 mu_error ("matched DN: %s", matched);
394
395 if (info && *info)
396 mu_error ("additional info: %s", info);
397
398 if (refs && *refs)
399 {
400 int i;
401 mu_error ("referrals:");
402 for (i = 0; refs[i]; i++)
403 mu_error ("%s", refs[i]);
404 }
405
406 }
407
408 if (matched)
409 ber_memfree (matched);
410 if (info)
411 ber_memfree (info);
412 if (refs)
413 ber_memvfree ((void **)refs);
414
415 return err == LDAP_SUCCESS ? 0 : MU_ERR_FAILURE;
416 }
417
418 static void
_mu_ldap_unbind(LDAP * ld)419 _mu_ldap_unbind (LDAP *ld)
420 {
421 if (ld)
422 {
423 ldap_set_option (ld, LDAP_OPT_SERVER_CONTROLS, NULL);
424 ldap_unbind_ext (ld, NULL, NULL);
425 }
426 }
427
428 static int
_construct_attr_array(size_t * pargc,char *** pargv)429 _construct_attr_array (size_t *pargc, char ***pargv)
430 {
431 size_t count, i;
432 char **argv;
433 mu_iterator_t itr = NULL;
434
435 mu_assoc_count (ldap_param.field_map, &count);
436 if (count == 0)
437 return MU_ERR_FAILURE;
438 argv = calloc (count + 1, sizeof argv[0]);
439
440 mu_assoc_get_iterator (ldap_param.field_map, &itr);
441 for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr);
442 mu_iterator_next (itr), i++)
443 {
444 char *str;
445 mu_iterator_current (itr, (void**) &str);
446 if ((argv[i] = strdup (str)) == NULL)
447 {
448 mu_argcv_free (i, argv);
449 return ENOMEM;
450 }
451 }
452 mu_iterator_destroy (&itr);
453 argv[i] = NULL;
454
455 *pargc = count;
456 *pargv = argv;
457
458 return 0;
459 }
460
461 static void
get_quota(mu_off_t * pquota,const char * str)462 get_quota (mu_off_t *pquota, const char *str)
463 {
464 size_t n;
465 char *p;
466 int rc = mu_strtosize (str, &p, &n);
467 switch (rc)
468 {
469 case 0:
470 *pquota = n;
471 break;
472
473 case ERANGE:
474 mu_error (_("quota value is out of allowed range: %s"), str);
475 break;
476
477 case MU_ERR_PARSE:
478 mu_error (_("bad quota value: %s, stopped at %s"), str, p);
479 break;
480
481 default:
482 mu_diag_funcall (MU_DIAG_ERROR, "mu_strtosize", str, rc);
483 }
484 }
485
486 static void
_free_partial_auth_data(struct mu_auth_data * d)487 _free_partial_auth_data (struct mu_auth_data *d)
488 {
489 free (d->name);
490 free (d->passwd);
491 free (d->gecos);
492 free (d->dir);
493 free (d->shell);
494 free (d->mailbox);
495 }
496
497 static int
_assign_partial_auth_data(struct mu_auth_data * d,const char * key,const char * val)498 _assign_partial_auth_data (struct mu_auth_data *d, const char *key,
499 const char *val)
500 {
501 int rc = 0;
502
503 if (strcmp (key, MU_AUTH_NAME) == 0)
504 rc = (d->name = strdup (val)) ? 0 : errno;
505 else if (strcmp (key, MU_AUTH_PASSWD) == 0)
506 rc = (d->passwd = strdup (val)) ? 0 : errno;
507 else if (strcmp (key, MU_AUTH_UID) == 0)
508 d->uid = atoi (val);
509 else if (strcmp (key, MU_AUTH_GID) == 0)
510 d->gid = atoi (val);
511 else if (strcmp (key, MU_AUTH_GECOS) == 0)
512 rc = (d->gecos = strdup (val)) ? 0 : errno;
513 else if (strcmp (key, MU_AUTH_DIR) == 0)
514 rc = (d->dir = strdup (val)) ? 0 : errno;
515 else if (strcmp (key, MU_AUTH_SHELL) == 0)
516 rc = (d->shell = strdup (val)) ? 0 : errno;
517 else if (strcmp (key, MU_AUTH_MAILBOX) == 0)
518 rc = (d->mailbox = strdup (val)) ? 0 : errno;
519 else if (strcmp (key, MU_AUTH_QUOTA) == 0)
520 get_quota (&d->quota, val);
521 return rc;
522 }
523
524 static int
_mu_entry_to_auth_data(LDAP * ld,LDAPMessage * msg,struct mu_auth_data ** return_data)525 _mu_entry_to_auth_data (LDAP *ld, LDAPMessage *msg,
526 struct mu_auth_data **return_data)
527 {
528 int rc;
529 BerElement *ber = NULL;
530 struct berval bv;
531 char *ufn = NULL;
532 struct mu_auth_data d;
533 mu_iterator_t itr = NULL;
534
535 memset (&d, 0, sizeof d);
536
537 rc = ldap_get_dn_ber (ld, msg, &ber, &bv);
538 ufn = ldap_dn2ufn (bv.bv_val);
539 ldap_memfree (ufn);
540
541 mu_assoc_get_iterator (ldap_param.field_map, &itr);
542 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
543 mu_iterator_next (itr))
544 {
545 char *key;
546 char *attr;
547 struct berval **values;
548
549 mu_iterator_current_kv (itr, (const void **)&key, (void**) &attr);
550 values = ldap_get_values_len (ld, msg, attr);
551 if (!values || !values[0])
552 {
553 mu_error ("LDAP field `%s' (`%s') has NULL value",
554 key, attr);
555 _free_partial_auth_data (&d);
556 return MU_ERR_READ;
557 }
558
559 rc = _assign_partial_auth_data (&d, key, values[0]->bv_val);
560
561 ldap_value_free_len (values);
562 if (rc)
563 {
564 _free_partial_auth_data (&d);
565 return rc;
566 }
567 }
568
569 rc = mu_auth_data_alloc (return_data,
570 d.name,
571 d.passwd,
572 d.uid,
573 d.gid,
574 d.gecos,
575 d.dir,
576 d.shell,
577 d.mailbox,
578 1);
579 if (rc == 0)
580 mu_auth_data_set_quota (*return_data, d.quota);
581
582 _free_partial_auth_data (&d);
583
584 return rc;
585 }
586
587
588 static int
_mu_ldap_search(LDAP * ld,const char * filter_pat,const char * key,struct mu_auth_data ** return_data)589 _mu_ldap_search (LDAP *ld, const char *filter_pat, const char *key,
590 struct mu_auth_data **return_data)
591 {
592 int rc;
593 char **attrs;
594 size_t nattrs;
595 LDAPMessage *res, *msg;
596 ber_int_t msgid;
597 char *filter_str;
598
599 rc = _construct_attr_array (&nattrs, &attrs);
600 if (rc)
601 return rc;
602
603 rc = mu_str_vexpand (&filter_str, filter_pat, "user", key, NULL);
604 if (rc)
605 {
606 mu_argcv_free (nattrs, attrs);
607 if (rc == MU_ERR_FAILURE)
608 {
609 mu_error (_("cannot expand line `%s': %s"), filter_pat,
610 filter_str);
611 free (filter_str);
612 }
613 else
614 {
615 mu_error (_("cannot expand line `%s': %s"), filter_pat,
616 mu_strerror (rc));
617 }
618 return rc;
619 }
620
621 rc = ldap_search_ext (ld, ldap_param.base, LDAP_SCOPE_SUBTREE,
622 filter_str, attrs, 0,
623 NULL, NULL, NULL, -1, &msgid);
624 free (filter_str);
625 mu_argcv_free (nattrs, attrs);
626
627 if (rc != LDAP_SUCCESS)
628 {
629 mu_error ("ldap_search_ext: %s", ldap_err2string (rc));
630 if (rc == LDAP_NO_SUCH_OBJECT)
631 return MU_ERR_NOENT;
632 else
633 return MU_ERR_FAILURE;
634 }
635
636 rc = ldap_result (ld, msgid, LDAP_MSG_ALL, NULL, &res);
637 if (rc < 0)
638 {
639 mu_error ("ldap_result failed");
640 return MU_ERR_FAILURE;
641 }
642
643 rc = ldap_count_entries (ld, res);
644 if (rc == 0)
645 {
646 mu_error ("not enough entires");
647 return MU_ERR_NOENT;
648 }
649 if (rc > 1)
650 mu_error ("LDAP: too many entries for key %s", key);
651
652
653 msg = ldap_first_entry (ld, res);
654 rc = _mu_entry_to_auth_data (ld, msg, return_data);
655 ldap_msgfree (res);
656
657 return rc;
658 }
659
660
661
662 typedef int (*pwcheck_fp) (const char *, const char *);
663
664
665 static int
chk_crypt(const char * db_pass,const char * pass)666 chk_crypt (const char *db_pass, const char *pass)
667 {
668 return strcmp (db_pass, crypt (pass, db_pass)) == 0 ?
669 0 : MU_ERR_AUTH_FAILURE;
670 }
671
672 static int
chk_md5(const char * db_pass,const char * pass)673 chk_md5 (const char *db_pass, const char *pass)
674 {
675 unsigned char md5digest[16];
676 unsigned char d1[16];
677 struct mu_md5_ctx md5context;
678 mu_stream_t str = NULL, flt = NULL;
679
680 mu_md5_init_ctx (&md5context);
681 mu_md5_process_bytes (pass, strlen (pass), &md5context);
682 mu_md5_finish_ctx (&md5context, md5digest);
683
684 mu_static_memory_stream_create (&str, db_pass, strlen (db_pass));
685 mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
686 mu_stream_unref (str);
687
688 mu_stream_read (flt, (char*) d1, sizeof d1, NULL);
689 mu_stream_destroy (&flt);
690
691 return memcmp (md5digest, d1, sizeof md5digest) == 0 ?
692 0 : MU_ERR_AUTH_FAILURE;
693 }
694
695 static int
chk_smd5(const char * db_pass,const char * pass)696 chk_smd5 (const char *db_pass, const char *pass)
697 {
698 int rc;
699 unsigned char md5digest[16];
700 unsigned char *d1;
701 struct mu_md5_ctx md5context;
702 mu_stream_t str = NULL, flt = NULL;
703 size_t size;
704
705 size = strlen (db_pass);
706 mu_static_memory_stream_create (&str, db_pass, size);
707 mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
708 mu_stream_unref (str);
709
710 d1 = malloc (size);
711 if (!d1)
712 {
713 mu_stream_destroy (&flt);
714 return ENOMEM;
715 }
716
717 mu_stream_read (flt, (char*) d1, size, &size);
718 mu_stream_destroy (&flt);
719
720 if (size <= 16)
721 {
722 mu_error ("malformed SMD5 password: %s", db_pass);
723 return MU_ERR_FAILURE;
724 }
725
726 mu_md5_init_ctx (&md5context);
727 mu_md5_process_bytes (pass, strlen (pass), &md5context);
728 mu_md5_process_bytes (d1 + 16, size - 16, &md5context);
729 mu_md5_finish_ctx (&md5context, md5digest);
730
731 rc = memcmp (md5digest, d1, sizeof md5digest) == 0 ?
732 0 : MU_ERR_AUTH_FAILURE;
733 free (d1);
734 return rc;
735 }
736
737 static int
chk_sha(const char * db_pass,const char * pass)738 chk_sha (const char *db_pass, const char *pass)
739 {
740 unsigned char sha1digest[20];
741 unsigned char d1[20];
742 mu_stream_t str = NULL, flt = NULL;
743 struct mu_sha1_ctx sha1context;
744
745 mu_sha1_init_ctx (&sha1context);
746 mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
747 mu_sha1_finish_ctx (&sha1context, sha1digest);
748
749 mu_static_memory_stream_create (&str, db_pass, strlen (db_pass));
750 mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
751 mu_stream_unref (str);
752
753 mu_stream_read (flt, (char*) d1, sizeof d1, NULL);
754 mu_stream_destroy (&flt);
755
756 return memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
757 0 : MU_ERR_AUTH_FAILURE;
758 }
759
760 static int
chk_ssha(const char * db_pass,const char * pass)761 chk_ssha (const char *db_pass, const char *pass)
762 {
763 int rc;
764 unsigned char sha1digest[20];
765 unsigned char *d1;
766 struct mu_sha1_ctx sha1context;
767 mu_stream_t str = NULL, flt = NULL;
768 size_t size;
769
770 size = strlen (db_pass);
771 mu_static_memory_stream_create (&str, db_pass, size);
772 mu_filter_create (&flt, str, "base64", MU_FILTER_DECODE, MU_STREAM_READ);
773 mu_stream_unref (str);
774
775 d1 = malloc (size);
776 if (!d1)
777 {
778 mu_stream_destroy (&flt);
779 return ENOMEM;
780 }
781
782 mu_stream_read (flt, (char*) d1, size, &size);
783 mu_stream_destroy (&flt);
784
785 if (size <= 16)
786 {
787 mu_error ("malformed SSHA1 password: %s", db_pass);
788 return MU_ERR_FAILURE;
789 }
790
791 mu_sha1_init_ctx (&sha1context);
792 mu_sha1_process_bytes (pass, strlen (pass), &sha1context);
793 mu_sha1_process_bytes (d1 + 20, size - 20, &sha1context);
794 mu_sha1_finish_ctx (&sha1context, sha1digest);
795
796 rc = memcmp (sha1digest, d1, sizeof sha1digest) == 0 ?
797 0 : MU_ERR_AUTH_FAILURE;
798 free (d1);
799 return rc;
800 }
801
802 static struct passwd_algo
803 {
804 char *algo;
805 size_t len;
806 pwcheck_fp pwcheck;
807 } pwtab[] = {
808 #define DP(s, f) { #s, sizeof (#s) - 1, f }
809 DP (CRYPT, chk_crypt),
810 DP (MD5, chk_md5),
811 DP (SMD5, chk_smd5),
812 DP (SHA, chk_sha),
813 DP (SSHA, chk_ssha),
814 { NULL }
815 #undef DP
816 };
817
818 static pwcheck_fp
find_pwcheck(const char * algo,int len)819 find_pwcheck (const char *algo, int len)
820 {
821 struct passwd_algo *p;
822 for (p = pwtab; p->algo; p++)
823 if (len == p->len && mu_c_strncasecmp (p->algo, algo, len) == 0)
824 return p->pwcheck;
825 return NULL;
826 }
827
828 static int
mu_ldap_authenticate(struct mu_auth_data ** return_data MU_ARG_UNUSED,const void * key,void * func_data MU_ARG_UNUSED,void * call_data)829 mu_ldap_authenticate (struct mu_auth_data **return_data MU_ARG_UNUSED,
830 const void *key,
831 void *func_data MU_ARG_UNUSED, void *call_data)
832 {
833 const struct mu_auth_data *auth_data = key;
834 char *db_pass = auth_data->passwd;
835 char *pass = call_data;
836
837 if (auth_data->passwd == NULL || !pass)
838 return EINVAL;
839
840 if (db_pass[0] == '{')
841 {
842 int len;
843 char *algo;
844 pwcheck_fp pwcheck;
845
846
847 algo = db_pass + 1;
848 for (len = 0; algo[len] != '}'; len++)
849 if (algo[len] == 0)
850 {
851 /* Possibly malformed password, try plaintext anyway */
852 return strcmp (db_pass, pass) == 0 ? 0 : MU_ERR_FAILURE;
853 }
854 db_pass = algo + len + 1;
855 pwcheck = find_pwcheck (algo, len);
856 if (pwcheck)
857 return pwcheck (db_pass, pass);
858 else
859 {
860 mu_error ("Unsupported password algorithm scheme: %.*s",
861 len, algo);
862 return MU_ERR_FAILURE;
863 }
864 }
865
866 return strcmp (db_pass, pass) == 0 ? 0 : MU_ERR_AUTH_FAILURE;
867 }
868
869
870 static int
mu_auth_ldap_user_by_name(struct mu_auth_data ** return_data,const void * key,void * func_data MU_ARG_UNUSED,void * call_data MU_ARG_UNUSED)871 mu_auth_ldap_user_by_name (struct mu_auth_data **return_data,
872 const void *key,
873 void *func_data MU_ARG_UNUSED,
874 void *call_data MU_ARG_UNUSED)
875 {
876 int rc;
877 LDAP *ld;
878
879 if (!ldap_param.enable)
880 return ENOSYS;
881 if (_mu_conn_setup (&ld))
882 return MU_ERR_FAILURE;
883 if (_mu_ldap_bind (ld))
884 return MU_ERR_FAILURE;
885 rc = _mu_ldap_search (ld, ldap_param.getpwnam_filter, key, return_data);
886 _mu_ldap_unbind (ld);
887 return rc;
888 }
889
890 static int
mu_auth_ldap_user_by_uid(struct mu_auth_data ** return_data,const void * key,void * func_data MU_ARG_UNUSED,void * call_data MU_ARG_UNUSED)891 mu_auth_ldap_user_by_uid (struct mu_auth_data **return_data,
892 const void *key,
893 void *func_data MU_ARG_UNUSED,
894 void *call_data MU_ARG_UNUSED)
895 {
896 int rc;
897 LDAP *ld;
898 char uidstr[128];
899
900 if (!ldap_param.enable)
901 return ENOSYS;
902 if (_mu_conn_setup (&ld))
903 return MU_ERR_FAILURE;
904 if (_mu_ldap_bind (ld))
905 return MU_ERR_FAILURE;
906
907 snprintf (uidstr, sizeof (uidstr), "%u", *(uid_t*)key);
908 rc = _mu_ldap_search (ld, ldap_param.getpwuid_filter, uidstr, return_data);
909 _mu_ldap_unbind (ld);
910 return rc;
911 }
912
913
914 #else
915 # define module_init NULL
916 # define mu_ldap_authenticate mu_auth_nosupport
917 # define mu_auth_ldap_user_by_name mu_auth_nosupport
918 # define mu_auth_ldap_user_by_uid mu_auth_nosupport
919 # define mu_ldap_param NULL
920
921 #endif
922
923 struct mu_auth_module mu_auth_ldap_module = {
924 .name = "ldap",
925 .commit = module_init,
926 .handler = {
927 [mu_auth_authenticate] = mu_ldap_authenticate,
928 [mu_auth_getpwnam] = mu_auth_ldap_user_by_name,
929 [mu_auth_getpwuid] = mu_auth_ldap_user_by_uid
930 },
931 .cfg = mu_ldap_param
932 };
933