3 * Copyright (c) 1990 Regents of the University of Michigan.
6 * Redistribution and use in source and binary forms are permitted
7 * provided that this notice is preserved and that due credit is given
8 * to the University of Michigan at Ann Arbor. The name of the University
9 * may not be used to endorse or promote products derived from this
10 * software without specific prior written permission. This software
11 * is provided ``as is'' without express or implied warranty.
13 * Copyright 1998-2000 The OpenLDAP Foundation
14 * COPYING RESTRICTIONS APPLY. See COPYRIGHT File in top level directory
15 * of this package for details.
22 #include <ac/stdlib.h>
26 #include <ac/signal.h>
27 #include <ac/string.h>
28 #include <ac/sysexits.h>
29 #include <ac/syslog.h>
31 #include <ac/unistd.h>
36 #ifdef HAVE_SYS_RESOURCE_H
37 #include <sys/resource.h>
42 #include "ldap_defaults.h"
44 #ifndef MAIL500_BOUNCEFROM
45 #define MAIL500_BOUNCEFROM "<>"
49 #define GROUP_ERRORS 0x02
50 #define GROUP_REQUEST 0x04
51 #define GROUP_MEMBERS 0x08
52 #define GROUP_OWNER 0x10
55 #define ERRORS "errors"
56 #define REQUEST "request"
57 #define REQUESTS "requests"
58 #define MEMBERS "members"
60 #define OWNERS "owners"
63 char *vacationhost = NULL;
64 char *errorsfrom = MAIL500_BOUNCEFROM;
65 char *mailfrom = NULL;
67 char *ldaphost = NULL;
73 #define E_USERUNKNOWN 1
79 #define E_JOINMEMBERNOEMAIL 7
80 #define E_MEMBERNOEMAIL 8
82 #define E_NOMEMBERS 10
84 #define E_GROUPUNKNOWN 12
85 #define E_NOOWNADDRESS 13
91 #define e_msg e_union.e_u_msg
92 #define e_loop e_union.e_u_loop
95 typedef struct groupto {
102 typedef struct baseinfo {
105 char b_rdnpref; /* give rdn's preference when searching? */
106 int b_search; /* ORed with the type of thing the address */
107 /* looks like (USER, GROUP_ERRORS, etc.) */
108 /* to see if this should be searched */
113 char *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL };
115 typedef struct attr_semantics {
117 int as_m_valued; /* Is multivalued? */
118 int as_priority; /* Priority level of this attribut type */
119 int as_syntax; /* How to interpret values */
120 int as_m_entries; /* Can resolve to several entries? */
121 int as_kind; /* Recipient, sender, etc. */
122 char *as_param; /* Extra info for filters and things alike */
125 #define AS_SYNTAX_UNKNOWN 0
126 #define AS_SYNTAX_NATIVE_MB 1 /* Unqualified mailbox name */
127 #define AS_SYNTAX_RFC822 2 /* RFC822 mail address */
128 #define AS_SYNTAX_HOST 3
129 #define AS_SYNTAX_DN 4 /* A directory entry */
130 #define AS_SYNTAX_RFC822_EXT 5
131 #define AS_SYNTAX_URL 6 /* mailto: or ldap: URL */
132 #define AS_SYNTAX_BOOL_FILTER 7 /* For joinable, filter in as_param */
133 #define AS_SYNTAX_PRESENT 8 /* Value irrelevant, only presence is
136 #define AS_KIND_UNKNOWN 0
137 #define AS_KIND_RECIPIENT 1
138 #define AS_KIND_ERRORS 2 /* For ErrorsTo and similar */
139 #define AS_KIND_REQUEST 3
140 #define AS_KIND_OWNER 4
141 #define AS_KIND_ROUTE_TO_HOST 5 /* Expand at some other host */
142 #define AS_KIND_ALLOWED_SENDER 6 /* Can send to group */
143 #define AS_KIND_MODERATOR 7
144 #define AS_KIND_ROUTE_TO_ADDR 8 /* Rewrite recipient address as */
145 #define AS_KIND_OWN_ADDR 9 /* RFC822 name of this entry */
146 #define AS_KIND_DELIVERY_TYPE 10 /* How to deliver mail to this entry */
148 AttrSemantics **attr_semantics = NULL;
149 int current_priority = 0;
151 typedef struct subst {
156 char **groupclasses = NULL;
157 char **def_attr = NULL;
158 char **myhosts = NULL; /* FQDNs not to route elsewhere */
159 char **mydomains = NULL; /* If an RFC822 address points to one
160 of these domains, search it in the
161 directory instead of returning it
164 static void load_config( char *filespec );
165 static void split_address( char *address, char **localpart, char **domainpart);
166 static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
167 static void do_address( char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
168 static void send_message( char **to );
169 static void send_errors( Error *err, int nerr );
170 static void do_noemail( FILE *fp, Error *err, int namelen );
171 static void do_ambiguous( FILE *fp, Error *err, int namelen );
172 static int count_values( char **list );
173 static void add_to( char ***list, int *nlist, char **new );
174 static void add_single_to( char ***list, char *new );
175 static int isgroup( LDAPMessage *e );
176 static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
177 static void unbind_and_exit( int rc ) LDAP_GCCATTR((noreturn));
178 static void send_group( Group **group, int ngroup );
180 static int connect_to_x500( void );
184 main ( int argc, char **argv )
190 int numto, ngroups, numerr, nargs;
192 char *conffile = NULL;
194 if ( (myname = strrchr( argv[0], '/' )) == NULL )
195 myname = strdup( argv[0] );
197 myname = strdup( myname + 1 );
200 (void) SIGNAL( SIGPIPE, SIG_IGN );
204 openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
206 openlog( myname, OPENLOG_OPTIONS );
209 while ( (i = getopt( argc, argv, "d:C:f:h:l:m:v:" )) != EOF ) {
211 case 'd': /* turn on debugging */
212 debug |= atoi( optarg );
215 case 'C': /* path to configuration file */
216 conffile = strdup( optarg );
219 case 'f': /* who it's from & where errors should go */
220 mailfrom = strdup( optarg );
221 for ( j = 0; sendmailargs[j] != NULL; j++ ) {
222 if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
223 sendmailargs[j+1] = mailfrom;
229 case 'h': /* hostname */
230 host = strdup( optarg );
231 hostlen = strlen(host);
234 case 'l': /* ldap host */
235 ldaphost = strdup( optarg );
238 /* mailer-daemon address - who we should */
239 case 'm': /* say errors come from */
240 errorsfrom = strdup( optarg );
243 case 'v': /* vacation host */
244 vacationhost = strdup( optarg );
248 syslog( LOG_ALERT, "unknown option" );
253 if ( mailfrom == NULL ) {
254 syslog( LOG_ALERT, "required argument -f not present" );
257 if ( errorsfrom == NULL ) {
258 syslog( LOG_ALERT, "required argument -m not present" );
261 /* if ( host == NULL ) { */
262 /* syslog( LOG_ALERT, "required argument -h not present" ); */
263 /* exit( EX_TEMPFAIL ); */
265 if ( conffile == NULL ) {
266 syslog( LOG_ALERT, "required argument -C not present" );
270 load_config( conffile );
272 if ( connect_to_x500() != 0 )
281 syslog( LOG_ALERT, "running as %d", geteuid() );
282 strcpy( buf, argv[0] );
283 for ( i = 1; i < argc; i++ ) {
285 strcat( buf, argv[i] );
288 syslog( LOG_ALERT, "args: (%s)", buf );
293 add_to( &tolist, &numto, sendmailargs );
295 ngroups = numerr = 0;
298 for ( i = optind; i < argc; i++ ) {
301 char *localpart = NULL, *domainpart = NULL;
305 split_address( argv[i], &localpart, &domainpart );
306 if ( (s = strrchr( localpart, '-' )) != NULL ) {
309 if ((strcasecmp(s, ERROR) == 0) ||
310 (strcasecmp(s, ERRORS) == 0)) {
313 } else if ((strcasecmp(s, REQUEST) == 0) ||
314 (strcasecmp(s, REQUESTS) == 0)) {
315 type = GROUP_REQUEST;
317 } else if ( strcasecmp( s, MEMBERS ) == 0 ) {
318 type = GROUP_MEMBERS;
320 } else if ((strcasecmp(s, OWNER) == 0) ||
321 (strcasecmp(s, OWNERS) == 0)) {
328 sprintf( address, "%s@%s", localpart, domainpart );
332 sprintf( address, "%s@%s", localpart, domainpart );
335 do_address( address, &tolist, &numto, &togroups, &ngroups,
336 &errlist, &numerr, type );
340 * If we have both errors and successful deliveries to make or if
341 * if there are any groups to deliver to, we basically need to read
342 * the message twice. So, we have to put it in a tmp file.
345 if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
350 if ( (fp = tmpfile()) == NULL ) {
351 syslog( LOG_ALERT, "could not open tmp file" );
352 unbind_and_exit( EX_TEMPFAIL );
355 /* copy the message to a temp file */
356 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
357 if ( fputs( buf, fp ) == EOF ) {
358 syslog( LOG_ALERT, "error writing tmpfile" );
359 unbind_and_exit( EX_TEMPFAIL );
363 if ( dup2( fileno( fp ), 0 ) == -1 ) {
364 syslog( LOG_ALERT, "could not dup2 tmpfile" );
365 unbind_and_exit( EX_TEMPFAIL );
371 /* deal with errors */
374 syslog( LOG_ALERT, "sending errors" );
376 (void) rewind( stdin );
377 send_errors( errlist, numerr );
380 (void) ldap_unbind( ld );
382 /* send to groups with errorsTo */
385 syslog( LOG_ALERT, "sending to groups with errorsto" );
387 (void) rewind( stdin );
388 send_group( togroups, ngroups );
391 /* send to expanded aliases and groups w/o errorsTo */
392 if ( numto > nargs ) {
394 syslog( LOG_ALERT, "sending to aliases and groups" );
396 (void) rewind( stdin );
397 send_message( tolist );
404 get_config_line( FILE *cf, int *lineno)
406 static char buf[2048];
412 room = sizeof( buf );
413 while ( fgets( &buf[pos], room, cf ) ) {
416 /* Delete whitespace at the beginning of new data */
417 if ( isspace( buf[pos] ) ) {
419 for ( s = buf+pos; isspace(*s); s++ )
421 for ( d = buf+pos; *s; s++, d++ ) {
428 if ( buf[len-1] != '\n' ) {
429 syslog( LOG_ALERT, "Definition too long at line %d",
435 if ( strspn( buf, " \t\n" ) == len )
437 if ( buf[len-2] == '\\' ) {
439 room = sizeof(buf) - pos;
442 /* We have a real line, we will exit the loop */
450 add_url ( char *url, int rdnpref, int typemask )
456 b = calloc(1, sizeof(Base));
458 syslog( LOG_ALERT, "Out of memory" );
461 b->b_url = strdup( url );
462 b->b_rdnpref = rdnpref;
463 b->b_search = typemask;
465 if ( base == NULL ) {
466 base = calloc(2, sizeof(LDAPURLDesc *));
468 syslog( LOG_ALERT, "Out of memory" );
473 for ( size = 0; base[size]; size++ )
476 list_temp = realloc( base, size*sizeof(LDAPURLDesc *) );
478 syslog( LOG_ALERT, "Out of memory" );
488 add_def_attr( char *s )
494 p += strspn( p, "\t," );
495 q = strpbrk( p, " \t," );
498 add_single_to( &def_attr, p );
500 add_single_to( &def_attr, p );
508 add_attr_semantics( char *s )
513 as = calloc( 1, sizeof( AttrSemantics ) );
514 as->as_priority = current_priority;
516 while ( isspace ( *p ) )
519 while ( !isspace ( *q ) && *q != '\0' )
522 as->as_name = strdup( p );
526 while ( isspace ( *p ) )
529 while ( !isspace ( *q ) && *q != '\0' )
532 if ( !strcasecmp( p, "multivalued" ) ) {
534 } else if ( !strcasecmp( p, "multiple-entries" ) ) {
535 as->as_m_entries = 1;
536 } else if ( !strcasecmp( p, "local-native-mailbox" ) ) {
537 as->as_syntax = AS_SYNTAX_NATIVE_MB;
538 } else if ( !strcasecmp( p, "rfc822" ) ) {
539 as->as_syntax = AS_SYNTAX_RFC822;
540 } else if ( !strcasecmp( p, "rfc822-extended" ) ) {
541 as->as_syntax = AS_SYNTAX_RFC822_EXT;
542 } else if ( !strcasecmp( p, "dn" ) ) {
543 as->as_syntax = AS_SYNTAX_DN;
544 } else if ( !strcasecmp( p, "url" ) ) {
545 as->as_syntax = AS_SYNTAX_URL;
546 } else if ( !strcasecmp( p, "search-with-filter" ) ) {
547 as->as_syntax = AS_SYNTAX_BOOL_FILTER;
548 } else if ( !strncasecmp( p, "param=", 6 ) ) {
549 q = strchr( p, '=' );
552 while ( *q && !isspace( *q ) ) {
557 as->as_param = strdup( p );
560 as->as_param = strdup( p );
564 } else if ( !strcasecmp( p, "host" ) ) {
565 as->as_kind = AS_SYNTAX_HOST;
566 } else if ( !strcasecmp( p, "present" ) ) {
567 as->as_kind = AS_SYNTAX_PRESENT;
568 } else if ( !strcasecmp( p, "route-to-host" ) ) {
569 as->as_kind = AS_KIND_ROUTE_TO_HOST;
570 } else if ( !strcasecmp( p, "route-to-address" ) ) {
571 as->as_kind = AS_KIND_ROUTE_TO_ADDR;
572 } else if ( !strcasecmp( p, "own-address" ) ) {
573 as->as_kind = AS_KIND_OWN_ADDR;
574 } else if ( !strcasecmp( p, "recipient" ) ) {
575 as->as_kind = AS_KIND_RECIPIENT;
576 } else if ( !strcasecmp( p, "errors" ) ) {
577 as->as_kind = AS_KIND_ERRORS;
578 } else if ( !strcasecmp( p, "request" ) ) {
579 as->as_kind = AS_KIND_REQUEST;
580 } else if ( !strcasecmp( p, "owner" ) ) {
581 as->as_kind = AS_KIND_OWNER;
582 } else if ( !strcasecmp( p, "delivery-type" ) ) {
583 as->as_kind = AS_KIND_DELIVERY_TYPE;
586 "Unknown semantics word %s", p );
591 if ( attr_semantics == NULL ) {
592 attr_semantics = calloc(2, sizeof(AttrSemantics *));
593 if ( !attr_semantics ) {
594 syslog( LOG_ALERT, "Out of memory" );
597 attr_semantics[0] = as;
600 AttrSemantics **list_temp;
601 for ( size = 0; attr_semantics[size]; size++ )
604 list_temp = realloc( attr_semantics,
605 size*sizeof(AttrSemantics *) );
607 syslog( LOG_ALERT, "Out of memory" );
610 attr_semantics = list_temp;
611 attr_semantics[size-2] = as;
612 attr_semantics[size-1] = NULL;
617 load_config( char *filespec )
626 cf = fopen( filespec, "r" );
628 perror( "Opening config file" );
632 while ( ( line = get_config_line( cf,&lineno ) ) ) {
633 p = strpbrk( line, " \t" );
636 "Missing space at line %d", lineno );
639 if ( !strncmp( line, "search", p-line ) ) {
640 p += strspn( p, " \t" );
644 add_url( p, rdnpref, typemask );
645 } else if ( !strncmp(line, "attribute", p-line) ) {
646 p += strspn(p, " \t");
647 add_attr_semantics( p );
648 } else if ( !strncmp(line, "default-attributes", p-line) ) {
649 p += strspn(p, " \t");
651 } else if ( !strncmp(line, "group-classes", p-line) ) {
652 p += strspn(p, " \t");
653 add_single_to( &groupclasses, p );
654 } else if ( !strncmp(line, "priority", p-line) ) {
655 p += strspn(p, " \t");
656 current_priority = atoi(p);
657 } else if ( !strncmp(line, "domain", p-line) ) {
658 p += strspn(p, " \t");
659 add_single_to( &mydomains, p );
660 } else if ( !strncmp(line, "host", p-line) ) {
661 p += strspn(p, " \t");
662 add_single_to( &myhosts, p );
665 "Unparseable config definition at line %d",
674 connect_to_x500( void )
678 if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
679 syslog( LOG_ALERT, "ldap_init failed" );
683 /* TBC: Set this only when it makes sense
684 opt = MAIL500_MAXAMBIGUOUS;
685 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
687 opt = LDAP_DEREF_ALWAYS;
688 ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
690 if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
691 syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
699 new_group( char *dn, Group ***list, int *nlist )
704 for ( i = 0; i < *nlist; i++ ) {
705 if ( strcasecmp( dn, (*list)[i]->g_dn ) == 0 ) {
706 syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
711 this_group = (Group *) malloc( sizeof(Group) );
714 *list = (Group **) malloc( sizeof(Group *) );
716 *list = (Group **) realloc( *list, (*nlist + 1) *
720 this_group->g_errorsto = NULL;
721 this_group->g_members = NULL;
722 this_group->g_nmembers = 0;
723 /* save the group's dn so we can check for loops above */
724 this_group->g_dn = strdup( dn );
726 (*list)[*nlist] = this_group;
729 return( this_group );
741 if ( ( p = strrchr( address, '@' ) ) == NULL ) {
742 *localpart = strdup( address );
745 *localpart = malloc( p - address + 1 );
746 strncpy( *localpart, address, p - address );
747 (*localpart)[p - address] = '\0';
749 *domainpart = strdup( p );
768 LDAPMessage *res, *e;
769 struct timeval timeout;
771 timeout.tv_sec = MAIL500_TIMEOUT;
773 for ( i = 0; dnlist[i]; i++ ) {
774 if ( (rc = ldap_search_st( ld, dnlist[i], LDAP_SCOPE_BASE,
776 &timeout, &res )) != LDAP_SUCCESS ) {
777 if ( rc == LDAP_NO_SUCH_OBJECT ) {
778 add_error( err, nerr, E_BADMEMBER, dnlist[i], NULL );
781 syslog( LOG_ALERT, "member search return 0x%x", rc );
783 unbind_and_exit( EX_TEMPFAIL );
786 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
787 syslog( LOG_ALERT, "member search error parsing entry" );
788 unbind_and_exit( EX_TEMPFAIL );
790 if ( entry_engine( e, dnlist[i], address, to, nto,
791 togroups, ngroups, err, nerr,
792 USER | GROUP_MEMBERS ) ) {
820 LDAPMessage *e, *res;
823 struct timeval timeout;
828 timeout.tv_sec = MAIL500_TIMEOUT;
831 rc = ldap_url_parse( url, &ludp );
834 case LDAP_URL_ERR_BADSCHEME:
836 "Not an LDAP URL: %s", url );
838 case LDAP_URL_ERR_BADENCLOSURE:
840 "Bad Enclosure in URL: %s", url );
842 case LDAP_URL_ERR_BADURL:
844 "Bad URL: %s", url );
846 case LDAP_URL_ERR_BADHOST:
848 "Host is invalid in URL: %s", url );
850 case LDAP_URL_ERR_BADATTRS:
852 "Attributes are invalid in URL: %s", url );
854 case LDAP_URL_ERR_BADSCOPE:
856 "Scope is invalid in URL: %s", url );
858 case LDAP_URL_ERR_BADFILTER:
860 "Filter is invalid in URL: %s", url );
862 case LDAP_URL_ERR_BADEXTS:
864 "Extensions are invalid in URL: %s", url );
866 case LDAP_URL_ERR_MEM:
868 "Out of memory parsing URL: %s", url );
870 case LDAP_URL_ERR_PARAM:
872 "bad parameter parsing URL: %s", url );
876 "Unknown error %d parsing URL: %s",
880 add_error( err, nerr, E_BADMEMBER,
886 for ( s = ludp->lud_filter, d = filter; *s; s++,d++ ) {
893 for ( i = 0; substs[i].sub_char != '\0';
895 if ( *s == substs[i].sub_char ) {
896 for ( p = substs[i].sub_value;
904 if ( substs[i].sub_char == '\0' ) {
906 "unknown format %c", *s );
914 strncpy( filter, ludp->lud_filter, sizeof( filter ) - 1 );
915 filter[ sizeof( filter ) - 1 ] = '\0';
918 if ( ludp->lud_attrs ) {
919 attrlist = ludp->lud_attrs;
924 /* TBC: we don't read the host, dammit */
925 rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope,
929 /* some other trouble - try again later */
930 if ( rc != LDAP_SUCCESS &&
931 rc != LDAP_SIZELIMIT_EXCEEDED ) {
932 syslog( LOG_ALERT, "return 0x%x from X.500",
934 unbind_and_exit( EX_TEMPFAIL );
937 match = ldap_count_entries( ld, res );
939 /* trouble - try again later */
941 syslog( LOG_ALERT, "error parsing result from X.500" );
942 unbind_and_exit( EX_TEMPFAIL );
945 if ( match == 1 || multi_entry ) {
946 for ( e = ldap_first_entry( ld, res ); e != NULL;
947 e = ldap_next_entry( ld, e ) ) {
948 dn = ldap_get_dn( ld, e );
949 resolved = entry_engine( e, dn, address, to, nto,
953 add_error( err, nerr, E_NOEMAIL, address, res );
959 /* more than one match - bounce with ambiguous user? */
961 LDAPMessage *next, *tmpres = NULL;
965 /* not giving rdn preference - bounce with ambiguous user */
966 if ( rdnpref == 0 ) {
967 add_error( err, nerr, E_AMBIGUOUS, address, res );
972 * giving rdn preference - see if any entries were matched
973 * because of their rdn. If so, collect them to deal with
974 * later (== 1 we deliver, > 1 we bounce).
977 for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
978 next = ldap_next_entry( ld, e );
979 dn = ldap_get_dn( ld, e );
980 xdn = ldap_explode_dn( dn, 1 );
982 /* XXX bad, but how else can we do it? XXX */
983 if ( strcasecmp( xdn[0], address ) == 0 ) {
984 ldap_delete_result_entry( &res, e );
985 ldap_add_result_entry( &tmpres, e );
988 ldap_value_free( xdn );
992 /* nothing matched by rdn - go ahead and bounce */
993 if ( tmpres == NULL ) {
994 add_error( err, nerr, E_AMBIGUOUS, address, res );
997 /* more than one matched by rdn - bounce with rdn matches */
998 } else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
999 add_error( err, nerr, E_AMBIGUOUS, address, tmpres );
1003 } else if ( match < 0 ) {
1004 syslog( LOG_ALERT, "error parsing result from X.500" );
1005 unbind_and_exit( EX_TEMPFAIL );
1008 /* otherwise one matched by rdn - send to it */
1009 ldap_msgfree( res );
1013 if ( (e = ldap_first_entry( ld, res )) == NULL ) {
1014 syslog( LOG_ALERT, "error parsing entry from X.500" );
1015 unbind_and_exit( EX_TEMPFAIL );
1018 dn = ldap_get_dn( ld, e );
1020 resolved = entry_engine( e, dn, address, to, nto,
1024 add_error( err, nerr, E_NOEMAIL, address, res );
1025 /* Don't free res if we passed it to add_error */
1027 ldap_msgfree( res );
1050 for ( i = 0; urllist[i]; i++ ) {
1052 if ( !strncasecmp( urllist[i], "mail:", 5 ) ) {
1055 vals[0] = urllist[i] + 5;
1057 add_to( to, nto, vals );
1060 } else if ( ldap_is_ldap_url( urllist[i] ) ) {
1062 resolved = search_ldap_url( urllist[i], NULL,
1063 address, 0, multi_entry,
1064 to, nto, togroups, ngroups,
1067 /* Produce some sensible error here */
1075 * We should probably take MX records into account to cover all bases,
1076 * but really, routing belongs in the MTA.
1085 if ( myhosts == NULL )
1087 for ( d = myhosts; *d; d++ ) {
1088 if ( !strcasecmp(*d,host) ) {
1103 if ( mydomains == NULL )
1105 p = strchr( address, '@' );
1108 for ( d = mydomains; *d; d++ ) {
1109 if ( !strcasecmp(*d,p+1) ) {
1132 * Well, this is tricky, every address in my_addresses will be
1133 * removed from the list while we shift the other values down
1134 * and we do it in a single scan of the address list and
1135 * without using additional memory. We are going to be
1136 * modifying the value list in a way that the later
1137 * ldap_value_free works.
1140 for ( i = 0; addresses[i]; i++ ) {
1141 if ( is_my_domain(addresses[i]) ) {
1142 do_address( addresses[i], to, nto, togroups, ngroups,
1144 ldap_memfree( addresses[i] );
1147 addresses[j] = addresses[i];
1152 addresses[j] = NULL;
1153 if ( addresses[0] ) {
1154 add_to( to, nto, addresses );
1159 * The entry engine processes an entry. Normally, each entry will resolve
1160 * to one or more values that will be added to the 'to' argument. This
1161 * argument needs not be the global 'to' list, it may be the g_to field
1162 * in a group. Groups have no special treatment, unless they require
1183 char ***current_to = to;
1184 int *current_nto = nto;
1185 Group *current_group = NULL;
1187 char *localpart = NULL, *domainpart = NULL;
1189 int cur_priority = 0;
1190 char *route_to_host = NULL;
1191 char *route_to_address = NULL;
1192 int needs_mta_routing = 0;
1193 char **own_addresses = NULL;
1194 int own_addresses_total = 0;
1195 char **delivery_types = NULL;
1196 int delivery_types_total = 0;
1199 for ( i=0; attr_semantics[i] != NULL; i++ ) {
1200 AttrSemantics *as = attr_semantics[i];
1204 if ( as->as_priority < cur_priority ) {
1206 * We already got higher priority information,
1207 * so no further work to do, ignore the rest.
1211 vals = ldap_get_values( ld, e, as->as_name );
1212 if ( !vals || vals[0] == NULL ) {
1215 nent = count_values( vals );
1216 if ( nent > 1 && !as->as_m_valued ) {
1217 add_error( err, nerr, E_AMBIGUOUS, address, e );
1220 switch ( as->as_kind ) {
1221 case AS_KIND_RECIPIENT:
1222 cur_priority = as->as_priority;
1223 if ( ! ( type & ( USER | GROUP_MEMBERS ) ) )
1225 switch ( as->as_syntax ) {
1226 case AS_SYNTAX_RFC822:
1227 do_addresses( vals, current_to, current_nto,
1228 togroups, ngroups, err, nerr,
1232 case AS_SYNTAX_RFC822_EXT:
1233 do_addresses( vals, current_to, current_nto,
1234 togroups, ngroups, err, nerr,
1238 case AS_SYNTAX_NATIVE_MB:
1239 /* We used to concatenate mailHost if set here */
1241 * We used to send a copy to the vacation host
1242 * if onVacation to uid@vacationhost
1244 if ( as->as_param ) {
1245 for ( j=0; j<delivery_types_total; j++ ) {
1246 if ( !strcasecmp( as->as_param, delivery_types[j] ) ) {
1247 add_to( current_to, current_nto, vals );
1253 add_to( current_to, current_nto, vals );
1259 if ( dn_search( vals, address,
1260 current_to, current_nto,
1268 if ( url_list_search( vals, address,
1270 current_to, current_nto,
1272 err, nerr, type ) ) {
1277 case AS_SYNTAX_BOOL_FILTER:
1278 if ( strcasecmp( vals[0], "true" ) ) {
1281 substs[0].sub_char = 'D';
1282 substs[0].sub_value = dn;
1283 substs[1].sub_char = '\0';
1284 substs[1].sub_value = NULL;
1285 if ( url_list_search( vals, address,
1287 current_to, current_nto,
1289 err, nerr, type ) ) {
1296 "Invalid syntax %d for kind %d",
1297 as->as_syntax, as->as_kind );
1302 case AS_KIND_ERRORS:
1303 cur_priority = as->as_priority;
1304 /* This is a group with special processing */
1305 if ( type & GROUP_ERRORS ) {
1306 switch (as->as_kind) {
1307 case AS_SYNTAX_RFC822:
1308 add_to( current_to, current_nto, vals );
1314 "Invalid syntax %d for kind %d",
1315 as->as_syntax, as->as_kind );
1318 current_group = new_group( dn, togroups,
1320 if ( ! current_group )
1322 * We have already considered
1323 * this group, so we just
1327 current_to = ¤t_group->g_members;
1328 current_nto = ¤t_group->g_nmembers;
1329 split_address( address,
1330 &localpart, &domainpart );
1332 sprintf( buf, "%s-%s@%s",
1338 sprintf( buf, "%s-%s@%s",
1343 current_group->g_errorsto = strdup( buf );
1347 case AS_KIND_REQUEST:
1348 cur_priority = as->as_priority;
1349 /* This is a group with special processing */
1350 if ( type & GROUP_REQUEST ) {
1351 add_to( current_to, current_nto, vals );
1357 cur_priority = as->as_priority;
1358 /* This is a group with special processing */
1359 if ( type & GROUP_REQUEST ) {
1360 add_to( current_to, current_nto, vals );
1365 case AS_KIND_ROUTE_TO_HOST:
1366 if ( !is_my_host( vals[0] ) ) {
1367 cur_priority = as->as_priority;
1368 if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1369 needs_mta_routing = 1;
1371 route_to_host = strdup( vals[0] );
1376 case AS_KIND_ROUTE_TO_ADDR:
1377 for ( j=0; j<own_addresses_total; j++ ) {
1378 if ( strcasecmp( vals[0], own_addresses[j] ) ) {
1379 cur_priority = as->as_priority;
1380 if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
1381 needs_mta_routing = 1;
1383 route_to_address = strdup( vals[0] );
1389 case AS_KIND_OWN_ADDR:
1390 add_to( &own_addresses, &own_addresses_total, vals );
1391 cur_priority = as->as_priority;
1394 case AS_KIND_DELIVERY_TYPE:
1395 add_to( &delivery_types, &delivery_types_total, vals );
1396 cur_priority = as->as_priority;
1401 "Invalid kind %d", as->as_kind );
1404 ldap_value_free( vals );
1407 * Now check if we are dealing with mail routing. We support
1410 * The first mode and by far the most robust method is doing
1411 * routing at the MTA. In this case, we just checked if the
1412 * routing attributes were present and did not seem like
1413 * pointing to ourselves. The only thing we have to do here
1414 * is adding to the recipient list any of the RFC822 addresses
1415 * of this entry. That means we needed to retrieve them from
1416 * the entry itself because we might have arrived here through
1417 * some directory search. The address received as argument is
1418 * not the address of the entry we are processing, but rather
1419 * the RFC822 address we are expanding now. Unfortunately,
1420 * this requires an MTA that understands LDAP routing.
1421 * Sendmail 8.10.0 does, if compiled properly.
1423 * The second method, that is most emphatically not recommended
1424 * is routing in maildap. This is going to require using the
1425 * percent hack. Moreover, this may occasionally loop.
1427 if ( needs_mta_routing ) {
1428 if ( !own_addresses ) {
1429 add_error( err, nerr, E_NOOWNADDRESS, address, e );
1432 nvals[0] = own_addresses[0]; /* Anyone will do */
1434 add_to( current_to, current_nto, nvals );
1436 } else if ( route_to_host ) {
1438 if ( !route_to_address ) {
1439 if ( !own_addresses ) {
1440 add_error( err, nerr, E_NOOWNADDRESS, address, e );
1443 route_to_address = strdup( own_addresses[0] );
1445 /* This makes use of the percent hack, but there's no choice */
1446 p = strchr( route_to_address, '@' );
1450 sprintf( buf, "%s@%s", route_to_address, route_to_host );
1453 add_to( current_to, current_nto, nvals );
1455 free( route_to_host );
1456 free( route_to_address );
1457 } else if ( route_to_address ) {
1458 nvals[0] = route_to_address;
1460 add_to( current_to, current_nto, nvals );
1462 free( route_to_address );
1464 if ( own_addresses ) {
1465 ldap_value_free( own_addresses );
1467 if ( delivery_types ) {
1468 ldap_value_free( delivery_types );
1488 int b, resolved = 0;
1490 for ( b = 0; base[b] != NULL; b++ ) {
1492 if ( ! (base[b]->b_search & type) ) {
1496 resolved = search_ldap_url( base[b]->b_url, substs, name,
1498 base[b]->b_m_entries,
1499 to, nto, togroups, ngroups,
1519 char *localpart = NULL, *domainpart = NULL;
1520 char *synthname = NULL;
1526 * Look up the name in X.500, add the appropriate addresses found
1527 * to the to list, or to the err list in case of error. Groups are
1528 * handled by the do_group routine, individuals are handled here.
1529 * When looking up name, we follow the bases hierarchy, looking
1530 * in base[0] first, then base[1], etc. For each base, there is
1531 * a set of search filters to try, in order. If something goes
1532 * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
1533 * If the b_rdnpref flag is set, then we give preference to entries
1534 * that matched name because it's their rdn, otherwise not.
1537 split_address( name, &localpart, &domainpart );
1538 synthname = strdup( localpart );
1539 for ( i = 0; synthname[i] != '\0'; i++ ) {
1540 if ( synthname[i] == '.' || synthname[i] == '_' )
1543 substs[0].sub_char = 'm';
1544 substs[0].sub_value = name;
1545 substs[1].sub_char = 'h';
1546 substs[1].sub_value = host;
1547 substs[2].sub_char = 'l';
1548 substs[2].sub_value = localpart;
1549 substs[3].sub_char = 'd';
1550 substs[3].sub_value = domainpart;
1551 substs[4].sub_char = 's';
1552 substs[4].sub_value = synthname;
1553 substs[5].sub_char = '\0';
1554 substs[5].sub_value = NULL;
1556 resolved = search_bases( NULL, substs, name,
1557 to, nto, togroups, ngroups,
1571 /* not resolved - bounce with user unknown */
1572 if ( type == USER ) {
1573 add_error( err, nerr, E_USERUNKNOWN, name, NULL );
1575 add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
1581 send_message( char **to )
1584 #ifndef HAVE_WAITPID
1585 WAITSTATUSTYPE status;
1592 strcpy( buf, to[0] );
1593 for ( i = 1; to[i] != NULL; i++ ) {
1595 strcat( buf, to[i] );
1598 syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
1602 if ( (pid = fork()) != 0 ) {
1604 waitpid( pid, (int *) NULL, 0 );
1606 wait4( pid, &status, WAIT_FLAGS, 0 );
1610 /* to includes sendmailargs */
1611 execv( MAIL500_SENDMAIL, to );
1613 syslog( LOG_ALERT, "execv failed" );
1615 exit( EX_TEMPFAIL );
1620 send_group( Group **group, int ngroup )
1626 #ifndef HAVE_WAITPID
1627 WAITSTATUSTYPE status;
1630 for ( i = 0; i < ngroup; i++ ) {
1631 (void) rewind( stdin );
1633 iargv[0] = MAIL500_SENDMAIL;
1635 iargv[2] = group[i]->g_errorsto;
1636 iargv[3] = "-oMrX.500";
1643 add_to( &argv, &argc, iargv );
1644 add_to( &argv, &argc, group[i]->g_members );
1650 strcpy( buf, argv[0] );
1651 for ( i = 1; i < argc; i++ ) {
1653 strcat( buf, argv[i] );
1656 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1660 if ( (pid = fork()) != 0 ) {
1662 waitpid( pid, (int *) NULL, 0 );
1664 wait4( pid, &status, WAIT_FLAGS, 0 );
1668 execv( MAIL500_SENDMAIL, argv );
1670 syslog( LOG_ALERT, "execv failed" );
1672 exit( EX_TEMPFAIL );
1678 send_errors( Error *err, int nerr )
1680 int pid, i, namelen;
1685 #ifndef HAVE_WAITPID
1686 WAITSTATUSTYPE status;
1689 if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
1690 mailfrom = errorsfrom;
1693 argv[0] = MAIL500_SENDMAIL;
1694 argv[1] = "-oMrX.500";
1698 argv[5] = MAIL500_BOUNCEFROM;
1705 strcpy( buf, argv[0] );
1706 for ( i = 1; argv[i] != NULL; i++ ) {
1708 strcat( buf, argv[i] );
1711 syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
1714 if ( pipe( fd ) == -1 ) {
1715 syslog( LOG_ALERT, "cannot create pipe" );
1716 exit( EX_TEMPFAIL );
1719 if ( (pid = fork()) != 0 ) {
1720 if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
1721 syslog( LOG_ALERT, "cannot fdopen pipe" );
1722 exit( EX_TEMPFAIL );
1725 fprintf( fp, "To: %s\n", mailfrom );
1726 fprintf( fp, "From: %s\n", errorsfrom );
1727 fprintf( fp, "Subject: undeliverable mail\n" );
1728 fprintf( fp, "\n" );
1729 fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
1730 for ( i = 0; i < nerr; i++ ) {
1731 namelen = strlen( err[i].e_addr );
1732 fprintf( fp, "\n" );
1734 switch ( err[i].e_code ) {
1736 fprintf( fp, "%s: User unknown\n", err[i].e_addr );
1739 case E_GROUPUNKNOWN:
1740 fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
1744 fprintf( fp, "%s: Group member does not exist\n",
1746 fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
1747 fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
1748 fprintf( fp, "then re-adding the person to the group.\n" );
1752 fprintf( fp, "%s: Group exists but has no request address\n",
1757 fprintf( fp, "%s: Group exists but has no errors-to address\n",
1762 fprintf( fp, "%s: Group exists but has no owner\n",
1767 do_ambiguous( fp, &err[i], namelen );
1771 do_noemail( fp, &err[i], namelen );
1774 case E_MEMBERNOEMAIL:
1775 fprintf( fp, "%s: Group member exists but does not have an email address\n",
1779 case E_JOINMEMBERNOEMAIL:
1780 fprintf( fp, "%s: User has joined group but does not have an email address\n",
1785 fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
1786 err[i].e_addr, err[i].e_loop );
1790 fprintf( fp, "%s: Group has no members\n",
1794 case E_NOOWNADDRESS:
1795 fprintf( fp, "%s: Not enough information to perform required routing\n",
1800 syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
1801 unbind_and_exit( EX_TEMPFAIL );
1806 fprintf( fp, "\n------- The original message sent:\n\n" );
1808 while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
1814 waitpid( pid, (int *) NULL, 0 );
1816 wait4( pid, &status, WAIT_FLAGS, 0 );
1821 execv( MAIL500_SENDMAIL, argv );
1823 syslog( LOG_ALERT, "execv failed" );
1825 exit( EX_TEMPFAIL );
1830 do_noemail( FILE *fp, Error *err, int namelen )
1836 fprintf(fp, "%s: User has no email address registered.\n",
1838 fprintf( fp, "%*s Name, title, postal address and phone for '%s':\n\n",
1839 namelen, " ", err->e_addr );
1842 dn = ldap_get_dn( ld, err->e_msg );
1843 ufn = ldap_explode_dn( dn, 1 );
1844 rdn = strdup( ufn[0] );
1845 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1846 if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
1848 for ( i = 0; vals[i]; i++ ) {
1849 last = strlen( vals[i] ) - 1;
1850 if ( isdigit((unsigned char) vals[i][last]) ) {
1851 rdn = strdup( vals[i] );
1856 ldap_value_free( vals );
1859 fprintf( fp, "%*s %s\n", namelen, " ", rdn );
1862 ldap_value_free( ufn );
1864 /* titles or descriptions */
1865 if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
1866 (vals = ldap_get_values( ld, err->e_msg, "description" ))
1868 fprintf( fp, "%*s No title or description registered\n",
1871 for ( i = 0; vals[i] != NULL; i++ ) {
1872 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1875 ldap_value_free( vals );
1878 /* postal address */
1879 if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
1881 fprintf( fp, "%*s No postal address registered\n", namelen,
1884 fprintf( fp, "%*s ", namelen, " " );
1885 for ( i = 0; vals[0][i] != '\0'; i++ ) {
1886 if ( vals[0][i] == '$' ) {
1887 fprintf( fp, "\n%*s ", namelen, " " );
1888 while ( isspace((unsigned char) vals[0][i+1]) )
1891 fprintf( fp, "%c", vals[0][i] );
1894 fprintf( fp, "\n" );
1896 ldap_value_free( vals );
1899 /* telephone number */
1900 if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
1902 fprintf( fp, "%*s No phone number registered\n", namelen,
1905 for ( i = 0; vals[i] != NULL; i++ ) {
1906 fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
1909 ldap_value_free( vals );
1915 do_ambiguous( FILE *fp, Error *err, int namelen )
1922 i = ldap_result2error( ld, err->e_msg, 0 );
1924 fprintf( fp, "%s: Ambiguous user. %s%d matches found:\n\n",
1925 err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
1926 ldap_count_entries( ld, err->e_msg ) );
1928 for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
1929 e = ldap_next_entry( ld, e ) ) {
1930 dn = ldap_get_dn( ld, e );
1931 ufn = ldap_explode_dn( dn, 1 );
1932 rdn = strdup( ufn[0] );
1933 if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
1934 if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
1935 for ( i = 0; vals[i]; i++ ) {
1936 last = strlen( vals[i] ) - 1;
1937 if (isdigit((unsigned char) vals[i][last])) {
1938 rdn = strdup( vals[i] );
1943 ldap_value_free( vals );
1948 if ( isgroup( e ) ) {
1949 vals = ldap_get_values( ld, e, "description" );
1951 vals = ldap_get_values( ld, e, "title" );
1954 vals = ldap_get_values( ld, e, "description" );
1956 fprintf( fp, " %-20s %s\n", rdn, vals ? vals[0] : "" );
1957 for ( i = 1; vals && vals[i] != NULL; i++ ) {
1958 fprintf( fp, " %s\n", vals[i] );
1963 ldap_value_free( ufn );
1965 ldap_value_free( vals );
1970 count_values( char **list )
1974 for ( i = 0; list && list[i] != NULL; i++ )
1981 add_to( char ***list, int *nlist, char **new )
1983 int i, nnew, oldnlist;
1985 nnew = count_values( new );
1988 if ( *list == NULL || *nlist == 0 ) {
1989 *list = (char **) malloc( (nnew + 1) * sizeof(char *) );
1992 *list = (char **) realloc( *list, *nlist * sizeof(char *) +
1993 nnew * sizeof(char *) + sizeof(char *) );
1997 for ( i = 0; i < nnew; i++ )
1998 (*list)[i + oldnlist] = strdup( new[i] );
1999 (*list)[*nlist] = NULL;
2003 add_single_to( char ***list, char *new )
2007 if ( *list == NULL ) {
2009 *list = (char **) malloc( 2 * sizeof(char *) );
2011 nlist = count_values( *list );
2012 *list = (char **) realloc( *list,
2013 ( nlist + 2 ) * sizeof(char *) );
2016 (*list)[nlist] = strdup( new );
2017 (*list)[nlist+1] = NULL;
2021 isgroup( LDAPMessage *e )
2026 if ( !groupclasses ) {
2030 oclist = ldap_get_values( ld, e, "objectClass" );
2032 for ( i = 0; oclist[i] != NULL; i++ ) {
2033 for ( j = 0; groupclasses[j] != NULL; j++ ) {
2034 if ( strcasecmp( oclist[i], groupclasses[j] ) == 0 ) {
2035 ldap_value_free( oclist );
2040 ldap_value_free( oclist );
2046 add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
2049 *err = (Error *) malloc( sizeof(Error) );
2051 *err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
2054 (*err)[*nerr].e_code = code;
2055 (*err)[*nerr].e_addr = strdup( addr );
2056 (*err)[*nerr].e_msg = msg;
2061 unbind_and_exit( int rc )
2065 if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
2066 syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );