mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-24 00:29:35 -05:00
This could cause problems on odd systems. The generic headers should be extended as needed to include necessary system headers or, if necessary, make explicit declarations. Extended ac/string.h header to look for string.h/strings.h if STDC_HEADERS is not defined. Also provide basic declarations for str*() functions. This could cause problems on odd systems. Extended ac/unistd.h header to define basic declaration for misc functions that might be missing from headers. This includes externs for getenv(), getopt(), mktemp(), tempname(). Protect fax500.h from multiple inclusion. Moved includes of system/generic headers back to source files. Made mail500 helper functions static. Fixed includes of ctype.h, signal.h, etc. to use generics. lutil/tempname.c: was including stdlib.h twice, one should stdio.h. Wrapped <sys/resource.h> with HAVE_SYS_RESOURCE_H. lber/io.c/ber_get_next(): Changed noctets back to signed. Used with BerRead which expects signed int as second arg and returns signed int.
1680 lines
38 KiB
C
1680 lines
38 KiB
C
/*
|
|
* Copyright (c) 1990 Regents of the University of Michigan.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this notice is preserved and that due credit is given
|
|
* to the University of Michigan at Ann Arbor. The name of the University
|
|
* may not be used to endorse or promote products derived from this
|
|
* software without specific prior written permission. This software
|
|
* is provided ``as is'' without express or implied warranty.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <ac/ctype.h>
|
|
#include <ac/string.h>
|
|
#include <ac/syslog.h>
|
|
#include <ac/time.h>
|
|
#include <ac/wait.h>
|
|
#include <ac/unistd.h>
|
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_RESOURCE_H
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include <sysexits.h>
|
|
|
|
#include "lber.h"
|
|
#include "ldap.h"
|
|
|
|
#include "ldapconfig.h"
|
|
|
|
#ifndef MAIL500_BOUNCEFROM
|
|
#define MAIL500_BOUNCEFROM "<>"
|
|
#endif
|
|
|
|
#define USER 0x01
|
|
#define GROUP_ERRORS 0x02
|
|
#define GROUP_REQUEST 0x04
|
|
#define GROUP_MEMBERS 0x08
|
|
#define GROUP_OWNER 0x10
|
|
|
|
#define ERROR "error"
|
|
#define ERRORS "errors"
|
|
#define REQUEST "request"
|
|
#define REQUESTS "requests"
|
|
#define MEMBERS "members"
|
|
#define OWNER "owner"
|
|
#define OWNERS "owners"
|
|
|
|
LDAP *ld;
|
|
char *vacationhost = NULL;
|
|
char *errorsfrom = NULL;
|
|
char *mailfrom = NULL;
|
|
char *host = NULL;
|
|
char *ldaphost = NULL;
|
|
int hostlen = 0;
|
|
int debug;
|
|
|
|
typedef struct errs {
|
|
int e_code;
|
|
#define E_USERUNKNOWN 1
|
|
#define E_AMBIGUOUS 2
|
|
#define E_NOEMAIL 3
|
|
#define E_NOREQUEST 4
|
|
#define E_NOERRORS 5
|
|
#define E_BADMEMBER 6
|
|
#define E_JOINMEMBERNOEMAIL 7
|
|
#define E_MEMBERNOEMAIL 8
|
|
#define E_LOOP 9
|
|
#define E_NOMEMBERS 10
|
|
#define E_NOOWNER 11
|
|
#define E_GROUPUNKNOWN 12
|
|
char *e_addr;
|
|
union {
|
|
char *e_u_loop;
|
|
LDAPMessage *e_u_msg;
|
|
} e_union;
|
|
#define e_msg e_union.e_u_msg
|
|
#define e_loop e_union.e_u_loop
|
|
} Error;
|
|
|
|
typedef struct groupto {
|
|
char *g_dn;
|
|
char *g_errorsto;
|
|
char **g_members;
|
|
} Group;
|
|
|
|
typedef struct baseinfo {
|
|
char *b_dn; /* dn to start searching at */
|
|
char b_rdnpref; /* give rdn's preference when searching? */
|
|
int b_search; /* ORed with the type of thing the address */
|
|
/* looks like (USER, GROUP_ERRORS, etc.) */
|
|
/* to see if this should be searched */
|
|
char *b_filter[3]; /* filter to apply - name substituted for %s */
|
|
/* (up to three of them) */
|
|
} Base;
|
|
|
|
Base base[] =
|
|
{ "ou=People, o=University of Michigan, c=US",
|
|
0, USER,
|
|
"uid=%s", "cn=%s", NULL,
|
|
"ou=System Groups, ou=Groups, o=University of Michigan, c=US",
|
|
1, 0xff,
|
|
"(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
|
|
"ou=User Groups, ou=Groups, o=University of Michigan, c=US",
|
|
1, 0xff,
|
|
"(&(cn=%s)(associatedDomain=%h))", NULL, NULL,
|
|
NULL
|
|
};
|
|
|
|
char *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrX.500", "-odi", "-oi", "-f", NULL, NULL };
|
|
|
|
static char *attrs[] = { "objectClass", "title", "postaladdress",
|
|
"telephoneNumber", "mail", "description", "owner",
|
|
"errorsTo", "rfc822ErrorsTo", "requestsTo",
|
|
"rfc822RequestsTo", "joinable", "cn", "member",
|
|
"moderator", "onVacation", "uid",
|
|
"suppressNoEmailError", NULL };
|
|
|
|
static void do_address( char *name, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, int type );
|
|
static int do_group( LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr );
|
|
static void do_group_members( LDAPMessage *e, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr );
|
|
static void send_message( char **to );
|
|
static void send_errors( Error *err, int nerr );
|
|
static void do_noemail( FILE *fp, Error *err, int namelen );
|
|
static void do_ambiguous( FILE *fp, Error *err, int namelen );
|
|
static void add_to( char ***list, int *nlist, char **new );
|
|
static int isgroup( LDAPMessage *e );
|
|
static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
|
|
static void add_group( char *dn, Group **list, int *nlist );
|
|
static void unbind_and_exit( int rc );
|
|
static int group_loop( char *dn );
|
|
static void send_group( Group *group, int ngroup );
|
|
static int has_attributes( LDAPMessage *e, char *attr1, char *attr2 );
|
|
static char **get_attributes_mail_dn( LDAPMessage *e, char *attr1, char *attr2 );
|
|
static char *canonical( char *s );
|
|
static int connect_to_x500( void );
|
|
|
|
static void do_group_errors( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
|
|
static void do_group_request( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
|
|
static void do_group_owner( LDAPMessage *e, char *dn, char ***to, int *nto, Error **err, int *nerr );
|
|
static void add_member( char *gdn, char *dn, char ***to, int *nto, Group **togroups, int *ngroups, Error **err, int *nerr, char **suppress );
|
|
|
|
int
|
|
main ( int argc, char **argv )
|
|
{
|
|
char *myname;
|
|
char **tolist;
|
|
Error *errlist;
|
|
Group *togroups;
|
|
int numto, ngroups, numerr, nargs;
|
|
int i, j;
|
|
|
|
if ( (myname = strrchr( argv[0], '/' )) == NULL )
|
|
myname = strdup( argv[0] );
|
|
else
|
|
myname = strdup( myname + 1 );
|
|
|
|
#ifdef LOG_MAIL
|
|
openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
|
|
#else
|
|
openlog( myname, OPENLOG_OPTIONS );
|
|
#endif
|
|
|
|
while ( (i = getopt( argc, argv, "d:f:h:l:m:v:" )) != EOF ) {
|
|
switch( i ) {
|
|
case 'd': /* turn on debugging */
|
|
debug = atoi( optarg );
|
|
break;
|
|
|
|
case 'f': /* who it's from & where errors should go */
|
|
mailfrom = strdup( optarg );
|
|
for ( j = 0; sendmailargs[j] != NULL; j++ ) {
|
|
if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
|
|
sendmailargs[j+1] = mailfrom;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'h': /* hostname */
|
|
host = strdup( optarg );
|
|
hostlen = strlen(host);
|
|
break;
|
|
|
|
case 'l': /* ldap host */
|
|
ldaphost = strdup( optarg );
|
|
break;
|
|
|
|
/* mailer-daemon address - who we should */
|
|
case 'm': /* say errors come from */
|
|
errorsfrom = strdup( optarg );
|
|
break;
|
|
|
|
case 'v': /* vacation host */
|
|
vacationhost = strdup( optarg );
|
|
break;
|
|
|
|
default:
|
|
syslog( LOG_ALERT, "unknown option" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( mailfrom == NULL ) {
|
|
syslog( LOG_ALERT, "required argument -f not present" );
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
if ( errorsfrom == NULL ) {
|
|
syslog( LOG_ALERT, "required argument -m not present" );
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
if ( host == NULL ) {
|
|
syslog( LOG_ALERT, "required argument -h not present" );
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
if ( connect_to_x500() != 0 )
|
|
exit( EX_TEMPFAIL );
|
|
|
|
setuid( geteuid() );
|
|
|
|
if ( debug ) {
|
|
char buf[1024];
|
|
int i;
|
|
|
|
syslog( LOG_ALERT, "running as %d", geteuid() );
|
|
strcpy( buf, argv[0] );
|
|
for ( i = 1; i < argc; i++ ) {
|
|
strcat( buf, " " );
|
|
strcat( buf, argv[i] );
|
|
}
|
|
|
|
syslog( LOG_ALERT, "args: (%s)", buf );
|
|
}
|
|
|
|
tolist = NULL;
|
|
numto = 0;
|
|
add_to( &tolist, &numto, sendmailargs );
|
|
nargs = numto;
|
|
ngroups = numerr = 0;
|
|
togroups = NULL;
|
|
errlist = NULL;
|
|
for ( i = optind; i < argc; i++ ) {
|
|
char *s;
|
|
int type;
|
|
|
|
for ( j = 0; argv[i][j] != '\0'; j++ ) {
|
|
if ( argv[i][j] == '.' || argv[i][j] == '_' )
|
|
argv[i][j] = ' ';
|
|
}
|
|
|
|
type = USER;
|
|
if ( (s = strrchr( argv[i], '-' )) != NULL ) {
|
|
s++;
|
|
|
|
if ((strcasecmp(s, ERROR) == 0) ||
|
|
(strcasecmp(s, ERRORS) == 0)) {
|
|
type = GROUP_ERRORS;
|
|
*(--s) = '\0';
|
|
} else if ((strcasecmp(s, REQUEST) == 0) ||
|
|
(strcasecmp(s, REQUESTS) == 0)) {
|
|
type = GROUP_REQUEST;
|
|
*(--s) = '\0';
|
|
} else if ( strcasecmp( s, MEMBERS ) == 0 ) {
|
|
type = GROUP_MEMBERS;
|
|
*(--s) = '\0';
|
|
} else if ((strcasecmp(s, OWNER) == 0) ||
|
|
(strcasecmp(s, OWNERS) == 0)) {
|
|
type = GROUP_OWNER;
|
|
*(--s) = '\0';
|
|
}
|
|
}
|
|
|
|
do_address( argv[i], &tolist, &numto, &togroups, &ngroups,
|
|
&errlist, &numerr, type );
|
|
}
|
|
|
|
/*
|
|
* If we have both errors and successful deliveries to make or if
|
|
* if there are any groups to deliver to, we basically need to read
|
|
* the message twice. So, we have to put it in a tmp file.
|
|
*/
|
|
|
|
if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
|
|
FILE *fp;
|
|
char buf[BUFSIZ];
|
|
|
|
umask( 077 );
|
|
if ( (fp = tmpfile()) == NULL ) {
|
|
syslog( LOG_ALERT, "could not open tmp file" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
/* copy the message to a temp file */
|
|
while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
|
|
if ( fputs( buf, fp ) == EOF ) {
|
|
syslog( LOG_ALERT, "error writing tmpfile" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
}
|
|
|
|
if ( dup2( fileno( fp ), 0 ) == -1 ) {
|
|
syslog( LOG_ALERT, "could not dup2 tmpfile" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
fclose( fp );
|
|
}
|
|
|
|
/* deal with errors */
|
|
if ( numerr > 0 ) {
|
|
if ( debug ) {
|
|
syslog( LOG_ALERT, "sending errors" );
|
|
}
|
|
(void) rewind( stdin );
|
|
send_errors( errlist, numerr );
|
|
}
|
|
|
|
(void) ldap_unbind( ld );
|
|
|
|
/* send to groups with errorsTo */
|
|
if ( ngroups > 0 ) {
|
|
if ( debug ) {
|
|
syslog( LOG_ALERT, "sending to groups with errorsto" );
|
|
}
|
|
(void) rewind( stdin );
|
|
send_group( togroups, ngroups );
|
|
}
|
|
|
|
/* send to expanded aliases and groups w/o errorsTo */
|
|
if ( numto > nargs ) {
|
|
if ( debug ) {
|
|
syslog( LOG_ALERT, "sending to aliases and groups" );
|
|
}
|
|
(void) rewind( stdin );
|
|
send_message( tolist );
|
|
}
|
|
|
|
return( EX_OK );
|
|
}
|
|
|
|
static int
|
|
connect_to_x500( void )
|
|
{
|
|
int opt;
|
|
|
|
if ( (ld = ldap_open( ldaphost, LDAP_PORT )) == NULL ) {
|
|
syslog( LOG_ALERT, "ldap_open failed" );
|
|
return( -1 );
|
|
}
|
|
|
|
opt = MAIL500_MAXAMBIGUOUS;
|
|
ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
|
|
opt = LDAP_DEREF_ALWAYS;
|
|
ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
|
|
|
|
if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
|
|
syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
mailcmp( char *a, char *b )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; a[i] != '\0'; i++ ) {
|
|
if ( a[i] != b[i] ) {
|
|
switch ( a[i] ) {
|
|
case ' ':
|
|
case '.':
|
|
case '_':
|
|
if ( b[i] == ' ' || b[i] == '.' || b[i] == '_' )
|
|
break;
|
|
return( 1 );
|
|
|
|
default:
|
|
return( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static void
|
|
do_address(
|
|
char *name,
|
|
char ***to,
|
|
int *nto,
|
|
Group **togroups,
|
|
int *ngroups,
|
|
Error **err,
|
|
int *nerr,
|
|
int type
|
|
)
|
|
{
|
|
int rc, b, f, match;
|
|
LDAPMessage *e, *res;
|
|
struct timeval timeout;
|
|
char *dn;
|
|
char filter[1024];
|
|
char realfilter[1024];
|
|
char **mail, **onvacation = NULL, **uid = NULL;
|
|
|
|
/*
|
|
* Look up the name in X.500, add the appropriate addresses found
|
|
* to the to list, or to the err list in case of error. Groups are
|
|
* handled by the do_group routine, individuals are handled here.
|
|
* When looking up name, we follow the bases hierarchy, looking
|
|
* in base[0] first, then base[1], etc. For each base, there is
|
|
* a set of search filters to try, in order. If something goes
|
|
* wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
|
|
* If the b_rdnpref flag is set, then we give preference to entries
|
|
* that matched name because it's their rdn, otherwise not.
|
|
*/
|
|
|
|
timeout.tv_sec = MAIL500_TIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
for ( b = 0, match = 0; !match && base[b].b_dn != NULL; b++ ) {
|
|
if ( ! (base[b].b_search & type) ) {
|
|
continue;
|
|
}
|
|
for ( f = 0; base[b].b_filter[f] != NULL; f++ ) {
|
|
char *format, *p, *s, *d;
|
|
char *argv[3];
|
|
int argc;
|
|
|
|
for ( argc = 0; argc < 3; argc++ ) {
|
|
argv[argc] = NULL;
|
|
}
|
|
|
|
format = strdup( base[b].b_filter[f] );
|
|
for ( argc = 0, p = format; *p; p++ ) {
|
|
if ( *p == '%' ) {
|
|
switch ( *++p ) {
|
|
case 's': /* %s is the name */
|
|
argv[argc] = name;
|
|
break;
|
|
|
|
case 'h': /* %h is the host */
|
|
*p = 's';
|
|
argv[argc] = host;
|
|
break;
|
|
|
|
default:
|
|
syslog( LOG_ALERT,
|
|
"unknown format %c", *p );
|
|
break;
|
|
}
|
|
|
|
argc++;
|
|
}
|
|
}
|
|
|
|
/* three names ought to do... */
|
|
sprintf( filter, format, argv[0], argv[1], argv[2] );
|
|
free( format );
|
|
for ( s = filter, d = realfilter; *s; s++, d++ ) {
|
|
if ( *s == '*' ) {
|
|
*d++ = '\\';
|
|
}
|
|
*d = *s;
|
|
}
|
|
*d = '\0';
|
|
|
|
res = NULL;
|
|
rc = ldap_search_st( ld, base[b].b_dn,
|
|
LDAP_SCOPE_SUBTREE, realfilter, attrs, 0, &timeout,
|
|
&res );
|
|
|
|
/* some other trouble - try again later */
|
|
if ( rc != LDAP_SUCCESS &&
|
|
rc != LDAP_SIZELIMIT_EXCEEDED ) {
|
|
syslog( LOG_ALERT, "return 0x%x from X.500",
|
|
rc );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
if ( (match = ldap_count_entries( ld, res )) != 0 )
|
|
break;
|
|
|
|
ldap_msgfree( res );
|
|
}
|
|
|
|
if ( match )
|
|
break;
|
|
}
|
|
|
|
/* trouble - try again later */
|
|
if ( match == -1 ) {
|
|
syslog( LOG_ALERT, "error parsing result from X.500" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
/* no matches - bounce with user unknown */
|
|
if ( match == 0 ) {
|
|
if ( type == USER ) {
|
|
add_error( err, nerr, E_USERUNKNOWN, name, NULLMSG );
|
|
} else {
|
|
add_error( err, nerr, E_GROUPUNKNOWN, name, NULLMSG );
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* more than one match - bounce with ambiguous user? */
|
|
if ( match > 1 ) {
|
|
LDAPMessage *next, *tmpres = NULL;
|
|
char *dn;
|
|
char **xdn;
|
|
|
|
/* not giving rdn preference - bounce with ambiguous user */
|
|
if ( base[b].b_rdnpref == 0 ) {
|
|
add_error( err, nerr, E_AMBIGUOUS, name, res );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* giving rdn preference - see if any entries were matched
|
|
* because of their rdn. If so, collect them to deal with
|
|
* later (== 1 we deliver, > 1 we bounce).
|
|
*/
|
|
|
|
for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
|
|
next = ldap_next_entry( ld, e );
|
|
dn = ldap_get_dn( ld, e );
|
|
xdn = ldap_explode_dn( dn, 1 );
|
|
|
|
/* XXX bad, but how else can we do it? XXX */
|
|
if ( strcasecmp( xdn[0], name ) == 0 ) {
|
|
ldap_delete_result_entry( &res, e );
|
|
ldap_add_result_entry( &tmpres, e );
|
|
}
|
|
|
|
ldap_value_free( xdn );
|
|
free( dn );
|
|
}
|
|
|
|
/* nothing matched by rdn - go ahead and bounce */
|
|
if ( tmpres == NULL ) {
|
|
add_error( err, nerr, E_AMBIGUOUS, name, res );
|
|
return;
|
|
|
|
/* more than one matched by rdn - bounce with rdn matches */
|
|
} else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
|
|
add_error( err, nerr, E_AMBIGUOUS, name, tmpres );
|
|
return;
|
|
|
|
/* trouble... */
|
|
} else if ( match < 0 ) {
|
|
syslog( LOG_ALERT, "error parsing result from X.500" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
/* otherwise one matched by rdn - send to it */
|
|
ldap_msgfree( res );
|
|
res = tmpres;
|
|
}
|
|
|
|
/*
|
|
* if we get this far, it means that we found a single match for
|
|
* name. for a user, we deliver to the mail attribute or bounce
|
|
* with address and phone if no mail attr. for a group, we
|
|
* deliver to all members or bounce to rfc822ErrorsTo if no members.
|
|
*/
|
|
|
|
/* trouble */
|
|
if ( (e = ldap_first_entry( ld, res )) == NULL ) {
|
|
syslog( LOG_ALERT, "error parsing entry from X.500" );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
dn = ldap_get_dn( ld, e );
|
|
|
|
if ( type == GROUP_ERRORS ) {
|
|
/* sent to group-errors - resend to [rfc822]ErrorsTo attr */
|
|
do_group_errors( e, dn, to, nto, err, nerr );
|
|
|
|
} else if ( type == GROUP_REQUEST ) {
|
|
/* sent to group-request - resend to [rfc822]RequestsTo attr */
|
|
do_group_request( e, dn, to, nto, err, nerr );
|
|
|
|
} else if ( type == GROUP_MEMBERS ) {
|
|
/* sent to group-members - expand */
|
|
do_group_members( e, dn, to, nto, togroups, ngroups, err,
|
|
nerr );
|
|
|
|
} else if ( type == GROUP_OWNER ) {
|
|
/* sent to group-owner - resend to owner attr */
|
|
do_group_owner( e, dn, to, nto, err, nerr );
|
|
|
|
} else if ( isgroup( e ) ) {
|
|
/*
|
|
* sent to group - resend from [rfc822]ErrorsTo if it's there,
|
|
* otherwise, expand the group
|
|
*/
|
|
|
|
do_group( e, dn, to, nto, togroups, ngroups, err, nerr );
|
|
|
|
ldap_msgfree( res );
|
|
|
|
} else {
|
|
/*
|
|
* sent to user - mail attribute => add it to the to list,
|
|
* otherwise bounce
|
|
*/
|
|
if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
|
|
char buf[1024];
|
|
char *h;
|
|
int i, j;
|
|
|
|
/* try to detect simple mail loops */
|
|
sprintf( buf, "%s@%s", name, host );
|
|
for ( i = 0; mail[i] != NULL; i++ ) {
|
|
/*
|
|
* address is the same as the one we're
|
|
* sending to - mail loop. syslog the
|
|
* problem, bounce a message back to the
|
|
* sender (who else?), and delete the bogus
|
|
* addr from the list.
|
|
*/
|
|
|
|
if ( (h = strchr( mail[i], '@' )) != NULL ) {
|
|
h++;
|
|
if ( strcasecmp( h, host ) == 0 ) {
|
|
syslog( LOG_ALERT,
|
|
"potential loop detected (%s)",
|
|
mail[i] );
|
|
}
|
|
}
|
|
|
|
if ( mailcmp( buf, mail[i] ) == 0 ) {
|
|
syslog( LOG_ALERT,
|
|
"loop detected (%s)", mail[i] );
|
|
|
|
/* remove the bogus address */
|
|
for ( j = i; mail[j] != NULL; j++ ) {
|
|
mail[j] = mail[j+1];
|
|
}
|
|
}
|
|
}
|
|
if ( mail[0] != NULL ) {
|
|
add_to( to, nto, mail );
|
|
} else {
|
|
add_error( err, nerr, E_NOEMAIL, name, res );
|
|
}
|
|
|
|
ldap_value_free( mail );
|
|
} else {
|
|
add_error( err, nerr, E_NOEMAIL, name, res );
|
|
}
|
|
|
|
/*
|
|
* If the user is on vacation, send a copy of the mail to
|
|
* the vacation server. The address is constructed from
|
|
* the vacationhost (set in a command line argument) and
|
|
* the uid (XXX this should be more general XXX).
|
|
*/
|
|
|
|
if ( vacationhost != NULL && (onvacation = ldap_get_values( ld,
|
|
e, "onVacation" )) != NULL && strcasecmp( onvacation[0],
|
|
"TRUE" ) == 0 ) {
|
|
char buf[1024];
|
|
char *vaddr[2];
|
|
|
|
if ( (uid = ldap_get_values( ld, e, "uid" )) != NULL ) {
|
|
sprintf( buf, "%s@%s", uid[0], vacationhost );
|
|
|
|
vaddr[0] = buf;
|
|
vaddr[1] = NULL;
|
|
|
|
add_to( to, nto, vaddr );
|
|
} else {
|
|
syslog( LOG_ALERT,
|
|
"user without a uid on vacation (%s)",
|
|
name );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( onvacation != NULL ) {
|
|
ldap_value_free( onvacation );
|
|
}
|
|
if ( uid != NULL ) {
|
|
ldap_value_free( uid );
|
|
}
|
|
free( dn );
|
|
}
|
|
|
|
static int
|
|
do_group(
|
|
LDAPMessage *e,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Group **togroups,
|
|
int *ngroups,
|
|
Error **err,
|
|
int *nerr
|
|
)
|
|
{
|
|
int i;
|
|
char **moderator;
|
|
|
|
/*
|
|
* If this group has an rfc822ErrorsTo attribute, we need to
|
|
* arrange for errors involving this group to go there, not
|
|
* to the sender. Since sendmail only has the concept of a
|
|
* single sender, we arrange for errors to go to groupname-errors,
|
|
* which we then handle specially when (if) it comes back to us
|
|
* by expanding to all the rfc822ErrorsTo addresses. If it has no
|
|
* rfc822ErrorsTo attribute, we call do_group_members() to expand
|
|
* the group.
|
|
*/
|
|
|
|
if ( group_loop( dn ) ) {
|
|
return( -1 );
|
|
}
|
|
|
|
/*
|
|
* check for moderated groups - if the group has a moderator
|
|
* attribute, we check to see if the from address is one of
|
|
* the moderator values. if so, continue on. if not, arrange
|
|
* to send the mail to the moderator(s). need to do this before
|
|
* we change the from below.
|
|
*/
|
|
|
|
if ( (moderator = ldap_get_values( ld, e, "moderator" )) != NULL ) {
|
|
/* check if it came from any of the group's moderators */
|
|
for ( i = 0; moderator[i] != NULL; i++ ) {
|
|
if ( strcasecmp( moderator[i], mailfrom ) == 0 )
|
|
break;
|
|
}
|
|
|
|
/* not from the moderator? */
|
|
if ( moderator[i] == NULL ) {
|
|
add_to( to, nto, moderator );
|
|
ldap_value_free( moderator );
|
|
|
|
return( 0 );
|
|
}
|
|
/* else from the moderator - fall through and deliver it */
|
|
}
|
|
|
|
if (strcmp(MAIL500_BOUNCEFROM, mailfrom) != 0 &&
|
|
has_attributes( e, "rfc822ErrorsTo", "errorsTo" ) ) {
|
|
add_group( dn, togroups, ngroups );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
do_group_members( e, dn, to, nto, togroups, ngroups, err, nerr );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
do_group_members(
|
|
LDAPMessage *e,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Group **togroups,
|
|
int *ngroups,
|
|
Error **err,
|
|
int *nerr
|
|
)
|
|
{
|
|
int i, rc, anymembers;
|
|
char *ndn;
|
|
char **mail, **member, **joinable, **suppress;
|
|
char filter[1024];
|
|
LDAPMessage *ee, *res;
|
|
struct timeval timeout;
|
|
int opt;
|
|
|
|
/*
|
|
* if all has gone according to plan, we've already arranged for
|
|
* errors to go to the [rfc822]ErrorsTo attributes (if they exist),
|
|
* so all we have to do here is arrange to send to the
|
|
* rfc822Mailbox attribute, the member attribute, and anyone who
|
|
* has joined the group by setting memberOfGroup equal to the
|
|
* group dn.
|
|
*/
|
|
|
|
/* add members in the group itself - mail attribute */
|
|
anymembers = 0;
|
|
if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
|
|
anymembers = 1;
|
|
add_to( to, nto, mail );
|
|
|
|
ldap_value_free( mail );
|
|
}
|
|
|
|
/* add members in the group itself - member attribute */
|
|
if ( (member = ldap_get_values( ld, e, "member" )) != NULL ) {
|
|
suppress = ldap_get_values( ld, e, "suppressNoEmailError" );
|
|
anymembers = 1;
|
|
for ( i = 0; member[i] != NULL; i++ ) {
|
|
if ( strcasecmp( dn, member[i] ) == 0 ) {
|
|
syslog( LOG_ALERT, "group (%s) contains itself",
|
|
dn );
|
|
continue;
|
|
}
|
|
add_member( dn, member[i], to, nto, togroups,
|
|
ngroups, err, nerr, suppress );
|
|
}
|
|
|
|
if ( suppress ) {
|
|
ldap_value_free( suppress );
|
|
}
|
|
ldap_value_free( member );
|
|
}
|
|
|
|
/* add members who have joined by setting memberOfGroup */
|
|
if ( (joinable = ldap_get_values( ld, e, "joinable" )) != NULL ) {
|
|
if ( strcasecmp( joinable[0], "FALSE" ) == 0 ) {
|
|
if ( ! anymembers ) {
|
|
add_error( err, nerr, E_NOMEMBERS, dn,
|
|
NULLMSG );
|
|
}
|
|
|
|
ldap_value_free( joinable );
|
|
return;
|
|
}
|
|
ldap_value_free( joinable );
|
|
|
|
sprintf( filter, "(memberOfGroup=%s)", dn );
|
|
|
|
timeout.tv_sec = MAIL500_TIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
|
|
/* for each subtree to look in... */
|
|
opt = MAIL500_MAXAMBIGUOUS;
|
|
ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
|
|
for ( i = 0; base[i].b_dn != NULL; i++ ) {
|
|
/* find entries that have joined this group... */
|
|
rc = ldap_search_st( ld, base[i].b_dn,
|
|
LDAP_SCOPE_SUBTREE, filter, attrs, 0, &timeout,
|
|
&res );
|
|
|
|
if ( rc == LDAP_SIZELIMIT_EXCEEDED ||
|
|
rc == LDAP_TIMELIMIT_EXCEEDED ) {
|
|
syslog( LOG_ALERT,
|
|
"group search limit exceeded %d", rc );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
syslog( LOG_ALERT, "group search return 0x%x",
|
|
rc );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
/* for each entry that has joined... */
|
|
for ( ee = ldap_first_entry( ld, res ); ee != NULL;
|
|
ee = ldap_next_entry( ld, ee ) ) {
|
|
anymembers = 1;
|
|
if ( isgroup( ee ) ) {
|
|
ndn = ldap_get_dn( ld, ee );
|
|
|
|
if ( do_group( e, ndn, to, nto,
|
|
togroups, ngroups, err, nerr )
|
|
== -1 ) {
|
|
syslog( LOG_ALERT,
|
|
"group loop (%s) (%s)",
|
|
dn, ndn );
|
|
}
|
|
|
|
free( ndn );
|
|
|
|
continue;
|
|
}
|
|
|
|
/* add them to the to list */
|
|
if ( (mail = ldap_get_values( ld, ee, "mail" ))
|
|
!= NULL ) {
|
|
add_to( to, nto, mail );
|
|
|
|
ldap_value_free( mail );
|
|
|
|
/* else generate a bounce */
|
|
} else {
|
|
ndn = ldap_get_dn( ld, ee );
|
|
|
|
add_error( err, nerr,
|
|
E_JOINMEMBERNOEMAIL, ndn, NULLMSG );
|
|
|
|
free( ndn );
|
|
}
|
|
}
|
|
|
|
ldap_msgfree( res );
|
|
}
|
|
opt = MAIL500_MAXAMBIGUOUS;
|
|
ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
|
|
}
|
|
|
|
if ( ! anymembers ) {
|
|
add_error( err, nerr, E_NOMEMBERS, dn, NULLMSG );
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_member(
|
|
char *gdn,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Group **togroups,
|
|
int *ngroups,
|
|
Error **err,
|
|
int *nerr,
|
|
char **suppress
|
|
)
|
|
{
|
|
char *ndn;
|
|
char **mail;
|
|
int i, rc;
|
|
LDAPMessage *res, *e;
|
|
struct timeval timeout;
|
|
|
|
timeout.tv_sec = MAIL500_TIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
if ( (rc = ldap_search_st( ld, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
|
|
attrs, 0, &timeout, &res )) != LDAP_SUCCESS ) {
|
|
if ( rc == LDAP_NO_SUCH_OBJECT ) {
|
|
add_error( err, nerr, E_BADMEMBER, dn, NULLMSG );
|
|
|
|
return;
|
|
} else {
|
|
syslog( LOG_ALERT, "member search return 0x%x", rc );
|
|
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
}
|
|
|
|
if ( (e = ldap_first_entry( ld, res )) == NULL ) {
|
|
syslog( LOG_ALERT, "member search error parsing entry" );
|
|
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
ndn = ldap_get_dn( ld, e );
|
|
|
|
/* allow groups within groups */
|
|
if ( isgroup( e ) ) {
|
|
if ( do_group( e, ndn, to, nto, togroups, ngroups, err, nerr )
|
|
== -1 ) {
|
|
syslog( LOG_ALERT, "group loop (%s) (%s)", gdn, ndn );
|
|
}
|
|
|
|
free( ndn );
|
|
|
|
return;
|
|
}
|
|
|
|
/* send to the member's mail attribute */
|
|
if ( (mail = ldap_get_values( ld, e, "mail" )) != NULL ) {
|
|
add_to( to, nto, mail );
|
|
|
|
ldap_value_free( mail );
|
|
|
|
/* else generate a bounce */
|
|
} else {
|
|
if ( suppress == NULL || strcasecmp( suppress[0], "FALSE" )
|
|
== 0 ) {
|
|
add_error( err, nerr, E_MEMBERNOEMAIL, ndn, NULLMSG );
|
|
}
|
|
}
|
|
|
|
free( ndn );
|
|
}
|
|
|
|
static void
|
|
do_group_request(
|
|
LDAPMessage *e,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Error **err,
|
|
int *nerr
|
|
)
|
|
{
|
|
char **requeststo;
|
|
|
|
if ( (requeststo = get_attributes_mail_dn( e, "rfc822RequestsTo",
|
|
"requestsTo" )) != NULL ) {
|
|
add_to( to, nto, requeststo );
|
|
|
|
ldap_value_free( requeststo );
|
|
} else {
|
|
add_error( err, nerr, E_NOREQUEST, dn, NULLMSG );
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_group_errors(
|
|
LDAPMessage *e,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Error **err,
|
|
int *nerr
|
|
)
|
|
{
|
|
char **errorsto;
|
|
|
|
if ( (errorsto = get_attributes_mail_dn( e, "rfc822ErrorsTo",
|
|
"errorsTo" )) != NULL ) {
|
|
add_to( to, nto, errorsto );
|
|
|
|
ldap_value_free( errorsto );
|
|
} else {
|
|
add_error( err, nerr, E_NOERRORS, dn, NULLMSG );
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_group_owner(
|
|
LDAPMessage *e,
|
|
char *dn,
|
|
char ***to,
|
|
int *nto,
|
|
Error **err,
|
|
int *nerr
|
|
)
|
|
{
|
|
char **owner;
|
|
|
|
if ( (owner = get_attributes_mail_dn( e, "", "owner" )) != NULL ) {
|
|
add_to( to, nto, owner );
|
|
ldap_value_free( owner );
|
|
} else {
|
|
add_error( err, nerr, E_NOOWNER, dn, NULLMSG );
|
|
}
|
|
}
|
|
|
|
static void
|
|
send_message( char **to )
|
|
{
|
|
int pid;
|
|
#ifndef HAVE_WAITPID
|
|
WAITSTATUSTYPE status;
|
|
#endif
|
|
|
|
if ( debug ) {
|
|
char buf[1024];
|
|
int i;
|
|
|
|
strcpy( buf, to[0] );
|
|
for ( i = 1; to[i] != NULL; i++ ) {
|
|
strcat( buf, " " );
|
|
strcat( buf, to[i] );
|
|
}
|
|
|
|
syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
|
|
}
|
|
|
|
/* parent */
|
|
if ( (pid = fork()) != 0 ) {
|
|
#ifdef HAVE_WAITPID
|
|
waitpid( pid, (int *) NULL, 0 );
|
|
#else
|
|
wait4( pid, &status, WAIT_FLAGS, 0 );
|
|
#endif
|
|
/* child */
|
|
} else {
|
|
/* to includes sendmailargs */
|
|
execv( MAIL500_SENDMAIL, to );
|
|
|
|
syslog( LOG_ALERT, "execv failed" );
|
|
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
}
|
|
|
|
static void
|
|
send_group( Group *group, int ngroup )
|
|
{
|
|
int i, pid;
|
|
char **argv;
|
|
int argc;
|
|
char *iargv[7];
|
|
#ifndef HAVE_WAITPID
|
|
WAITSTATUSTYPE status;
|
|
#endif
|
|
|
|
for ( i = 0; i < ngroup; i++ ) {
|
|
(void) rewind( stdin );
|
|
|
|
iargv[0] = MAIL500_SENDMAIL;
|
|
iargv[1] = "-f";
|
|
iargv[2] = group[i].g_errorsto;
|
|
iargv[3] = "-oMrX.500";
|
|
iargv[4] = "-odi";
|
|
iargv[5] = "-oi";
|
|
iargv[6] = NULL;
|
|
|
|
argv = NULL;
|
|
argc = 0;
|
|
add_to( &argv, &argc, iargv );
|
|
add_to( &argv, &argc, group[i].g_members );
|
|
|
|
if ( debug ) {
|
|
char buf[1024];
|
|
int i;
|
|
|
|
strcpy( buf, argv[0] );
|
|
for ( i = 1; i < argc; i++ ) {
|
|
strcat( buf, " " );
|
|
strcat( buf, argv[i] );
|
|
}
|
|
|
|
syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
|
|
}
|
|
|
|
/* parent */
|
|
if ( (pid = fork()) != 0 ) {
|
|
#ifdef HAVE_WAITPID
|
|
waitpid( pid, (int *) NULL, 0 );
|
|
#else
|
|
wait4( pid, &status, WAIT_FLAGS, 0 );
|
|
#endif
|
|
/* child */
|
|
} else {
|
|
execv( MAIL500_SENDMAIL, argv );
|
|
|
|
syslog( LOG_ALERT, "execv failed" );
|
|
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
send_errors( Error *err, int nerr )
|
|
{
|
|
int pid, i, namelen;
|
|
FILE *fp;
|
|
int fd[2];
|
|
char *argv[8];
|
|
char buf[1024];
|
|
#ifndef HAVE_WAITPID
|
|
WAITSTATUSTYPE status;
|
|
#endif
|
|
|
|
if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
|
|
mailfrom = errorsfrom;
|
|
}
|
|
|
|
argv[0] = MAIL500_SENDMAIL;
|
|
argv[1] = "-oMrX.500";
|
|
argv[2] = "-odi";
|
|
argv[3] = "-oi";
|
|
argv[4] = "-f";
|
|
argv[5] = MAIL500_BOUNCEFROM;
|
|
argv[6] = mailfrom;
|
|
argv[7] = NULL;
|
|
|
|
if ( debug ) {
|
|
int i;
|
|
|
|
strcpy( buf, argv[0] );
|
|
for ( i = 1; argv[i] != NULL; i++ ) {
|
|
strcat( buf, " " );
|
|
strcat( buf, argv[i] );
|
|
}
|
|
|
|
syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
|
|
}
|
|
|
|
if ( pipe( fd ) == -1 ) {
|
|
syslog( LOG_ALERT, "cannot create pipe" );
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
if ( (pid = fork()) != 0 ) {
|
|
if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
|
|
syslog( LOG_ALERT, "cannot fdopen pipe" );
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
fprintf( fp, "To: %s\n", mailfrom );
|
|
fprintf( fp, "From: %s\n", errorsfrom );
|
|
fprintf( fp, "Subject: undeliverable mail\n" );
|
|
fprintf( fp, "\n" );
|
|
fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
|
|
for ( i = 0; i < nerr; i++ ) {
|
|
namelen = strlen( err[i].e_addr );
|
|
fprintf( fp, "\n" );
|
|
|
|
switch ( err[i].e_code ) {
|
|
case E_USERUNKNOWN:
|
|
fprintf( fp, "%s: User unknown\n", err[i].e_addr );
|
|
break;
|
|
|
|
case E_GROUPUNKNOWN:
|
|
fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
|
|
break;
|
|
|
|
case E_BADMEMBER:
|
|
fprintf( fp, "%s: Group member does not exist\n",
|
|
err[i].e_addr );
|
|
fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
|
|
fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
|
|
fprintf( fp, "then re-adding the person to the group.\n" );
|
|
break;
|
|
|
|
case E_NOREQUEST:
|
|
fprintf( fp, "%s: Group exists but has no request address\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
case E_NOERRORS:
|
|
fprintf( fp, "%s: Group exists but has no errors-to address\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
case E_NOOWNER:
|
|
fprintf( fp, "%s: Group exists but has no owner\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
case E_AMBIGUOUS:
|
|
do_ambiguous( fp, &err[i], namelen );
|
|
break;
|
|
|
|
case E_NOEMAIL:
|
|
do_noemail( fp, &err[i], namelen );
|
|
break;
|
|
|
|
case E_MEMBERNOEMAIL:
|
|
fprintf( fp, "%s: Group member exists but does not have an email address\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
case E_JOINMEMBERNOEMAIL:
|
|
fprintf( fp, "%s: User has joined group but does not have an email address\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
case E_LOOP:
|
|
fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
|
|
err[i].e_addr, err[i].e_loop );
|
|
break;
|
|
|
|
case E_NOMEMBERS:
|
|
fprintf( fp, "%s: Group has no members\n",
|
|
err[i].e_addr );
|
|
break;
|
|
|
|
default:
|
|
syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
break;
|
|
}
|
|
}
|
|
|
|
fprintf( fp, "\n------- The original message sent:\n\n" );
|
|
|
|
while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
|
|
fputs( buf, fp );
|
|
}
|
|
fclose( fp );
|
|
|
|
#ifdef HAVE_WAITPID
|
|
waitpid( pid, (int *) NULL, 0 );
|
|
#else
|
|
wait4( pid, &status, WAIT_FLAGS, 0 );
|
|
#endif
|
|
} else {
|
|
dup2( fd[0], 0 );
|
|
|
|
execv( MAIL500_SENDMAIL, argv );
|
|
|
|
syslog( LOG_ALERT, "execv failed" );
|
|
|
|
exit( EX_TEMPFAIL );
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_noemail( FILE *fp, Error *err, int namelen )
|
|
{
|
|
int i, last;
|
|
char *dn, *rdn;
|
|
char **ufn, **vals;
|
|
|
|
fprintf(fp, "%s: User has no email address registered.\n",
|
|
err->e_addr );
|
|
fprintf( fp, "%*s Name, title, postal address and phone for '%s':\n\n",
|
|
namelen, " ", err->e_addr );
|
|
|
|
/* name */
|
|
dn = ldap_get_dn( ld, err->e_msg );
|
|
ufn = ldap_explode_dn( dn, 1 );
|
|
rdn = strdup( ufn[0] );
|
|
if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
|
|
if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
|
|
!= NULL ) {
|
|
for ( i = 0; vals[i]; i++ ) {
|
|
last = strlen( vals[i] ) - 1;
|
|
if ( isdigit( vals[i][last] ) ) {
|
|
rdn = strdup( vals[i] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
ldap_value_free( vals );
|
|
}
|
|
}
|
|
fprintf( fp, "%*s %s\n", namelen, " ", rdn );
|
|
free( dn );
|
|
free( rdn );
|
|
ldap_value_free( ufn );
|
|
|
|
/* titles or descriptions */
|
|
if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
|
|
(vals = ldap_get_values( ld, err->e_msg, "description" ))
|
|
== NULL ) {
|
|
fprintf( fp, "%*s No title or description registered\n",
|
|
namelen, " " );
|
|
} else {
|
|
for ( i = 0; vals[i] != NULL; i++ ) {
|
|
fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
|
|
}
|
|
|
|
ldap_value_free( vals );
|
|
}
|
|
|
|
/* postal address */
|
|
if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
|
|
== NULL ) {
|
|
fprintf( fp, "%*s No postal address registered\n", namelen,
|
|
" " );
|
|
} else {
|
|
fprintf( fp, "%*s ", namelen, " " );
|
|
for ( i = 0; vals[0][i] != '\0'; i++ ) {
|
|
if ( vals[0][i] == '$' ) {
|
|
fprintf( fp, "\n%*s ", namelen, " " );
|
|
while ( isspace( vals[0][i+1] ) )
|
|
i++;
|
|
} else {
|
|
fprintf( fp, "%c", vals[0][i] );
|
|
}
|
|
}
|
|
fprintf( fp, "\n" );
|
|
|
|
ldap_value_free( vals );
|
|
}
|
|
|
|
/* telephone number */
|
|
if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
|
|
== NULL ) {
|
|
fprintf( fp, "%*s No phone number registered\n", namelen,
|
|
" " );
|
|
} else {
|
|
for ( i = 0; vals[i] != NULL; i++ ) {
|
|
fprintf( fp, "%*s %s\n", namelen, " ", vals[i] );
|
|
}
|
|
|
|
ldap_value_free( vals );
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
do_ambiguous( FILE *fp, Error *err, int namelen )
|
|
{
|
|
int i, last;
|
|
char *dn, *rdn;
|
|
char **ufn, **vals;
|
|
LDAPMessage *e;
|
|
|
|
i = ldap_result2error( ld, err->e_msg, 0 );
|
|
|
|
fprintf( fp, "%s: Ambiguous user. %s%d matches found:\n\n",
|
|
err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
|
|
ldap_count_entries( ld, err->e_msg ) );
|
|
|
|
for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
|
|
e = ldap_next_entry( ld, e ) ) {
|
|
dn = ldap_get_dn( ld, e );
|
|
ufn = ldap_explode_dn( dn, 1 );
|
|
rdn = strdup( ufn[0] );
|
|
if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
|
|
if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
|
|
for ( i = 0; vals[i]; i++ ) {
|
|
last = strlen( vals[i] ) - 1;
|
|
if ( isdigit( vals[i][last] ) ) {
|
|
rdn = strdup( vals[i] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
ldap_value_free( vals );
|
|
}
|
|
}
|
|
|
|
if ( isgroup( e ) ) {
|
|
vals = ldap_get_values( ld, e, "description" );
|
|
} else {
|
|
vals = ldap_get_values( ld, e, "title" );
|
|
}
|
|
|
|
fprintf( fp, " %-20s %s\n", rdn, vals ? vals[0] : "" );
|
|
for ( i = 1; vals && vals[i] != NULL; i++ ) {
|
|
fprintf( fp, " %s\n", vals[i] );
|
|
}
|
|
|
|
free( dn );
|
|
free( rdn );
|
|
ldap_value_free( ufn );
|
|
if ( vals != NULL )
|
|
ldap_value_free( vals );
|
|
}
|
|
}
|
|
|
|
static int
|
|
count_values( char **list )
|
|
{
|
|
int i;
|
|
|
|
for ( i = 0; list && list[i] != NULL; i++ )
|
|
; /* NULL */
|
|
|
|
return( i );
|
|
}
|
|
|
|
static void
|
|
add_to( char ***list, int *nlist, char **new )
|
|
{
|
|
int i, nnew, oldnlist;
|
|
|
|
nnew = count_values( new );
|
|
|
|
oldnlist = *nlist;
|
|
if ( *list == NULL || *nlist == 0 ) {
|
|
*list = (char **) malloc( (nnew + 1) * sizeof(char *) );
|
|
*nlist = nnew;
|
|
} else {
|
|
*list = (char **) realloc( *list, *nlist * sizeof(char *) +
|
|
nnew * sizeof(char *) + sizeof(char *) );
|
|
*nlist += nnew;
|
|
}
|
|
|
|
for ( i = 0; i < nnew; i++ )
|
|
(*list)[i + oldnlist] = strdup( new[i] );
|
|
(*list)[*nlist] = NULL;
|
|
}
|
|
|
|
static int
|
|
isgroup( LDAPMessage *e )
|
|
{
|
|
int i;
|
|
char **oclist;
|
|
|
|
oclist = ldap_get_values( ld, e, "objectClass" );
|
|
|
|
for ( i = 0; oclist[i] != NULL; i++ ) {
|
|
if ( strcasecmp( oclist[i], "rfc822MailGroup" ) == 0 ) {
|
|
ldap_value_free( oclist );
|
|
return( 1 );
|
|
}
|
|
}
|
|
ldap_value_free( oclist );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static void
|
|
add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
|
|
{
|
|
if ( *nerr == 0 ) {
|
|
*err = (Error *) malloc( sizeof(Error) );
|
|
} else {
|
|
*err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
|
|
}
|
|
|
|
(*err)[*nerr].e_code = code;
|
|
(*err)[*nerr].e_addr = strdup( addr );
|
|
(*err)[*nerr].e_msg = msg;
|
|
(*nerr)++;
|
|
}
|
|
|
|
static void
|
|
add_group( char *dn, Group **list, int *nlist )
|
|
{
|
|
int i, namelen;
|
|
char **ufn;
|
|
|
|
for ( i = 0; i < *nlist; i++ ) {
|
|
if ( strcmp( dn, (*list)[i].g_dn ) == 0 ) {
|
|
syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
|
|
return;
|
|
}
|
|
}
|
|
|
|
ufn = ldap_explode_dn( dn, 1 );
|
|
namelen = strlen( ufn[0] );
|
|
|
|
if ( *nlist == 0 ) {
|
|
*list = (Group *) malloc( sizeof(Group) );
|
|
} else {
|
|
*list = (Group *) realloc( *list, (*nlist + 1) *
|
|
sizeof(Group) );
|
|
}
|
|
|
|
/* send errors to groupname-errors@host */
|
|
(*list)[*nlist].g_errorsto = (char *) malloc( namelen + sizeof(ERRORS)
|
|
+ hostlen + 2 );
|
|
sprintf( (*list)[*nlist].g_errorsto, "%s-%s@%s", ufn[0], ERRORS, host );
|
|
(void) canonical( (*list)[*nlist].g_errorsto );
|
|
|
|
/* send to groupname-members@host - make it a list for send_group */
|
|
(*list)[*nlist].g_members = (char **) malloc( 2 * sizeof(char *) );
|
|
(*list)[*nlist].g_members[0] = (char *) malloc( namelen +
|
|
sizeof(MEMBERS) + hostlen + 2 );
|
|
sprintf( (*list)[*nlist].g_members[0], "%s-%s@%s", ufn[0], MEMBERS,
|
|
host );
|
|
(void) canonical( (*list)[*nlist].g_members[0] );
|
|
(*list)[*nlist].g_members[1] = NULL;
|
|
|
|
/* save the group's dn so we can check for loops above */
|
|
(*list)[*nlist].g_dn = strdup( dn );
|
|
|
|
(*nlist)++;
|
|
|
|
ldap_value_free( ufn );
|
|
}
|
|
|
|
static void
|
|
unbind_and_exit( int rc )
|
|
{
|
|
int i;
|
|
|
|
if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
|
|
syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
|
|
|
|
exit( rc );
|
|
}
|
|
|
|
static char *
|
|
canonical( char *s )
|
|
{
|
|
char *saves = s;
|
|
|
|
for ( ; *s != '\0'; s++ ) {
|
|
if ( *s == ' ' )
|
|
*s = '.';
|
|
}
|
|
|
|
return( saves );
|
|
}
|
|
|
|
static int
|
|
group_loop( char *dn )
|
|
{
|
|
int i;
|
|
static char **groups;
|
|
static int ngroups;
|
|
|
|
for ( i = 0; i < ngroups; i++ ) {
|
|
if ( strcmp( dn, groups[i] ) == 0 )
|
|
return( 1 );
|
|
}
|
|
|
|
if ( ngroups == 0 )
|
|
groups = (char **) malloc( sizeof(char *) );
|
|
else
|
|
groups = (char **) realloc( groups,
|
|
(ngroups + 1) * sizeof(char *) );
|
|
|
|
groups[ngroups++] = strdup( dn );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
has_attributes( LDAPMessage *e, char *attr1, char *attr2 )
|
|
{
|
|
char **attr;
|
|
|
|
if ( (attr = ldap_get_values( ld, e, attr1 )) != NULL ) {
|
|
ldap_value_free( attr );
|
|
return( 1 );
|
|
}
|
|
|
|
if ( (attr = ldap_get_values( ld, e, attr2 )) != NULL ) {
|
|
ldap_value_free( attr );
|
|
return( 1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static char **
|
|
get_attributes_mail_dn(
|
|
LDAPMessage *e,
|
|
char *attr1,
|
|
char *attr2 /* this one is dn-valued */
|
|
)
|
|
{
|
|
LDAPMessage *ee, *res;
|
|
char **vals, **dnlist, **mail, **grname, **graddr;
|
|
char *dn;
|
|
int nto = 0, i, rc;
|
|
struct timeval timeout;
|
|
|
|
dn = ldap_get_dn( ld, e );
|
|
|
|
vals = ldap_get_values( ld, e, attr1 );
|
|
for ( nto = 0; vals != NULL && vals[nto] != NULL; nto++ )
|
|
; /* NULL */
|
|
|
|
if ( (dnlist = ldap_get_values( ld, e, attr2 )) != NULL ) {
|
|
timeout.tv_sec = MAIL500_TIMEOUT;
|
|
timeout.tv_usec = 0;
|
|
|
|
for ( i = 0; dnlist[i] != NULL; i++ ) {
|
|
if ( (rc = ldap_search_st( ld, dnlist[i],
|
|
LDAP_SCOPE_BASE, "(objectclass=*)", attrs, 0,
|
|
&timeout, &res )) != LDAP_SUCCESS ) {
|
|
if ( rc != LDAP_NO_SUCH_OBJECT ) {
|
|
unbind_and_exit( EX_TEMPFAIL );
|
|
}
|
|
|
|
syslog( LOG_ALERT, "bad (%s) dn (%s)", attr2,
|
|
dnlist[i] );
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( (ee = ldap_first_entry( ld, res )) == NULL ) {
|
|
syslog( LOG_ALERT, "error parsing x500 entry" );
|
|
continue;
|
|
}
|
|
|
|
if ( isgroup(ee) ) {
|
|
char *graddr[2];
|
|
|
|
grname = ldap_explode_dn( dnlist[i], 1 );
|
|
|
|
/* groupname + host + @ + null */
|
|
graddr[0] = (char *) malloc( strlen( grname[0] )
|
|
+ strlen( host ) + 2 );
|
|
graddr[1] = NULL;
|
|
sprintf( graddr[0], "%s@%s", grname[0], host);
|
|
(void) canonical( graddr[0] );
|
|
|
|
add_to( &vals, &nto, graddr );
|
|
|
|
free( graddr[0] );
|
|
ldap_value_free( grname );
|
|
} else if ( (mail = ldap_get_values( ld, ee, "mail" ))
|
|
!= NULL ) {
|
|
add_to( &vals, &nto, mail );
|
|
|
|
ldap_value_free( mail );
|
|
}
|
|
|
|
ldap_msgfree( res );
|
|
}
|
|
}
|
|
|
|
return( vals );
|
|
}
|