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