static void close_sn( struct connlist *cl, void *s ) { char *line; struct timeval tv; /* Close network connection */ if (( snet_writef( cl->conn_sn, "QUIT\r\n" )) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_writef failed" ); goto finish; } tv = timeout; if ( ( line = snet_getline_multi( cl->conn_sn, logger, &tv ) ) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_getline_multi failed" ); goto finish; } if ( *line != '2' ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: %s", line ); } finish: if ( snet_close( cl->conn_sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: close_sn: snet_close failed" ); } cl->conn_sn = NULL; return; }
int env_jail_status( struct envelope *env, int jail ) { SNET *snet_lock; int rc; if ( env == NULL ) { return( 0 ); } if (( jail != ENV_JAIL_PRISONER ) && ( jail != ENV_JAIL_PAROLEE )) { syslog( LOG_ERR, "Envelope.jail env <%s>: illegal jail code: %d", env->e_id, jail ); return( 1 ); } if (( env->e_jail != ENV_JAIL_PRISONER ) && ( env->e_jail != ENV_JAIL_PAROLEE )) { syslog( LOG_ERR, "Envelope.jail env <%s>: illegal env code: %d", env->e_id, env->e_jail ); return( 1 ); } if ( env->e_jail == jail ) { return( 0 ); } if ( env->e_hq != NULL ) { if ( jail == ENV_JAIL_PRISONER ) { env->e_hq->hq_jail_envs--; } else { env->e_hq->hq_jail_envs++; } } if ( env_read( READ_JAIL_INFO, env, &snet_lock ) != 0 ) { return( 0 ); } env_jail_set( env, jail ); rc = env_outfile( env ); if ( snet_close( snet_lock ) < 0 ) { syslog( LOG_ERR, "Liberror: env_jail_status snet_close: %m" ); } if ( rc != 0 ) { return( 1 ); } env_rcpt_free( env ); syslog( LOG_INFO, "Envelope.jail %s: %s", env->e_id, ( jail == ENV_JAIL_PRISONER ) ? "immured in durance vile" : "paroled" ); return( 0 ); }
int main( int argc, char *argv[]) { int fd; SNET *snet; char *line; u_int line_len; struct message_digest md; if ( argc != 3 ) { fprintf( stderr, "Usage: %s <checksum_algorithm> <file>\n", argv[ 0 ]); return( 1 ); } /* OpenSSL 1.1.0 added auto-init */ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) OpenSSL_add_all_digests(); #endif /* OpenSSL < 1.1.0 */ simta_checksum_md = EVP_get_digestbyname((const char*)(argv[ 1 ])); if ( simta_checksum_md == NULL ) { fprintf( stderr, "%s: unknown checksum algorithm\n", argv[ 1 ]); return( 1 ); } md_init( &md ); md_reset( &md ); if (( fd = open( argv[ 2 ], O_RDONLY, 0 )) < 0 ) { perror( "open" ); exit( 1 ); } if (( snet = snet_attach( fd, 1024 * 1024 )) == NULL ) { perror( "snet_attach" ); exit( 1 ); } while (( line = snet_getline( snet, NULL )) != NULL ) { line_len = strlen( line ); md_update( &md, line, line_len ); } md_finalize( &md ); md_cleanup( &md ); if ( snet_close( snet ) != 0 ) { perror( "snet_close" ); return( 1 ); } printf( "\nChecksum: %s\n", md.md_b16 ); return( 0 ); }
/* sets command file for connected host */ int command_k( char *path_config, int depth ) { SNET *sn; char **av, *line, *p; char *valid_host; char temp[ MAXPATHLEN ]; int ac; int rc = -1; int linenum = 0; if (( sn = snet_open( path_config, O_RDONLY, 0, 0 )) == NULL ) { syslog( LOG_ERR, "command_k: snet_open: %s: %m", path_config ); return( -1 ); } while (( line = snet_getline( sn, NULL )) != NULL ) { linenum++; if (( ac = argcargv( line, &av )) < 0 ) { syslog( LOG_ERR, "argvargc: %m" ); goto command_k_done; } if ( ( ac == 0 ) || ( *av[ 0 ] == '#' ) ) { continue; } if ( ac < 2 ) { syslog( LOG_ERR, "%s: line %d: invalid number of arguments", path_config, linenum ); continue; } if ( strcmp( av[ 0 ], "@include" ) == 0 ) { if ( depth >= RADMIND_MAX_INCLUDE_DEPTH ) { syslog( LOG_ERR, "%s: line %d: include %s exceeds max depth", path_config, linenum, av[ 1 ] ); goto command_k_done; } if ( ac > 3 ) { syslog( LOG_ERR, "%s: line %d: invalid number of arguments", path_config, linenum ); continue; } else if ( ac == 3 ) { if ( match_config_entry( av[ 2 ] ) == NULL ) { /* connecting host doesn't match pattern, skip include. */ continue; } } if ( command_k( av[ 1 ], depth + 1 ) != 0 ) { continue; } rc = 0; goto command_k_done; } if (( ac > 2 ) && ( *av[ 2 ] != '#' )) { syslog( LOG_ERR, "%s: line %d: invalid number of arguments", path_config, linenum ); continue; } if (( p = strrchr( av[ 1 ], '/' )) == NULL ) { sprintf( special_dir, "special" ); } else { *p = '\0'; if ( snprintf( special_dir, MAXPATHLEN, "special/%s", av[ 1 ] ) >= MAXPATHLEN ) { syslog( LOG_ERR, "config file: line %d: path too long\n", linenum ); continue; } *p = '/'; } if (( valid_host = match_config_entry( av[ 0 ] )) != NULL ) { if ( strlen( av[ 1 ] ) >= MAXPATHLEN ) { syslog( LOG_ERR, "config file: line %d: command file too long\n", linenum ); continue; } strcpy( command_file, av[ 1 ] ); if ( snprintf( temp, MAXPATHLEN, "%s/%s", special_dir, valid_host ) >= MAXPATHLEN ) { syslog( LOG_ERR, "config file: line %d: special dir too long\n", linenum ); continue; } strcpy( special_dir, temp ); rc = 0; goto command_k_done; } } /* If we get here, the host that connected is not in the config file. So screw him. */ syslog( LOG_ERR, "host %s not in config file %s", remote_host, path_config ); command_k_done: snet_close( sn ); return( rc ); }
int read_scookie( char *path, struct sinfo *si, void *s ) { SNET *sn; struct stat st; char *p, *line; memset( si, 0, sizeof( struct sinfo )); if (( sn = snet_open( path, O_RDONLY, 0, 0 )) == NULL ) { if ( errno != ENOENT ) { perror( path ); } return( 1 ); } if ( fstat( snet_fd( sn ), &st ) != 0 ) { (void)snet_close( sn ); perror( path ); return( -1 ); } si->si_itime = st.st_mtime; while (( line = snet_getline( sn, NULL )) != NULL ) { p = line + 1; switch( line[0] ) { case 'v': errno = 0; si->si_protocol = strtol( p, (char **)NULL, 10 ); if ( errno ) { cosign_log( APLOG_NOTICE, s, "mod_cosign: read_scookie: " "invalid protocol version %s, " "falling back to protocol v0.", p ); si->si_protocol = 0; } break; case 'i': strcpy( si->si_ipaddr, p ); break; case 'p': strcpy( si->si_user, p ); break; case 'r': strcpy( si->si_realm, p ); break; case 'f': strcpy( si->si_factor, p ); break; #ifdef KRB case 'k': strcpy( si->si_krb5tkt, p ); break; #endif /* KRB */ default: cosign_log( APLOG_ERR, s, "mod_cosign: read_scookie: unknown key %c", line[0] ); (void)snet_close( sn ); return( -1 ); } } if ( snet_close( sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: read_scookie: %s", path ); return( -1 ); } return( 0 ); }
int env_read( int mode, struct envelope *env, SNET **s_lock ) { char *line; SNET *snet; char filename[ MAXPATHLEN + 1 ]; char *hostname; int ret = 1; ino_t dinode; int version; int exp_level; int jail; int line_no = 1; struct dll_entry *e_dll; switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read unknown mode: %d", mode ); return( 1 ); case READ_QUEUE_INFO: if ( s_lock != NULL ) { syslog( LOG_ERR, "Envelope.read no lock allowed in READ_QUEUE_INFO mode" ); return( 1 ); } break; case READ_DELIVER_INFO: case READ_JAIL_INFO: break; } sprintf( filename, "%s/E%s", env->e_dir, env->e_id ); if (( snet = snet_open( filename, O_RDWR, 0, 1024 * 1024 )) == NULL ) { if ( errno != ENOENT ) { syslog( LOG_ERR, "Syserror: env_read snet_open %s: %m", filename ); } return( 1 ); } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read invalid mode change: %d", mode ); goto cleanup; case READ_QUEUE_INFO: /* test to see if env is locked by a q_runner */ if ( lockf( snet_fd( snet ), F_TEST, 0 ) != 0 ) { syslog( LOG_ERR, "Syserror: env_read lockf %s: %m", filename ); goto cleanup; } break; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( s_lock != NULL ) { *s_lock = snet; /* lock envelope fd */ if ( lockf( snet_fd( snet ), F_TLOCK, 0 ) != 0 ) { if ( errno != EAGAIN ) { /* file not locked by a diferent process */ syslog( LOG_ERR, "Syserror: env_read lockf %s: %m", filename ); } goto cleanup; } } break; } /* Vsimta-version */ if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'V' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected version syntax", filename, line_no ); goto cleanup; } sscanf( line + 1, "%d", &version ); if (( version < 1 ) || ( version > SIMTA_EFILE_VERSION )) { syslog( LOG_ERR, "Envelope.read %s %d: unsupported efile version %d", filename, line_no, version ); goto cleanup; } if ( version >= 2 ) { /* Emessage-id */ line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'E' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Equeue-id syntax", filename, line_no ); goto cleanup; } if ( strcmp( line + 1, env->e_id ) != 0 ) { syslog( LOG_WARNING, "Envelope.read %s %d: queue-id mismatch: %s", filename, line_no, line + 1 ); goto cleanup; } } line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } /* ignore optional M for now */ if ( *line == 'M' ) { line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } } /* Dinode info */ if ( *line != 'I' ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Dinode syntax", filename, line_no ); goto cleanup; } sscanf( line + 1, "%lu", &dinode ); switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read invalid mode change: %d", mode ); goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( dinode != env->e_dinode ) { syslog( LOG_WARNING, "Envelope.read %s %d: Dinode reread mismatch: " "old %d new %d, ignoring", filename, line_no, (int)env->e_dinode, (int)dinode ); } break; case READ_QUEUE_INFO: if ( dinode == 0 ) { syslog( LOG_WARNING, "Envelope.read %s %d: Dinode is 0", filename, line_no ); } env->e_dinode = dinode; break; } /* expansion info */ if ( version >= 3 ) { line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'X' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Xpansion syntax", filename, line_no ); goto cleanup; } if ( sscanf( line + 1, "%d", &exp_level) != 1 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad Xpansion syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( exp_level == env->e_n_exp_level ) { break; } syslog( LOG_WARNING, "Envelope.read %s %d: Xpansion mismatch: " "old %d new %d, ignoring", filename, line_no, env->e_n_exp_level, exp_level ); break; case READ_QUEUE_INFO: env->e_n_exp_level = exp_level; break; } } /* Jail info */ if ( version >= 5 ) { line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'J' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Jail syntax", filename, line_no ); goto cleanup; } if ( sscanf( line + 1, "%d", &jail) != 1 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad Jail syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( env->e_jail == jail ) { break; } syslog( LOG_WARNING, "Envelope.read %s %d: Jail mismatch: " "old %d new %d, ignoring", filename, line_no, env->e_jail, jail ); break; case READ_QUEUE_INFO: env_jail_set( env, jail ); break; } } line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'H' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected host syntax", filename, line_no ); goto cleanup; } hostname = line + 1; switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_DELIVER_INFO: case READ_JAIL_INFO: if ( env->e_hostname == NULL ) { if ( *hostname != '\0' ) { syslog( LOG_ERR, "Envelope.read %s %d: hostname reread mismatch, " "old \"\" new \"%s\"", filename, line_no, hostname ); goto cleanup; } } else if ( strcasecmp( hostname, env->e_hostname ) != 0 ) { syslog( LOG_ERR, "Envelope.read %s %d: hostname reread mismatch, " "old \"%s\" new \"%s\"", filename, line_no, env->e_hostname, hostname ); goto cleanup; } break; case READ_QUEUE_INFO: if ( env_hostname( env, hostname ) != 0 ) { goto cleanup; } break; } /* Dattributes */ if ( version >= 4 ) { line_no++; if (( line = snet_getline( snet, NULL )) == NULL ) { syslog( LOG_ERR, "Envelope.read %s: unexpected EOF", filename ); goto cleanup; } if ( *line != 'D' ) { syslog( LOG_ERR, "Envelope.read %s: expected Dattributes syntax", filename ); goto cleanup; } if ( sscanf( line + 1, "%d", &exp_level) != 1 ) { syslog( LOG_ERR, "Envelope.read %s: bad Dattributes syntax", filename ); goto cleanup; } if ( mode == READ_QUEUE_INFO ) { env->e_attributes = exp_level; } else if ( exp_level != env->e_attributes ) { syslog( LOG_WARNING, "Envelope.read %s: " "Dattributes reread mismatch old %d new %d", filename, env->e_attributes, exp_level ); } } /* Ffrom-address */ line_no++; if ((( line = snet_getline( snet, NULL )) == NULL ) || ( *line != 'F' )) { syslog( LOG_ERR, "Envelope.read %s %d: expected Ffrom syntax", filename, line_no ); goto cleanup; } switch ( mode ) { default: syslog( LOG_ERR, "Envelope.read: invalid mode change: %d", mode ); goto cleanup; case READ_QUEUE_INFO: if ( env_sender( env, line + 1 ) == 0 ) { ret = 0; } goto cleanup; case READ_JAIL_INFO: case READ_DELIVER_INFO: if ( strcmp( env->e_mail, line + 1 ) != 0 ) { syslog( LOG_ERR, "Envelope.read %s %d: bad sender re-read: " "old <%s> new <%s>", filename, line_no, env->e_mail, line + 1 ); goto cleanup; } break; } /* Rto-addresses */ for ( line_no++; ( line = snet_getline( snet, NULL )) != NULL; line_no++ ) { if ( *line != 'R' ) { syslog( LOG_ERR, "Envelope.read %s %d: expected Recipient syntax", filename, line_no ); goto cleanup; } if ( env_recipient( env, line + 1 ) != 0 ) { goto cleanup; } } if ( env->e_rcpt == NULL ) { syslog( LOG_ERR, "Envelope.read %s %d: no recipients", filename, line_no ); goto cleanup; } ret = 0; /* close snet if no need to maintain lock */ if ( s_lock == NULL ) { cleanup: if ( snet_close( snet ) < 0 ) { syslog( LOG_ERR, "Liberror: env_read snet_close %s: %m", filename ); ret = 1; } } if (( simta_mid_list_enable != 0 ) && ( ret == 0 )) { if (( e_dll = dll_lookup_or_create( &simta_env_list, env->e_id, 0 )) == NULL ) { return( 1 ); } if ( e_dll->dll_data == NULL ) { e_dll->dll_data = env; env->e_env_list_entry = e_dll; } } if (( simta_sender_list_enable != 0 ) && ( ret == 0 )) { if ( sender_list_add( env ) != 0 ) { return( 1 ); } } return( ret ); }
yastr env_dkim_sign( struct envelope *env ) { char df[ MAXPATHLEN + 1 ]; DKIM_LIB *libhandle; unsigned int flags; DKIM *dkim; DKIM_STAT result; yastr signature = NULL; yastr key = NULL; yastr tmp = NULL; yastr *split = NULL; size_t tok_count = 0; char buf[ 1024 * 1024 ]; unsigned char *dkim_header; SNET *snet; ssize_t chunk; sprintf( df, "%s/D%s", env->e_dir, env->e_id ); if (( snet = snet_open( simta_dkim_key, O_RDONLY, 0, 1024 * 1024 )) == NULL ) { syslog( LOG_ERR, "Liberror: env_dkim_sign snet_open %s: %m", simta_dkim_key ); return( NULL ); } key = yaslempty(); while (( chunk = snet_read( snet, buf, 1024 * 1024, NULL )) > 0 ) { key = yaslcatlen( key, buf, chunk ); } snet_close( snet ); if (( libhandle = dkim_init( NULL, NULL )) == NULL ) { syslog( LOG_ERR, "Liberror: env_dkim_sign dkim_init" ); return( NULL ); } /* Data is stored in UNIX format, so tell libopendkim to fix * CRLF issues. */ flags = DKIM_LIBFLAGS_FIXCRLF; dkim_options( libhandle, DKIM_OP_SETOPT, DKIM_OPTS_FLAGS, &flags, sizeof( flags )); /* Only sign the headers recommended by RFC 6376 */ dkim_options( libhandle, DKIM_OP_SETOPT, DKIM_OPTS_SIGNHDRS, dkim_should_signhdrs, sizeof( unsigned char ** )); if (( dkim = dkim_sign( libhandle, (unsigned char *)(env->e_id), NULL, (unsigned char *)key, (unsigned char *)simta_dkim_selector, (unsigned char *)simta_dkim_domain, DKIM_CANON_RELAXED, DKIM_CANON_RELAXED, DKIM_SIGN_RSASHA256, -1, &result )) == NULL ) { syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_sign: %s", dkim_getresultstr( result )); goto error; } if (( snet = snet_open( df, O_RDONLY, 0, 1024 * 1024 )) == NULL ) { syslog( LOG_ERR, "Liberror: env_dkim_sign snet_open %s: %m", buf ); goto error; } while (( chunk = snet_read( snet, buf, 1024 * 1024, NULL )) > 0 ) { if (( result = dkim_chunk( dkim, (unsigned char *)buf, chunk )) != 0 ) { syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_chunk: %s: %s", dkim_getresultstr( result ), dkim_geterror( dkim )); snet_close( snet ); goto error; } } snet_close( snet ); if (( result = dkim_chunk( dkim, NULL, 0 )) != 0 ) { syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_chunk: %s: %s", dkim_getresultstr( result ), dkim_geterror( dkim )); goto error; } if (( result = dkim_eom( dkim, NULL )) != 0 ) { syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_eom: %s: %s", dkim_getresultstr( result ), dkim_geterror( dkim )); goto error; } if (( result = dkim_getsighdr_d( dkim, 16, &dkim_header, (size_t *)&chunk )) != 0 ) { syslog( LOG_NOTICE, "Liberror: env_dkim_sign dkim_getsighdr_d: %s: %s", dkim_getresultstr( result ), dkim_geterror( dkim )); goto error; } /* Get rid of carriage returns in libopendkim output */ split = yaslsplitlen( (const char *)dkim_header, strlen( (const char *)dkim_header ), "\r", 1, &tok_count ); tmp = yasljoinyasl( split, tok_count, "", 0 ); signature = yaslcatyasl( yaslauto( "DKIM-Signature: " ), tmp ); error: yaslfree( tmp ); yaslfreesplitres( split, tok_count ); yaslfree( key ); dkim_free( dkim ); dkim_close( libhandle ); return( signature ); }
ino_t env_dfile_copy( struct envelope *env, char *source, char *header ) { int dfile_fd = -1; ino_t retval = 0; FILE *dfile = NULL; struct stat sbuf; SNET *snet = NULL; char *line; char df[ MAXPATHLEN + 1 ]; /* If the tfile has already been written it has incorrect Dinode * information. */ if ( env->e_flags & ENV_FLAG_TFILE ) { env_tfile_unlink( env ); } if ( source == NULL ) { if ( ! ( env->e_flags & ENV_FLAG_DFILE )) { syslog( LOG_ERR, "env_dfile_copy: no source" ); return( 0 ); } sprintf( df, "%s/D%s", env->e_dir, env->e_id ); if (( snet = snet_open( df, O_RDONLY, 0, 1024 * 1024 )) != NULL ) { if ( unlink( df )) { syslog( LOG_ERR, "Syserror: env_dfile_copy unlink %s: %m", df ); goto error; } } } else { snet = snet_open( source, O_RDONLY, 0, 1024 * 1024 ); } if ( snet == NULL ) { syslog( LOG_ERR, "Liberror: env_dfile_copy snet_open: %m" ); return( 0 ); } if (( dfile_fd = env_dfile_open( env )) < 0 ) { goto error; } if (( dfile = fdopen( dfile_fd, "w" )) == NULL ) { syslog( LOG_ERR, "Syserror: env_dfile_copy fdopen: %m" ); if ( close( dfile_fd ) != 0 ) { syslog( LOG_ERR, "Syserror: env_dfile_copy close: %m" ); } goto error; } if ( header ) { if ( fprintf( dfile, "%s\n", header ) < 0 ) { syslog( LOG_ERR, "Syserror: env_dfile_copy fprintf: %m" ); goto error; } } while (( line = snet_getline( snet, NULL )) != NULL ) { if ( fprintf( dfile, "%s\n", line ) < 0 ) { syslog( LOG_ERR, "Syserror: env_dfile_copy fprintf: %m" ); goto error; } } if ( fstat( dfile_fd, &sbuf ) == 0 ) { retval = sbuf.st_ino; } else { syslog( LOG_ERR, "Syserror: env_dfile_copy fstat: %m" ); } error: if ( dfile != NULL && ( fclose( dfile ) != 0 )) { syslog( LOG_ERR, "Syserror: env_dfile_copy fclose: %m" ); } if ( snet != NULL && ( snet_close( snet ) != 0 )) { syslog( LOG_ERR, "Liberror: env_dfile_copy snet_close: %m" ); } if ( retval == 0 ) { env_dfile_unlink( env ); } return( retval ); }
int expand( struct envelope *unexpanded_env ) { struct expand exp; struct envelope *base_error_env; struct envelope *env_dead = NULL; struct envelope *env; struct envelope **env_p; struct recipient *rcpt; struct expand_output *host_stab = NULL; struct expand_output *eo; struct expand_output *eo_free; struct exp_addr *e_addr; struct exp_addr *next_e_addr; struct simta_red *hq_red; char *domain; SNET *snet = NULL; int n_rcpts; int return_value = 1; int env_out = 0; int fast_file_start; int sendermatch; char e_original[ MAXPATHLEN ]; char d_original[ MAXPATHLEN ]; char d_out[ MAXPATHLEN ]; /* RFC 5321 4.5.3.1.3. Path * The maximum total length of a reverse-path or forward-path is 256 * octets (including the punctuation and element separators). */ char header[ 270 ]; #ifdef HAVE_LDAP char *p; int loop_color = 1; struct exp_link *memonly; struct exp_link *parent; #endif /* HAVE_LDAP */ if ( unexpanded_env->e_hostname != NULL ) { syslog( LOG_INFO, "Expand env <%s>: already expanded for host %s", unexpanded_env->e_id, unexpanded_env->e_hostname ); return_value = 0; goto done; } memset( &exp, 0, sizeof( struct expand )); exp.exp_env = unexpanded_env; fast_file_start = simta_fast_files; /* call address_expand on each address in the expansion list. * * if an address is expandable, the address(es) that it expands to will * be added to the expansion list. These non-terminal addresses must * have their st_data set to NULL to specify that they are not to be * included in the terminal expansion list. * * Any address in the expansion list whose st_data is not NULL is * considered a terminal address and will be written out as one * of the addresses in expanded envelope(s). */ if (( base_error_env = address_bounce_create( &exp )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: address_bounce_create: %m", unexpanded_env->e_id ); goto done; } if ( env_recipient( base_error_env, unexpanded_env->e_mail ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: env_recipient: %m", unexpanded_env->e_id ); goto done; } /* add all of the original recipients to the expansion list */ for ( rcpt = unexpanded_env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { if ( add_address( &exp, rcpt->r_rcpt, base_error_env, ADDRESS_TYPE_EMAIL, exp.exp_env->e_mail ) != 0 ) { /* add_address syslogs errors */ goto cleanup1; } } /* process the expansion list */ for ( exp.exp_addr_cursor = exp.exp_addr_head; exp.exp_addr_cursor != NULL; exp.exp_addr_cursor = exp.exp_addr_cursor->e_addr_next ) { switch ( address_expand( &exp )) { case ADDRESS_EXCLUDE: exp.exp_addr_cursor->e_addr_terminal = 0; /* the address is not a terminal local address */ break; case ADDRESS_FINAL: exp.exp_addr_cursor->e_addr_terminal = 1; break; case ADDRESS_SYSERROR: goto cleanup1; default: panic( "Expand: address_expand out of range" ); } } #ifdef HAVE_LDAP /* Members-only processing */ for ( memonly = exp.exp_memonly; memonly != NULL; memonly = memonly->el_next ) { if ((( p = parent_permitted( memonly->el_exp_addr )) != NULL ) || ( sender_is_child( memonly->el_exp_addr->e_addr_children, loop_color++ ))) { if ( p != NULL ) { syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: " "parent %s permitted", unexpanded_env->e_id, memonly->el_exp_addr->e_addr, p ); } else { syslog( LOG_INFO, "Expand env <%s>: members-only group %s OK: " "sender is child", unexpanded_env->e_id, memonly->el_exp_addr->e_addr ); } memonly->el_exp_addr->e_addr_ldap_flags = ( memonly->el_exp_addr->e_addr_ldap_flags & ( ~STATUS_LDAP_MEMONLY )); if ( memonly->el_exp_addr->e_addr_env_moderated != NULL ) { env_free( memonly->el_exp_addr->e_addr_env_moderated ); memonly->el_exp_addr->e_addr_env_moderated = NULL; } } else { syslog( LOG_NOTICE, "Expand env <%s>: members-only group %s suppressed", unexpanded_env->e_id, memonly->el_exp_addr->e_addr ); memonly->el_exp_addr->e_addr_ldap_flags |= STATUS_LDAP_SUPPRESSOR; suppress_addrs( memonly->el_exp_addr->e_addr_children, loop_color++ ); } } #endif /* HAVE_LDAP */ sprintf( d_original, "%s/D%s", unexpanded_env->e_dir, unexpanded_env->e_id ); /* Create one expanded envelope for every host we expanded address for */ for ( e_addr = exp.exp_addr_head; e_addr != NULL; e_addr = e_addr->e_addr_next ) { #ifdef HAVE_LDAP if ((( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSED ) != 0 ) && ( !unblocked_path_to_root( e_addr, loop_color++ ))) { if ( simta_expand_debug != 0 ) { printf( "Suppressed: %s\n", e_addr->e_addr ); } continue; } if ( e_addr->e_addr_env_gmailfwd != NULL ) { e_addr->e_addr_env_gmailfwd->e_attributes = unexpanded_env->e_attributes | ENV_ATTR_ARCHIVE_ONLY; if ( simta_expand_debug != 0 ) { printf( "Group mail forwarding: %s\n", e_addr->e_addr ); env_stdout( e_addr->e_addr_env_gmailfwd ); continue; } sprintf( d_out, "%s/D%s", e_addr->e_addr_env_gmailfwd->e_dir, e_addr->e_addr_env_gmailfwd->e_id ); if ( env_dfile_copy( e_addr->e_addr_env_gmailfwd, d_original, NULL ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: group mail env %s dinode %d", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, (int)e_addr->e_addr_env_gmailfwd->e_dinode ); sendermatch = !strcasecmp( unexpanded_env->e_mail, e_addr->e_addr_env_gmailfwd->e_mail ); n_rcpts = 0; for ( rcpt = e_addr->e_addr_env_gmailfwd->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt, e_addr->e_addr_env_gmailfwd->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, rcpt->r_rcpt, e_addr->e_addr_env_gmailfwd->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d group mail forwarders", unexpanded_env->e_id, e_addr->e_addr_env_gmailfwd->e_id, n_rcpts ); if ( env_outfile( e_addr->e_addr_env_gmailfwd ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup3; } env_out++; queue_envelope( e_addr->e_addr_env_gmailfwd ); continue; } if ( e_addr->e_addr_env_moderated != NULL ) { e_addr->e_addr_env_moderated->e_attributes = unexpanded_env->e_attributes; if ( simta_expand_debug != 0 ) { printf( "Moderated: %s\n", e_addr->e_addr ); env_stdout( e_addr->e_addr_env_moderated ); continue; } sprintf( d_out, "%s/D%s", e_addr->e_addr_env_moderated->e_dir, e_addr->e_addr_env_moderated->e_id ); if ( env_dfile_copy( e_addr->e_addr_env_moderated, d_original, NULL ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: %s: moderation env dinode %d", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, (int)e_addr->e_addr_env_moderated->e_dinode ); sendermatch = !strcasecmp( unexpanded_env->e_mail, e_addr->e_addr_env_moderated->e_mail ); n_rcpts = 0; for ( rcpt = e_addr->e_addr_env_moderated->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt, e_addr->e_addr_env_moderated->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, rcpt->r_rcpt, e_addr->e_addr_env_moderated->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d moderators", unexpanded_env->e_id, e_addr->e_addr_env_moderated->e_id, n_rcpts ); if ( env_outfile( e_addr->e_addr_env_moderated ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "expand unlink %s: %m", d_out ); } goto cleanup3; } env_out++; queue_envelope( e_addr->e_addr_env_moderated ); continue; } else if ( e_addr->e_addr_ldap_flags & STATUS_LDAP_SUPPRESSOR ) { for ( parent = e_addr->e_addr_parents; parent != NULL; parent = parent->el_next ) { if ( parent->el_exp_addr == NULL ) { if ( bounce_text( base_error_env, TEXT_ERROR, "Members only group conditions not met: ", e_addr->e_addr, NULL ) != 0 ) { goto cleanup3; } if ( bounce_text( base_error_env, TEXT_ERROR, "If you have any questions, please contact the group owner: ", e_addr->e_addr_owner, NULL ) != 0 ) { goto cleanup3; } } else if (( e_addr->e_addr_ldap_flags & STATUS_LDAP_PRIVATE ) == 0 ) { if ( bounce_text( parent->el_exp_addr->e_addr_errors, TEXT_ERROR, "Members only group conditions not met: ", e_addr->e_addr, NULL ) != 0 ) { goto cleanup3; } if ( bounce_text( parent->el_exp_addr->e_addr_errors, TEXT_ERROR, "If you have any questions, please contact the group owner: ", e_addr->e_addr_owner, NULL ) != 0 ) { goto cleanup3; } } } continue; } #endif /* HAVE_LDAP */ if ( e_addr->e_addr_terminal == 0 ) { if ( simta_expand_debug != 0 ) { printf( "Non-terminal: %s\n", e_addr->e_addr ); } /* not a terminal expansion, do not add */ continue; } if ( simta_expand_debug != 0 ) { printf( "Terminal: %s\n", e_addr->e_addr ); } switch ( e_addr->e_addr_type ) { case ADDRESS_TYPE_EMAIL: if (( domain = strchr( e_addr->e_addr, '@' )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: strchr blivet", unexpanded_env->e_id ); goto cleanup3; } domain++; env = eo_lookup( host_stab, domain, e_addr->e_addr_from ); break; case ADDRESS_TYPE_DEAD: domain = NULL; env = env_dead; break; default: panic( "expand: address type out of range" ); } if ( env == NULL ) { /* Create envelope and add it to list */ if (( env = env_create( domain ? simta_dir_fast : simta_dir_dead, NULL, e_addr->e_addr_from, unexpanded_env )) == NULL ) { syslog( LOG_ERR, "Expand env <%s>: env_create: %m", unexpanded_env->e_id ); goto cleanup3; } simta_debuglog( 2, "Expand env <%s>: %s: expansion env dinode %d", unexpanded_env->e_id, env->e_id, (int)env->e_dinode ); /* fill in env */ env->e_attributes = unexpanded_env->e_attributes; if ( domain != NULL ) { if ( env_hostname( env, domain ) != 0 ) { env_free( env ); goto cleanup3; } } else { env_dead = env; } /* Add env to host_stab */ if ( eo_insert( &host_stab, env ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: eo_insert %s failed: %m", unexpanded_env->e_id, env->e_id ); env_free( env ); goto cleanup3; } } if ( env_recipient( env, e_addr->e_addr ) != 0 ) { goto cleanup3; } syslog( LOG_NOTICE, "Expand env <%s>: %s: recipient <%s> added to env for host %s", unexpanded_env->e_id, env->e_id, e_addr->e_addr, env->e_hostname ? env->e_hostname : "NULL" ); } /* Write out all expanded envelopes and place them in to the host_q */ for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) { env = eo->eo_env; if ( simta_expand_debug == 0 ) { sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); /* RFC 5321 4.4 Trace Information * When the delivery SMTP server makes the "final delivery" of a * message, it inserts a return-path line at the beginning of the * mail data. This use of return-path is required; mail systems * MUST support it. The return-path line preserves the * information in the <reverse-path> from the MAIL command. * Here, final delivery means the message has left the SMTP * environment. */ if ((( hq_red = red_host_lookup( eo->eo_hostname )) != NULL ) && ( hq_red->red_deliver_type == RED_DELIVER_BINARY )) { if ( snprintf( header, 270, "Return-Path: <%s>", env->e_mail ) >= 270 ) { syslog( LOG_ERR, "Expand env <%s>: %s: return path is too large", unexpanded_env->e_id, env->e_id ); } if ( env_dfile_copy( env, d_original, header ) == 0 ) { syslog( LOG_ERR, "Expand env <%s>: %s: env_dfile_copy failed", unexpanded_env->e_id, env->e_id ); goto cleanup4; } } else { /* Dfile: link Dold_id env->e_dir/Dnew_id */ if ( link( d_original, d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand link %s %s: %m", d_original, d_out ); goto cleanup4; } } sendermatch = !strcasecmp( unexpanded_env->e_mail, env->e_mail ); n_rcpts = 0; for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_INFO, "Expand env <%s>: %s: Expanded %d recipients", unexpanded_env->e_id, env->e_id, n_rcpts ); /* Efile: write env->e_dir/Enew_id for all recipients at host */ syslog( LOG_NOTICE, "Expand env <%s>: %s: writing Efile for %s", unexpanded_env->e_id, env->e_id, env->e_hostname ? env->e_hostname : "NULL" ); if ( env_outfile( env ) != 0 ) { /* env_outfile syslogs errors */ if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup4; } env_out++; queue_envelope( env ); } else { printf( "\n" ); env_stdout( env ); } } if ( env_out == 0 ) { syslog( LOG_NOTICE, "Expand env <%s>: no terminal recipients, " "deleting message", unexpanded_env->e_id ); } /* write errors out to disk */ env_p = &(exp.exp_errors); while (( env = *env_p ) != NULL ) { if ( simta_expand_debug == 0 ) { if ( env->e_error != 0 ) { env_p = &(env->e_next); if ( snet == NULL ) { if (( snet = snet_open( d_original, O_RDONLY, 0, 1024 * 1024 )) == NULL ) { syslog( LOG_ERR, "Liberror: expand snet_open %s: %m", d_original ); goto cleanup5; } } else { if ( lseek( snet_fd( snet ), (off_t)0, SEEK_SET ) != 0 ) { syslog( LOG_ERR, "Syserror: q_deliver lseek: %m" ); panic( "q_deliver lseek fail" ); } } /* write out error text, get Dfile inode */ if ( bounce_dfile_out( env, snet ) == 0 ) { if ( snet != NULL ) { if ( snet_close( snet ) != 0 ) { syslog( LOG_ERR, "Liberror: expand snet_close %s: %m", d_original ); } } goto cleanup5; } simta_debuglog( 2, "Expand env <%s>: %s: errors env dinode %d", unexpanded_env->e_id, env->e_id, (int)env->e_dinode ); line_file_free( env->e_err_text ); env->e_err_text = NULL; env->e_error = 0; if ( env_outfile( env ) != 0 ) { /* env_outfile syslogs errors */ sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup5; } sendermatch = !strcasecmp( unexpanded_env->e_mail, env->e_mail ); n_rcpts = 0; for ( rcpt = env->e_rcpt; rcpt != NULL; rcpt = rcpt->r_next ) { n_rcpts++; if ( sendermatch ) { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s>", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail ); } else { syslog( LOG_INFO, "Expand env <%s>: %s: To <%s> From <%s> (%s)", unexpanded_env->e_id, env->e_id, rcpt->r_rcpt, env->e_mail, unexpanded_env->e_mail ); } } syslog( LOG_NOTICE, "Expand env <%s>: %s: Expanded %d bounces", unexpanded_env->e_id, env->e_id, n_rcpts ); queue_envelope( env ); } else { *env_p = env->e_next; env_free( env ); } } else { *env_p = env->e_next; bounce_stdout( env ); env_free( env ); } } if ( snet != NULL ) { if ( snet_close( snet ) != 0 ) { syslog( LOG_ERR, "Liberror: expand snet_close %s: %m", d_original ); sprintf( d_out, "%s/D%s", env->e_dir, env->e_id ); if ( unlink( d_out ) != 0 ) { syslog( LOG_ERR, "Syserror: expand unlink %s: %m", d_out ); } goto cleanup5; } snet = NULL; } syslog( LOG_INFO, "Expand env <%s>: Metric %d entries %d levels", unexpanded_env->e_id, exp.exp_entries, exp.exp_max_level ); if ( simta_expand_debug != 0 ) { return_value = 0; goto cleanup2; } if ( utime( d_original, NULL ) != 0 ) { syslog( LOG_ERR, "Syserror: expand utime %s: %m", d_original ); goto cleanup5; } if ( unexpanded_env->e_dir != simta_dir_fast ) { /* truncate orignal Efile */ sprintf( e_original, "%s/E%s", unexpanded_env->e_dir, unexpanded_env->e_id ); if ( truncate( e_original, (off_t)0 ) != 0 ) { syslog( LOG_ERR, "Syserror: expand truncate %s: %m", e_original ); goto cleanup5; } } /* delete original message */ if ( env_unlink( unexpanded_env ) != 0 ) { syslog( LOG_ERR, "Expand env <%s>: Expansion complete, can't delete message", unexpanded_env->e_id ); } else { syslog( LOG_INFO, "Expand env <%s>: Expansion complete, message deleted", unexpanded_env->e_id ); } return_value = 0; goto cleanup2; cleanup5: cleanup_envelope_list( &exp.exp_errors ); #ifdef HAVE_LDAP cleanup_envelope_list( &exp.exp_gmailfwding ); #endif /* HAVE_LDAP */ cleanup4: for ( eo = host_stab; eo != NULL; eo = eo->eo_next ) { env = eo->eo_env; eo->eo_env = NULL; if (( env->e_flags & ENV_FLAG_EFILE ) != 0 ) { queue_remove_envelope( env ); if ( env_unlink( env ) == 0 ) { syslog( LOG_WARNING, "Expand env <%s>: Message Deleted: " "System error, unwinding expansion", env->e_id ); } else { syslog( LOG_ERR, "Expand env <%s>: " "System error, can't unwind expansion", env->e_id ); } } env_free( env ); } cleanup3: #ifdef HAVE_LDAP for ( memonly = exp.exp_memonly; memonly != NULL; memonly = memonly->el_next ) { if (( memonly->el_exp_addr->e_addr_env_moderated != NULL ) && (( memonly->el_exp_addr->e_addr_env_moderated->e_flags & ENV_FLAG_EFILE ) != 0 )) { env_unlink( memonly->el_exp_addr->e_addr_env_moderated ); env_free( memonly->el_exp_addr->e_addr_env_moderated ); memonly->el_exp_addr->e_addr_env_moderated = NULL; } } #endif /* HAVE_LDAP */ if ( simta_fast_files != fast_file_start ) { syslog( LOG_ERR, "Expand env <%s>: could not unwind expansion", unexpanded_env->e_id ); return_value = 1; } cleanup2: /* free host_stab */ eo = host_stab; while ( eo != NULL ) { eo_free = eo; eo = eo->eo_next; free( eo_free ); } cleanup1: #ifdef HAVE_LDAP exp_addr_link_free( exp.exp_memonly ); #endif /* HAVE_LDAP */ /* free the expansion list */ for ( e_addr = exp.exp_addr_head; e_addr != NULL; e_addr = next_e_addr ) { next_e_addr = e_addr->e_addr_next; #ifdef HAVE_LDAP exp_addr_link_free( e_addr->e_addr_parents ); exp_addr_link_free( e_addr->e_addr_children ); permitted_destroy( e_addr ); if (( e_addr->e_addr_env_moderated != NULL ) && (( e_addr->e_addr_env_moderated->e_flags & ENV_FLAG_EFILE ) == 0 )) { env_free( e_addr->e_addr_env_moderated ); } if ( e_addr->e_addr_owner ) { free( e_addr->e_addr_owner ); } if ( e_addr->e_addr_dn ) { free( e_addr->e_addr_dn ); } #endif free( e_addr->e_addr ); free( e_addr->e_addr_from ); free( e_addr ); } done: if ( return_value != 0 ) { syslog( LOG_ERR, "Expand env <%s>: Expansion failed", unexpanded_env->e_id ); } return( return_value ); }
int main( int argc, char *argv[] ) { SNET *in; SNET *out; char *line; int x; struct timeval tv; char path[ MAXPATHLEN ]; int c; if ( simta_gettimeofday( &tv ) != 0 ) { perror( "gettimeofday" ); return( 1 ); } /* XXX hard path */ sprintf( path, "%s/%ld.%ld", "/var/simta/log", tv.tv_sec, tv.tv_usec ); if (( in = snet_attach( 0, 1024 * 1024 )) == NULL ) { perror( "snet_attach" ); exit( 1 ); } if (( out = snet_open( path, O_CREAT | O_WRONLY, S_IRUSR | S_IRGRP | S_IROTH, 1024 * 1024 )) == NULL ) { perror( "snet_open" ); exit( 1 ); } snet_writef( out, "%s", argv[ 0 ] ); for ( x = 1; x < argc; x++ ) { snet_writef( out, " %s", argv[ x ] ); } snet_writef( out, "\n\n" ); opterr = 0; while (( c = getopt( argc, argv, "b:" )) != -1 ) { switch ( c ) { case 'b': if ( strlen( optarg ) == 1 ) { switch ( *optarg ) { case 'a': /* -ba ARPANET mode */ case 'd': /* -bd Daemon mode, background */ case 's': /* 501 Permission denied */ printf( "501 Mode not supported\r\n" ); exit( 1 ); } } break; default: break; } } while (( line = snet_getline( in, NULL )) != NULL ) { snet_writef( out, "%s\n", line ); } if ( snet_close( in ) != 0 ) { perror( "snet_close" ); exit( 1 ); } if ( snet_close( out ) != 0 ) { perror( "snet_close" ); exit( 1 ); } return( 0 ); }
static int connect_sn( struct connlist *cl, cosign_host_config *cfg, void *s ) { int sock, zero = 0, ac = 0, state, i; char *line, buf[ 1024 ], **av; X509 *peer; struct timeval tv; struct protoent *proto; if (( sock = socket( PF_INET, SOCK_STREAM, 0 )) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: socket" ); return( -1 ); } if (( proto = getprotobyname( "tcp" )) != NULL ) { if ( setsockopt( sock, proto->p_proto, TCP_NODELAY, &zero, sizeof( zero )) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: setsockopt: TCP_NODELAY" ); } } if ( connect( sock, ( struct sockaddr *)&cl->conn_sin, sizeof( struct sockaddr_in )) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: connect" ); (void)close( sock ); return( -1 ); } if (( cl->conn_sn = snet_attach( sock, 1024 * 1024 ) ) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_attach failed" ); (void)close( sock ); return( -1 ); } tv = timeout; if (( line = snet_getline( cl->conn_sn, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_getline failed" ); goto done; } if ( *line != '2' ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: %s", line ); goto done; } if (( ac = argcargv( line, &av )) < 4 ) { cosign_log( APLOG_ERR, s, "mod_cosign: argcargv: %s", line ); goto done; } errno = 0; cl->conn_proto = strtol( av[ 1 ], (char **)NULL, 10 ); if ( errno ) { cosign_log( APLOG_ERR, s, "mod_cosign: unrecognized protocol " "version %s, falling back to protocol v0", av[1]); cl->conn_proto = COSIGN_PROTO_V0; } if ( cfg->reqfc > 0 && !COSIGN_PROTO_SUPPORTS_FACTORS( cl->conn_proto )) { cosign_log( APLOG_ERR, s, "mod_cosign: required v2 or greater " "protocol unsupported by server " "(server protocol version: %s)", av[ 1 ] ); goto done; } cl->conn_capa = COSIGN_CAPA_DEFAULTS; if ( ac > 6 ) { /* "220 2 Collaborative Web Single Sign-On [COSIGNv3 REKEY ...]" */ ac -= 6; av += 6; if ( capa_parse( ac, av, cl, s ) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: failed to parse server " "capabilities" ); goto done; } } else { /* pre-3.1: "220 2 Collaborative Web Single Sign-On" */ if ( COSIGN_PROTO_SUPPORTS_FACTORS( cl->conn_proto )) { cl->conn_capa |= COSIGN_CAPA_FACTORS; } } if ( cl->conn_proto >= COSIGN_PROTO_V2 ) { if ( snet_writef( cl->conn_sn, "STARTTLS %d\r\n", cl->conn_proto ) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: starttls 2 failed" ); goto done; } } else { if ( snet_writef( cl->conn_sn, "STARTTLS\r\n" ) < 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: starttls failed" ); goto done; } } tv = timeout; if (( line = snet_getline_multi( cl->conn_sn, logger, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_getline_multi failed" ); goto done; } if ( snet_starttls( cl->conn_sn, cfg->ctx, 0 ) != 1 ) { cosign_log( APLOG_ERR, s, "mod_cosign: snet_starttls: %s", ERR_error_string( ERR_get_error(), NULL )); goto done; } if (( peer = SSL_get_peer_certificate( cl->conn_sn->sn_ssl )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: no certificate" ); goto done; } X509_NAME_get_text_by_NID( X509_get_subject_name( peer ), NID_commonName, buf, sizeof( buf )); X509_free( peer ); /* cn and host must match */ if ( strcasecmp( buf, cfg->host ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: cn=%s & host=%s " "don't match!", buf, cfg->host ); goto done; } if ( cl->conn_proto >= COSIGN_PROTO_V2 ) { tv = timeout; if (( line = snet_getline_multi( cl->conn_sn, logger, &tv )) == NULL ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_getline_multi failed" ); goto done; } if ( *line != '2' ) { cosign_log( APLOG_ERR, s, "mod_cosign: starttls 2: %s", line ); goto done; } } return( 0 ); done: if ( snet_close( cl->conn_sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: connect_sn: snet_close failed" ); } cl->conn_sn = NULL; cl->conn_proto = COSIGN_PROTO_V0; return( -1 ); }
int cosign_check_cookie( char *scookie, char **rekey, struct sinfo *si, cosign_host_config *cfg, int first, void *s ) { struct connlist **cur, *tmp; int rc = COSIGN_ERROR, retry = 0; /* use connection, then shuffle if there is a problem * what happens if they are all bad? */ for ( cur = cfg->cl; *cur != NULL; cur = &(*cur)->conn_next ) { if ( (*cur)->conn_sn == NULL ) { continue; } switch ( rc = netcheck_cookie( scookie, rekey, si, *cur, s, cfg )) { case COSIGN_OK : case COSIGN_LOGGED_OUT : goto done; case COSIGN_RETRY : retry = 1; break; default: cosign_log( APLOG_ERR, s, "mod_cosign: cosign_check_cookie: unknown return: %d", rc ); case COSIGN_ERROR : if ( snet_close( (*cur)->conn_sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: choose_conn: snet_close failed" ); } (*cur)->conn_sn = NULL; break; } } /* all are closed or we didn't like their answer */ for ( cur = cfg->cl; *cur != NULL; cur = &(*cur)->conn_next ) { if ( (*cur)->conn_sn != NULL ) { continue; } if (( rc = connect_sn( *cur, cfg, s )) != 0 ) { continue; } switch ( rc = netcheck_cookie( scookie, rekey, si, *cur, s, cfg )) { case COSIGN_OK : case COSIGN_LOGGED_OUT : goto done; case COSIGN_RETRY : retry = 1; break; default: cosign_log( APLOG_ERR, s, "mod_cosign: cosign_check_cookie: unknown return: %d", rc ); case COSIGN_ERROR : if ( snet_close( (*cur)->conn_sn ) != 0 ) { cosign_log( APLOG_ERR, s, "mod_cosign: choose_conn: snet_close failed" ); } (*cur)->conn_sn = NULL; break; } } if ( retry ) { return( COSIGN_RETRY ); } return( COSIGN_ERROR ); done: if ( cur != cfg->cl ) { tmp = *cur; *cur = (*cur)->conn_next; tmp->conn_next = *(cfg->cl); *(cfg->cl) = tmp; } if ( rekey && *rekey ) { /* use the rekeyed cookie to request tickets and proxy cookies */ scookie = *rekey; } if ( rc == COSIGN_LOGGED_OUT ) { return( COSIGN_RETRY ); } else { if (( first ) && ( cfg->proxy == 1 )) { if ( netretr_proxy( scookie, si, (*(cfg->cl))->conn_sn, cfg->proxydb, s ) != COSIGN_OK ) { cosign_log( APLOG_ERR, s, "mod_cosign: choose_conn: " "can't retrieve proxy cookies" ); } } #ifdef KRB if (( first ) && ( cfg->krbtkt == 1 )) { if ( netretr_ticket( scookie, si, (*(cfg->cl))->conn_sn, cfg->tkt_prefix, s ) != COSIGN_OK ) { cosign_log( APLOG_ERR, s, "mod_cosign: choose_conn: " "can't retrieve kerberos ticket" ); } } #endif /* KRB */ return( COSIGN_OK ); } }