/* $OpenLDAP$ */ /* * 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. * * Copyright 1998-2000 The OpenLDAP Foundation * COPYING RESTRICTIONS APPLY. See COPYRIGHT File in top level directory * of this package for details. */ #include "portable.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_RESOURCE_H #include #endif #include #include "ldap_defaults.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 = MAIL500_BOUNCEFROM; 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 #define E_NOOWNADDRESS 13 char *e_addr; union e_union_u { 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; int g_nmembers; } Group; typedef struct baseinfo { char *b_url; int b_m_entries; 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 */ } Base; Base **base = NULL; char *sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL }; typedef struct attr_semantics { char *as_name; int as_m_valued; /* Is multivalued? */ int as_priority; /* Priority level of this attribut type */ int as_syntax; /* How to interpret values */ int as_m_entries; /* Can resolve to several entries? */ int as_kind; /* Recipient, sender, etc. */ char *as_param; /* Extra info for filters and things alike */ } AttrSemantics; #define AS_SYNTAX_UNKNOWN 0 #define AS_SYNTAX_NATIVE_MB 1 /* Unqualified mailbox name */ #define AS_SYNTAX_RFC822 2 /* RFC822 mail address */ #define AS_SYNTAX_HOST 3 #define AS_SYNTAX_DN 4 /* A directory entry */ #define AS_SYNTAX_RFC822_EXT 5 #define AS_SYNTAX_URL 6 /* mailto: or ldap: URL */ #define AS_SYNTAX_BOOL_FILTER 7 /* For joinable, filter in as_param */ #define AS_SYNTAX_PRESENT 8 /* Value irrelevant, only presence is * considered. */ #define AS_KIND_UNKNOWN 0 #define AS_KIND_RECIPIENT 1 #define AS_KIND_ERRORS 2 /* For ErrorsTo and similar */ #define AS_KIND_REQUEST 3 #define AS_KIND_OWNER 4 #define AS_KIND_ROUTE_TO_HOST 5 /* Expand at some other host */ #define AS_KIND_ALLOWED_SENDER 6 /* Can send to group */ #define AS_KIND_MODERATOR 7 #define AS_KIND_ROUTE_TO_ADDR 8 /* Rewrite recipient address as */ #define AS_KIND_OWN_ADDR 9 /* RFC822 name of this entry */ #define AS_KIND_DELIVERY_TYPE 10 /* How to deliver mail to this entry */ AttrSemantics **attr_semantics = NULL; int current_priority = 0; typedef struct subst { char sub_char; char *sub_value; } Subst; char **groupclasses = NULL; char **def_attr = NULL; char **myhosts = NULL; /* FQDNs not to route elsewhere */ char **mydomains = NULL; /* If an RFC822 address points to one of these domains, search it in the directory instead of returning it to hte MTA */ static void load_config( char *filespec ); static void split_address( char *address, char **localpart, char **domainpart); static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ); static void do_address( char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ); 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 int count_values( char **list ); static void add_to( char ***list, int *nlist, char **new ); static void add_single_to( char ***list, char *new ); static int isgroup( LDAPMessage *e ); static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg ); static void unbind_and_exit( int rc ) LDAP_GCCATTR((noreturn)); static void send_group( Group **group, int ngroup ); static int connect_to_x500( void ); int main ( int argc, char **argv ) { char *myname; char **tolist; Error *errlist; Group **togroups; int numto, ngroups, numerr, nargs; int i, j; char *conffile = NULL; if ( (myname = strrchr( argv[0], '/' )) == NULL ) myname = strdup( argv[0] ); else myname = strdup( myname + 1 ); #ifdef SIGPIPE (void) SIGNAL( SIGPIPE, SIG_IGN ); #endif #ifdef LOG_MAIL openlog( myname, OPENLOG_OPTIONS, LOG_MAIL ); #elif LOG_DEBUG openlog( myname, OPENLOG_OPTIONS ); #endif while ( (i = getopt( argc, argv, "d:C:f:h:l:m:v:" )) != EOF ) { switch( i ) { case 'd': /* turn on debugging */ debug |= atoi( optarg ); break; case 'C': /* path to configuration file */ conffile = strdup( 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 ( conffile == NULL ) { syslog( LOG_ALERT, "required argument -C not present" ); exit( EX_TEMPFAIL ); } load_config( conffile ); 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; char *localpart = NULL, *domainpart = NULL; char address[1024]; type = USER; split_address( argv[i], &localpart, &domainpart ); if ( (s = strrchr( localpart, '-' )) != 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'; } } if ( domainpart ) { sprintf( address, "%s@%s", localpart, domainpart ); free( localpart ); free( domainpart ); } else { sprintf( address, "%s@%s", localpart, domainpart ); free( localpart ); } do_address( address, &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 char * get_config_line( FILE *cf, int *lineno) { static char buf[2048]; int len; int pos; int room; pos = 0; room = sizeof( buf ); while ( fgets( &buf[pos], room, cf ) ) { (*lineno)++; if ( pos > 0 ) { /* Delete whitespace at the beginning of new data */ if ( isspace( buf[pos] ) ) { char *s, *d; for ( s = buf+pos; isspace(*s); s++ ) ; for ( d = buf+pos; *s; s++, d++ ) { *d = *s; } *d = *s; } } len = strlen( buf ); if ( buf[len-1] != '\n' ) { syslog( LOG_ALERT, "Definition too long at line %d", *lineno ); exit( EX_TEMPFAIL ); } if ( buf[0] == '#' ) continue; if ( strspn( buf, " \t\n" ) == len ) continue; if ( buf[len-2] == '\\' ) { pos = len - 2; room = sizeof(buf) - pos; continue; } /* We have a real line, we will exit the loop */ buf[len-1] = '\0'; return( buf ); } return( NULL ); } static void add_url ( char *url, int rdnpref, int typemask ) { Base **list_temp; int size; Base *b; b = calloc(1, sizeof(Base)); if ( !b ) { syslog( LOG_ALERT, "Out of memory" ); exit( EX_TEMPFAIL ); } b->b_url = strdup( url ); b->b_rdnpref = rdnpref; b->b_search = typemask; if ( base == NULL ) { base = calloc(2, sizeof(LDAPURLDesc *)); if ( !base ) { syslog( LOG_ALERT, "Out of memory" ); exit( EX_TEMPFAIL ); } base[0] = b; } else { for ( size = 0; base[size]; size++ ) ; size += 2; list_temp = realloc( base, size*sizeof(LDAPURLDesc *) ); if ( !list_temp ) { syslog( LOG_ALERT, "Out of memory" ); exit( EX_TEMPFAIL ); } base = list_temp; base[size-2] = b; base[size-1] = NULL; } } static void add_def_attr( char *s ) { char *p, *q; p = s; while ( *p ) { p += strspn( p, "\t," ); q = strpbrk( p, " \t," ); if ( q ) { *q = '\0'; add_single_to( &def_attr, p ); } else { add_single_to( &def_attr, p ); break; } p = q + 1; } } static void add_attr_semantics( char *s ) { char *p, *q; AttrSemantics *as; as = calloc( 1, sizeof( AttrSemantics ) ); as->as_priority = current_priority; p = s; while ( isspace ( *p ) ) p++; q = p; while ( !isspace ( *q ) && *q != '\0' ) q++; *q = '\0'; as->as_name = strdup( p ); p = q + 1; while ( *p ) { while ( isspace ( *p ) ) p++; q = p; while ( !isspace ( *q ) && *q != '\0' ) q++; *q = '\0'; if ( !strcasecmp( p, "multivalued" ) ) { as->as_m_valued = 1; } else if ( !strcasecmp( p, "multiple-entries" ) ) { as->as_m_entries = 1; } else if ( !strcasecmp( p, "local-native-mailbox" ) ) { as->as_syntax = AS_SYNTAX_NATIVE_MB; } else if ( !strcasecmp( p, "rfc822" ) ) { as->as_syntax = AS_SYNTAX_RFC822; } else if ( !strcasecmp( p, "rfc822-extended" ) ) { as->as_syntax = AS_SYNTAX_RFC822_EXT; } else if ( !strcasecmp( p, "dn" ) ) { as->as_syntax = AS_SYNTAX_DN; } else if ( !strcasecmp( p, "url" ) ) { as->as_syntax = AS_SYNTAX_URL; } else if ( !strcasecmp( p, "search-with-filter" ) ) { as->as_syntax = AS_SYNTAX_BOOL_FILTER; } else if ( !strncasecmp( p, "param=", 6 ) ) { q = strchr( p, '=' ); if ( q ) { p = q + 1; while ( *q && !isspace( *q ) ) { q++; } if ( *q ) { *q = '\0'; as->as_param = strdup( p ); p = q + 1; } else { as->as_param = strdup( p ); p = q; } } } else if ( !strcasecmp( p, "host" ) ) { as->as_kind = AS_SYNTAX_HOST; } else if ( !strcasecmp( p, "present" ) ) { as->as_kind = AS_SYNTAX_PRESENT; } else if ( !strcasecmp( p, "route-to-host" ) ) { as->as_kind = AS_KIND_ROUTE_TO_HOST; } else if ( !strcasecmp( p, "route-to-address" ) ) { as->as_kind = AS_KIND_ROUTE_TO_ADDR; } else if ( !strcasecmp( p, "own-address" ) ) { as->as_kind = AS_KIND_OWN_ADDR; } else if ( !strcasecmp( p, "recipient" ) ) { as->as_kind = AS_KIND_RECIPIENT; } else if ( !strcasecmp( p, "errors" ) ) { as->as_kind = AS_KIND_ERRORS; } else if ( !strcasecmp( p, "request" ) ) { as->as_kind = AS_KIND_REQUEST; } else if ( !strcasecmp( p, "owner" ) ) { as->as_kind = AS_KIND_OWNER; } else if ( !strcasecmp( p, "delivery-type" ) ) { as->as_kind = AS_KIND_DELIVERY_TYPE; } else { syslog( LOG_ALERT, "Unknown semantics word %s", p ); exit( EX_TEMPFAIL ); } p = q + 1; } if ( attr_semantics == NULL ) { attr_semantics = calloc(2, sizeof(AttrSemantics *)); if ( !attr_semantics ) { syslog( LOG_ALERT, "Out of memory" ); exit( EX_TEMPFAIL ); } attr_semantics[0] = as; } else { int size; AttrSemantics **list_temp; for ( size = 0; attr_semantics[size]; size++ ) ; size += 2; list_temp = realloc( attr_semantics, size*sizeof(AttrSemantics *) ); if ( !list_temp ) { syslog( LOG_ALERT, "Out of memory" ); exit( EX_TEMPFAIL ); } attr_semantics = list_temp; attr_semantics[size-2] = as; attr_semantics[size-1] = NULL; } } static void load_config( char *filespec ) { FILE *cf; char *line; int lineno = 0; char *p; int rdnpref; int typemask; cf = fopen( filespec, "r" ); if ( !cf ) { perror( "Opening config file" ); exit( EX_TEMPFAIL ); } while ( ( line = get_config_line( cf,&lineno ) ) ) { p = strpbrk( line, " \t" ); if ( !p ) { syslog( LOG_ALERT, "Missing space at line %d", lineno ); exit( EX_TEMPFAIL ); } if ( !strncmp( line, "search", p-line ) ) { p += strspn( p, " \t" ); /* TBC, get these */ rdnpref = 0; typemask = 0xFF; add_url( p, rdnpref, typemask ); } else if ( !strncmp(line, "attribute", p-line) ) { p += strspn(p, " \t"); add_attr_semantics( p ); } else if ( !strncmp(line, "default-attributes", p-line) ) { p += strspn(p, " \t"); add_def_attr( p ); } else if ( !strncmp(line, "group-classes", p-line) ) { p += strspn(p, " \t"); add_single_to( &groupclasses, p ); } else if ( !strncmp(line, "priority", p-line) ) { p += strspn(p, " \t"); current_priority = atoi(p); } else if ( !strncmp(line, "domain", p-line) ) { p += strspn(p, " \t"); add_single_to( &mydomains, p ); } else if ( !strncmp(line, "host", p-line) ) { p += strspn(p, " \t"); add_single_to( &myhosts, p ); } else { syslog( LOG_ALERT, "Unparseable config definition at line %d", lineno ); exit( EX_TEMPFAIL ); } } fclose( cf ); } static int connect_to_x500( void ) { int opt; if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) { syslog( LOG_ALERT, "ldap_init failed" ); return( -1 ); } /* TBC: Set this only when it makes sense 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 Group * new_group( char *dn, Group ***list, int *nlist ) { int i; Group *this_group; for ( i = 0; i < *nlist; i++ ) { if ( strcmp( dn, (*list)[i]->g_dn ) == 0 ) { syslog( LOG_ALERT, "group loop 2 detected (%s)", dn ); return NULL; } } this_group = (Group *) malloc( sizeof(Group) ); if ( *nlist == 0 ) { *list = (Group **) malloc( sizeof(Group *) ); } else { *list = (Group **) realloc( *list, (*nlist + 1) * sizeof(Group *) ); } this_group->g_errorsto = NULL; this_group->g_members = NULL; this_group->g_nmembers = 0; /* save the group's dn so we can check for loops above */ this_group->g_dn = strdup( dn ); (*list)[*nlist] = this_group; (*nlist)++; return( this_group ); } static void split_address( char *address, char **localpart, char **domainpart ) { char *p; if ( ( p = strrchr( address, '@' ) ) == NULL ) { *localpart = strdup( address ); *domainpart = NULL; } else { *localpart = malloc( p - address + 1 ); strncpy( *localpart, address, p - address ); (*localpart)[p - address] = '\0'; p++; *domainpart = strdup( p ); } } static int dn_search( char **dnlist, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr ) { int rc; int i; int resolved = 0; LDAPMessage *res, *e; struct timeval timeout; timeout.tv_sec = MAIL500_TIMEOUT; timeout.tv_usec = 0; for ( i = 0; dnlist[i]; i++ ) { if ( (rc = ldap_search_st( ld, dnlist[i], LDAP_SCOPE_BASE, NULL, def_attr, 0, &timeout, &res )) != LDAP_SUCCESS ) { if ( rc == LDAP_NO_SUCH_OBJECT ) { add_error( err, nerr, E_BADMEMBER, dnlist[i], NULL ); continue; } else { syslog( LOG_ALERT, "member search return 0x%x", rc ); unbind_and_exit( EX_TEMPFAIL ); } } else { if ( (e = ldap_first_entry( ld, res )) == NULL ) { syslog( LOG_ALERT, "member search error parsing entry" ); unbind_and_exit( EX_TEMPFAIL ); } if ( entry_engine( e, dnlist[i], address, to, nto, togroups, ngroups, err, nerr, USER | GROUP_MEMBERS ) ) { resolved = 1; } } } return( resolved ); } static int search_ldap_url( char *url, Subst *substs, char *address, int rdnpref, int multi_entry, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { LDAPURLDesc *ludp; char *p, *s, *d; int i; char filter[1024]; LDAPMessage *e, *res; int rc; char **attrlist; struct timeval timeout; int match; int resolved = 0; char *dn; timeout.tv_sec = MAIL500_TIMEOUT; timeout.tv_usec = 0; rc = ldap_url_parse( url, &ludp ); if ( rc ) { switch ( rc ) { case LDAP_URL_ERR_BADSCHEME: syslog( LOG_ALERT, "Not an LDAP URL: %s", url ); break; case LDAP_URL_ERR_BADENCLOSURE: syslog( LOG_ALERT, "Bad Enclosure in URL: %s", url ); break; case LDAP_URL_ERR_BADURL: syslog( LOG_ALERT, "Bad URL: %s", url ); break; case LDAP_URL_ERR_BADHOST: syslog( LOG_ALERT, "Host is invalid in URL: %s", url ); break; case LDAP_URL_ERR_BADATTRS: syslog( LOG_ALERT, "Attributes are invalid in URL: %s", url ); break; case LDAP_URL_ERR_BADSCOPE: syslog( LOG_ALERT, "Scope is invalid in URL: %s", url ); break; case LDAP_URL_ERR_BADFILTER: syslog( LOG_ALERT, "Filter is invalid in URL: %s", url ); break; case LDAP_URL_ERR_BADEXTS: syslog( LOG_ALERT, "Extensions are invalid in URL: %s", url ); break; case LDAP_URL_ERR_MEM: syslog( LOG_ALERT, "Out of memory parsing URL: %s", url ); break; case LDAP_URL_ERR_PARAM: syslog( LOG_ALERT, "bad parameter parsing URL: %s", url ); break; default: syslog( LOG_ALERT, "Unknown error %d parsing URL: %s", rc, url ); break; } add_error( err, nerr, E_BADMEMBER, url, NULL ); return 0; } if ( substs ) { for ( s = ludp->lud_filter, d = filter; *s; s++,d++ ) { if ( *s == '%' ) { s++; if ( *s == '%' ) { *d = '%'; continue; } for ( i = 0; substs[i].sub_char != '\0'; i++ ) { if ( *s == substs[i].sub_char ) { for ( p = substs[i].sub_value; *p; p++,d++ ) { *d = *p; } d--; break; } } if ( substs[i].sub_char == '\0' ) { syslog( LOG_ALERT, "unknown format %c", *s ); } } else { *d = *s; } } *d = *s; } else { strncpy( filter, ludp->lud_filter, sizeof( filter ) - 1 ); filter[ sizeof( filter ) - 1 ] = '\0'; } if ( ludp->lud_attrs ) { attrlist = ludp->lud_attrs; } else { attrlist = def_attr; } res = NULL; /* TBC: we don't read the host, dammit */ rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope, filter, attrlist, 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 ); } match = ldap_count_entries( ld, res ); /* trouble - try again later */ if ( match == -1 ) { syslog( LOG_ALERT, "error parsing result from X.500" ); unbind_and_exit( EX_TEMPFAIL ); } if ( match == 1 || multi_entry ) { for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) ) { dn = ldap_get_dn( ld, e ); resolved = entry_engine( e, dn, address, to, nto, togroups, ngroups, err, nerr, type ); if ( !resolved ) { add_error( err, nerr, E_NOEMAIL, address, res ); } } return ( resolved ); } /* 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 ( rdnpref == 0 ) { add_error( err, nerr, E_AMBIGUOUS, address, res ); return 0; } /* * 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], address ) == 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, address, res ); return 0; /* 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, address, tmpres ); return 0; /* 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; /* 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 ); resolved = entry_engine( e, dn, address, to, nto, togroups, ngroups, err, nerr, type ); if ( !resolved ) { add_error( err, nerr, E_NOEMAIL, address, res ); /* Don't free res if we passed it to add_error */ } else { ldap_msgfree( res ); } } return( resolved ); } static int url_list_search( char **urllist, char *address, int multi_entry, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { int i; int resolved = 0; for ( i = 0; urllist[i]; i++ ) { if ( !strncasecmp( urllist[i], "mail:", 5 ) ) { char *vals[2]; vals[0] = urllist[i] + 5; vals[1] = NULL; add_to( to, nto, vals ); resolved = 1; } else if ( ldap_is_ldap_url( urllist[i] ) ) { resolved = search_ldap_url( urllist[i], NULL, address, 0, multi_entry, to, nto, togroups, ngroups, err, nerr, type ); } else { /* Produce some sensible error here */ resolved = 0; } } return( resolved ); } /* * We should probably take MX records into account to cover all bases, * but really, routing belongs in the MTA. */ static int is_my_host( char * host ) { char **d; if ( myhosts == NULL ) return 0; for ( d = myhosts; *d; d++ ) { if ( !strcasecmp(*d,host) ) { return 1; } } return 0; } static int is_my_domain( char * address ) { char **d; char *p; if ( mydomains == NULL ) return 0; p = strchr( address, '@' ); if ( p == NULL) return 0; for ( d = mydomains; *d; d++ ) { if ( !strcasecmp(*d,p+1) ) { return 1; } } return 0; } static void do_addresses( char **addresses, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { int i, j; int n; /* * Well, this is tricky, every address in my_addresses will be * removed from the list while we shift the other values down * and we do it in a single scan of the address list and * without using additional memory. We are going to be * modifying the value list in a way that the later * ldap_value_free works. */ j = 0; for ( i = 0; addresses[i]; i++ ) { if ( is_my_domain(addresses[i]) ) { do_address( addresses[i], to, nto, togroups, ngroups, err, nerr, type ); ldap_memfree( addresses[i] ); } else { if ( j < i ) { addresses[j] = addresses[i]; } j++; } } addresses[j] = NULL; if ( addresses[0] ) { add_to( to, nto, addresses ); } } /* * The entry engine processes an entry. Normally, each entry will resolve * to one or more values that will be added to the 'to' argument. This * argument needs not be the global 'to' list, it may be the g_to field * in a group. Groups have no special treatment, unless they require * a special sender. */ static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { char **vals; int i; int resolved = 0; char ***current_to = to; int *current_nto = nto; Group *current_group = NULL; char buf[1024]; char *localpart = NULL, *domainpart = NULL; Subst substs[2]; int cur_priority = 0; char *route_to_host = NULL; char *route_to_address = NULL; int needs_mta_routing = 0; char **own_addresses = NULL; int own_addresses_total = 0; char **delivery_types = NULL; int delivery_types_total = 0; char *nvals[2]; for ( i=0; attr_semantics[i] != NULL; i++ ) { AttrSemantics *as = attr_semantics[i]; int nent; int j; if ( as->as_priority < cur_priority ) { /* * We already got higher priority information, * so no further work to do, ignore the rest. */ break; } vals = ldap_get_values( ld, e, as->as_name ); if ( !vals || vals[0] == NULL ) { continue; } nent = count_values( vals ); if ( nent > 1 && !as->as_m_valued ) { add_error( err, nerr, E_AMBIGUOUS, address, e ); return( 0 ); } switch ( as->as_kind ) { case AS_KIND_RECIPIENT: cur_priority = as->as_priority; if ( ! ( type & ( USER | GROUP_MEMBERS ) ) ) break; switch ( as->as_syntax ) { case AS_SYNTAX_RFC822: do_addresses( vals, current_to, current_nto, togroups, ngroups, err, nerr, USER ); resolved = 1; break; case AS_SYNTAX_RFC822_EXT: do_addresses( vals, current_to, current_nto, togroups, ngroups, err, nerr, USER ); resolved = 1; break; case AS_SYNTAX_NATIVE_MB: /* We used to concatenate mailHost if set here */ /* * We used to send a copy to the vacation host * if onVacation to uid@vacationhost */ if ( as->as_param ) { for ( j=0; jas_param, delivery_types[j] ) ) { add_to( current_to, current_nto, vals ); resolved = 1; break; } } } else { add_to( current_to, current_nto, vals ); resolved = 1; } break; case AS_SYNTAX_DN: if ( dn_search( vals, address, current_to, current_nto, togroups, ngroups, err, nerr ) ) { resolved = 1; } break; case AS_SYNTAX_URL: if ( url_list_search( vals, address, as->as_m_entries, current_to, current_nto, togroups, ngroups, err, nerr, type ) ) { resolved = 1; } break; case AS_SYNTAX_BOOL_FILTER: if ( strcasecmp( vals[0], "true" ) ) { break; } substs[0].sub_char = 'D'; substs[0].sub_value = dn; substs[1].sub_char = '\0'; substs[1].sub_value = NULL; if ( url_list_search( vals, address, as->as_m_entries, current_to, current_nto, togroups, ngroups, err, nerr, type ) ) { resolved = 1; } break; default: syslog( LOG_ALERT, "Invalid syntax %d for kind %d", as->as_syntax, as->as_kind ); break; } break; case AS_KIND_ERRORS: cur_priority = as->as_priority; /* This is a group with special processing */ if ( type & GROUP_ERRORS ) { switch (as->as_kind) { case AS_SYNTAX_RFC822: add_to( current_to, current_nto, vals ); resolved = 1; break; case AS_SYNTAX_URL: default: syslog( LOG_ALERT, "Invalid syntax %d for kind %d", as->as_syntax, as->as_kind ); } } else { current_group = new_group( dn, togroups, ngroups ); current_to = ¤t_group->g_members; current_nto = ¤t_group->g_nmembers; split_address( address, &localpart, &domainpart ); if ( domainpart ) { sprintf( buf, "%s-%s@%s", localpart, ERRORS, domainpart ); free( localpart ); free( domainpart ); } else { sprintf( buf, "%s-%s@%s", localpart, ERRORS, host ); free( localpart ); } current_group->g_errorsto = strdup( buf ); } break; case AS_KIND_REQUEST: cur_priority = as->as_priority; /* This is a group with special processing */ if ( type & GROUP_REQUEST ) { add_to( current_to, current_nto, vals ); resolved = 1; } break; case AS_KIND_OWNER: cur_priority = as->as_priority; /* This is a group with special processing */ if ( type & GROUP_REQUEST ) { add_to( current_to, current_nto, vals ); resolved = 1; } break; case AS_KIND_ROUTE_TO_HOST: if ( !is_my_host( vals[0] ) ) { cur_priority = as->as_priority; if ( as->as_syntax == AS_SYNTAX_PRESENT ) { needs_mta_routing = 1; } else { route_to_host = strdup( vals[0] ); } } break; case AS_KIND_ROUTE_TO_ADDR: for ( j=0; jas_priority; if ( as->as_syntax == AS_SYNTAX_PRESENT ) { needs_mta_routing = 1; } else { route_to_address = strdup( vals[0] ); } } break; } case AS_KIND_OWN_ADDR: add_to( &own_addresses, &own_addresses_total, vals ); cur_priority = as->as_priority; break; case AS_KIND_DELIVERY_TYPE: add_to( &delivery_types, &delivery_types_total, vals ); cur_priority = as->as_priority; break; default: syslog( LOG_ALERT, "Invalid kind %d", as->as_kind ); /* Error, TBC */ } ldap_value_free( vals ); } /* * Now check if we are dealing with mail routing. We support * two modes. * * The first mode and by far the most robust method is doing * routing at the MTA. In this case, we just checked if the * routing attributes were present and did not seem like * pointing to ourselves. The only thing we have to do here * is adding to the recipient list any of the RFC822 addresses * of this entry. That means we needed to retrieve them from * the entry itself because we might have arrived here through * some directory search. The address received as argument is * not the address of the entry we are processing, but rather * the RFC822 address we are expanding now. Unfortunately, * this requires an MTA that understands LDAP routing. * Sendmail 8.10.0 does, if compiled properly. * * The second method, that is most emphatically not recommended * is routing in maildap. This is going to require using the * percent hack. Moreover, this may occasionally loop. */ if ( needs_mta_routing ) { if ( !own_addresses ) { add_error( err, nerr, E_NOOWNADDRESS, address, e ); return( 0 ); } nvals[0] = own_addresses[0]; /* Anyone will do */ nvals[1] = NULL; add_to( current_to, current_nto, nvals ); resolved = 1; } else if ( route_to_host ) { char *p; if ( !route_to_address ) { if ( !own_addresses ) { add_error( err, nerr, E_NOOWNADDRESS, address, e ); return( 0 ); } route_to_address = strdup( own_addresses[0] ); } /* This makes use of the percent hack, but there's no choice */ p = strchr( route_to_address, '@' ); if ( p ) { *p = '%'; } sprintf( buf, "%s@%s", route_to_address, route_to_host ); nvals[0] = buf; nvals[1] = NULL; add_to( current_to, current_nto, nvals ); resolved = 1; free( route_to_host ); free( route_to_address ); } else if ( route_to_address ) { nvals[0] = route_to_address; nvals[1] = NULL; add_to( current_to, current_nto, nvals ); resolved = 1; free( route_to_address ); } if ( own_addresses ) { ldap_value_free( own_addresses ); } if ( delivery_types ) { ldap_value_free( delivery_types ); } return( resolved ); } static int search_bases( char *filter, Subst *substs, char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { int b, resolved = 0; for ( b = 0; base[b] != NULL; b++ ) { if ( ! (base[b]->b_search & type) ) { continue; } resolved = search_ldap_url( base[b]->b_url, substs, name, base[b]->b_rdnpref, base[b]->b_m_entries, to, nto, togroups, ngroups, err, nerr, type ); if ( resolved ) break; } return( resolved ); } static void do_address( char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type ) { char *localpart = NULL, *domainpart = NULL; char *synthname = NULL; int resolved; int i; Subst substs[6]; /* * 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. */ split_address( name, &localpart, &domainpart ); synthname = strdup( localpart ); for ( i = 0; synthname[i] != '\0'; i++ ) { if ( synthname[i] == '.' || synthname[i] == '_' ) synthname[i] = ' '; } substs[0].sub_char = 'm'; substs[0].sub_value = name; substs[1].sub_char = 'h'; substs[1].sub_value = host; substs[2].sub_char = 'l'; substs[2].sub_value = localpart; substs[3].sub_char = 'd'; substs[3].sub_value = domainpart; substs[4].sub_char = 's'; substs[4].sub_value = synthname; substs[5].sub_char = '\0'; substs[5].sub_value = NULL; resolved = search_bases( NULL, substs, name, to, nto, togroups, ngroups, err, nerr, type ); if ( localpart ) { free( localpart ); } if ( domainpart ) { free( domainpart ); } if ( synthname ) { free( synthname ); } if ( !resolved ) { /* not resolved - bounce with user unknown */ if ( type == USER ) { add_error( err, nerr, E_USERUNKNOWN, name, NULL ); } else { add_error( err, nerr, E_GROUPUNKNOWN, name, NULL ); } } } 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; case E_NOOWNADDRESS: fprintf( fp, "%s: Not enough information to perform required routing\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((unsigned char) 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((unsigned char) 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((unsigned char) 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" ); } */ vals = ldap_get_values( ld, e, "description" ); 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 void add_single_to( char ***list, char *new ) { int nlist; if ( *list == NULL ) { nlist = 0; *list = (char **) malloc( 2 * sizeof(char *) ); } else { nlist = count_values( *list ); *list = (char **) realloc( *list, ( nlist + 2 ) * sizeof(char *) ); } (*list)[nlist] = strdup( new ); (*list)[nlist+1] = NULL; } static int isgroup( LDAPMessage *e ) { int i, j; char **oclist; if ( !groupclasses ) { return( 0 ); } oclist = ldap_get_values( ld, e, "objectClass" ); for ( i = 0; oclist[i] != NULL; i++ ) { for ( j = 0; groupclasses[j] != NULL; j++ ) { if ( strcasecmp( oclist[i], groupclasses[j] ) == 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 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 ); }