/* * Invoked when a server of the specified service dies */ void svc_postmortem( struct service *sp, struct server *serp ) { struct service *co_sp = SERVER_CONNSERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; const char *func = "svc_postmortem" ; SVC_DEC_RUNNING_SERVERS( sp ) ; /* * Log information about the server that died */ if ( SVC_IS_LOGGING( sp ) ) { if ( SERVER_WRITES_TO_LOG(serp) ) { if ( debug.on ) msg( LOG_DEBUG, func, "Checking log size of %s service", SVC_ID( sp ) ) ; xlog_control( SVC_LOG( sp ), XLOG_SIZECHECK ) ; } svc_log_exit( sp, serp ) ; } /* * Now check if we have to check the log size of the service that owns * the connection */ if ( co_sp != sp && SVC_IS_LOGGING( co_sp ) ) xlog_control( SVC_LOG( co_sp ), XLOG_SIZECHECK ) ; if (!SVC_WAITS(sp)) { conn_free( cp, 1 ) ; cp = NULL; } else { if (cp) { if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM ) drain( cp->co_descriptor ) ; free(cp); cp = NULL; if( SVC_RELE( sp ) == 0 ) svc_release( sp ); /* shouldn't be 0, but should remove from * pset if it is... */ } svc_resume(sp); } }
/* * Read data from the remote socket and send it to the appropriate local * socket. * If this is a new connection, insert it in the connection table and * place its handle in *chpp. */ static void udp_remote_to_local( struct intercept_s *ip, channel_s **chpp ) { char buf[ MAX_DATAGRAM_SIZE ] ; packet_s packet ; channel_s *chp ; bool_int addr_checked ; *chpp = CHANNEL_NULL ; packet.data = buf ; packet.size = sizeof( buf ) ; if ( get_incoming_packet( ip, &packet ) == FAILED ) return ; chp = int_lookupconn( ip, &packet.from, &addr_checked ) ; if ( chp == CHANNEL_NULL ) { struct server *serp = INT_SERVER( ip ) ; struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cop = SERVER_CONNECTION( serp ) ; if ( ( chp = int_newconn( ip, &packet.from, INT_REMOTE( ip ) ) ) == NULL ) return ; CONN_SETADDR( cop, &packet.from ) ; /* for logging */ if ( INTERCEPT( ip ) ) { mask_t check_mask ; access_e result ; M_OR( check_mask, XMASK( CF_ADDRESS ), XMASK( CF_TIME ) ) ; result = access_control( sp, cop, &check_mask ) ; if ( result != AC_OK ) { svc_log_failure( sp, cop, result ) ; chp->ch_state = BAD_CHANNEL ; return ; } } /* * Since we don't distinguish ports, there is no point to log * another successful attempt from the same address */ if ( ! addr_checked ) svc_log_success( sp, cop, SERVER_PID( serp ) ) ; *chpp = chp ; } else if ( chp->ch_state == BAD_CHANNEL ) return ; #ifdef DEBUG_UDPINT if ( debug.on ) msg( LOG_DEBUG, "udp_remote_to_local", "sending %d bytes to server on port %d", packet.size, ntohs( INT_LOCALADDR( ip )->sin_port ) ) ; #endif send_data( chp->ch_local_socket, packet.data, packet.size, NULL ) ; }
static void tcpmux_handler( const struct server *serp ) { char svc_name[ BUFFER_SIZE ] ; int cc ; int descriptor = SERVER_FD( serp ) ; const struct service *svc = SERVER_SERVICE( serp ) ; unsigned u; struct service *sp = NULL; struct server server, *nserp; struct service_config *scp = NULL; close_all_svc_descriptors(); /* Read in the name of the service in the format "svc_name\r\n". * * XXX: should loop on partial reads (could probably use Sread() if * it wasn't thrown out of xinetd source code a few revisions back). */ do { cc = read( descriptor, svc_name, sizeof( svc_name ) ) ; } while (cc == -1 && errno == EINTR); if ( cc <= 0 ) { msg(LOG_ERR, "tcpmux_handler", "read failed"); exit(0); } if ( ( cc <= 2 ) || ( ( svc_name[cc - 1] != '\n' ) || ( svc_name[cc - 2] != '\r' ) ) ) { if ( debug.on ) msg(LOG_DEBUG, "tcpmux_handler", "Invalid service name format."); exit(0); } svc_name[cc - 2] = '\0'; /* Remove \r\n for compare */ if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Input (%d bytes) %s as service name.", cc, svc_name); } /* Search the services for the a match on name. */ for ( u = 0 ; u < pset_count( SERVICES( ps ) ) ; u++ ) { sp = SP( pset_pointer( SERVICES( ps ), u ) ) ; if ( strcasecmp( svc_name, SC_NAME( SVC_CONF( sp ) ) ) == 0 ) { /* Found the pointer. Validate its type. */ scp = SVC_CONF( sp ); /* if ( ! SVC_IS_MUXCLIENT( sp ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Non-tcpmux service name: %s.", svc_name); } exit(0); } */ /* Send the accept string if we're a PLUS (+) client. */ if ( SVC_IS_MUXPLUSCLIENT( sp ) ) { if ( Swrite( descriptor, TCPMUX_ACK, sizeof( TCPMUX_ACK ) ) != sizeof( TCPMUX_ACK ) ) { msg(LOG_ERR, "tcpmux_handler", "Ack write failed for %s.", svc_name); exit(0); } } break; /* Time to get on with the service */ } continue; /* Keep looking */ } if ( u >= pset_count( SERVICES( ps ) ) ) { if ( debug.on ) { msg(LOG_DEBUG, "tcpmux_handler", "Service name %s not found.", svc_name); } exit(0); } if( SVC_WAITS( svc ) ) /* Service forks, so close it */ Sclose(descriptor); server.svr_sp = sp; server.svr_conn = SERVER_CONNECTION(serp); nserp = server_alloc(&server); if( SC_IS_INTERNAL( scp ) ) { SC_INTERNAL(scp, nserp); } else { exec_server(nserp); } }
/* * This function is invoked in a forked process to run a server. * If the service is internal the appropriate function is invoked * otherwise the server program is exec'ed. * This function also logs the remote user id if appropriate */ void child_process( struct server *serp ) { struct service *sp = SERVER_SERVICE( serp ) ; connection_s *cp = SERVER_CONNECTION( serp ) ; struct service_config *scp = SVC_CONF( sp ) ; const char *func = "child_process" ; signal_default_state(); if ((signals_pending[0] >= 0 && Sclose(signals_pending[0])) || (signals_pending[1] >= 0 && Sclose(signals_pending[1]))) { msg(LOG_ERR, func, "Failed to close the signal pipe: %m"); _exit(1); } signals_pending[0] = -1; signals_pending[1] = -1; Sclose(0); Sclose(1); Sclose(2); #ifdef DEBUG_SERVER if ( debug.on ) { msg( LOG_DEBUG, func, "Process %d is sleeping", getpid() ) ; sleep( 10 ) ; } #endif if ( ! SC_IS_INTERCEPTED( scp ) ) { set_credentials( scp ) ; if ( SC_SPECIFIED( scp, A_NICE ) ) (void) nice( SC_NICE( scp ) ) ; } if ( svc_child_access_control(sp, cp) != OK ) exit(0); if ( SERVER_LOGUSER( serp ) ) { unsigned timeout ; idresult_e result ; /* * We use LOGUSER_SUCCESS_TIMEOUT unless the service requires * identification, in which case we use an infinite timeout */ timeout = SC_MUST_IDENTIFY( scp ) ? 0 : LOGUSER_SUCCESS_TIMEOUT ; result = log_remote_user( serp, timeout ) ; if ( result != IDR_OK && SC_MUST_IDENTIFY( scp ) ) { svc_logprint( sp, NOID_ENTRY, "%s %s", conn_addrstr( SERVER_CONNECTION( serp ) ), idresult_explain( result ) ) ; _exit( 0 ) ; } } #ifdef HAVE_SESSIONCREATE if ( scp->sc_sessioncreate == YES ) { if ( SessionCreate(0, sessionHasTTY|sessionIsRemote) != noErr ) svc_logprint( sp, "SessionCreate", "SessionCreate() failed!" ); } #endif /* this is where the server gets executed -bbraun */ if ( ! SC_IS_INTERNAL( scp ) ) { if( scp->sc_redir_addr != NULL ) { redir_handler( serp ); } else { #if defined(HAVE_SETENV) char buff[1024]; strx_sprint(buff, sizeof(buff)-1, "REMOTE_HOST=%s", conn_addrstr(cp)); if( env_addstr(SC_ENV(scp)->env_handle, buff) != ENV_OK ) { msg( LOG_ERR, func, "Error adding REMOTE_HOST variable for %s: %m", SC_NAME(scp) ); _exit( 1 ) ; } #endif exec_server( serp ) ; } } else { char name[ 180 ] ; /* * We don't bother to disassociate from the controlling terminal * (we have a controlling terminal only if debug.on is TRUE) * * Also, for interceptor processes, we give them the name: * <program_name> <service-id> interceptor */ if ( SC_IS_INTERCEPTED( scp ) ) strx_print( INT_NULL, name, sizeof( name ) - 1, "%s %s interceptor", program_name, SC_ID( scp ) ) ; else { int namelen = sizeof( name ) - 1 ; /* leave space for the NUL */ char host[NI_MAXHOST]; size_t hostlen = NI_MAXHOST; socklen_t addrlen = 0; union xsockaddr *sinp = CONN_XADDRESS(SERVER_CONNECTION(serp)); int len; if( sinp == NULL ) exit(0); if( SC_IPV6(scp) ) addrlen = sizeof(struct sockaddr_in6); else if( SC_IPV4(scp) ) addrlen = sizeof(struct sockaddr_in); len = strx_nprint(name, namelen, "(%s service) %s", program_name, SC_ID( scp ) ) ; if( getnameinfo( SA(sinp), addrlen, host, hostlen, NULL, 0, 0) != 0 ) strcpy(host, "unknown"); if ( SC_IPV6(scp) && SC_ACCEPTS_CONNECTIONS( scp ) && !IN6_IS_ADDR_UNSPECIFIED(&sinp->sa_in6.sin6_addr) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s" , host ) ; if ( SC_IPV4(scp) && SC_ACCEPTS_CONNECTIONS( scp ) ) strx_print( INT_NULL, &name[ len ], namelen - len, " %s", host ) ; } rename_process( name ) ; SVC_INTERNAL( sp, serp ) ; } _exit( 0 ) ; /* NOTREACHED */ }
/* * This function always runs in a forked process. */ idresult_e log_remote_user( const struct server *serp, unsigned timeout ) { static char buf[ IBUFSIZE ] ; int cc ; union xsockaddr sin_local, sin_remote, sin_contact, sin_bind; volatile unsigned local_port; volatile unsigned remote_port; int sd ; socklen_t sin_len ; char *p ; const char *func = "log_remote_user" ; if ( timeout && signal( SIGALRM, sigalrm_handler ) == SIG_ERR ) { msg( LOG_ERR, func, "signal: %m" ) ; return( IDR_ERROR ) ; } /* * Determine local and remote addresses */ sin_len = sizeof( sin_local ) ; if ( getsockname( SERVER_FD( serp ), &sin_local.sa, &sin_len ) == -1 ) { msg( LOG_ERR, func, "(%d) getsockname: %m", getpid() ) ; return( IDR_ERROR ) ; } if ( CONN_XADDRESS( SERVER_CONNECTION( serp ) ) == NULL ) { /* * This shouldn't happen since identification only works for * connection-based services. */ msg( LOG_ERR, func, "connection has no address" ) ; return( IDR_ERROR ) ; } CLEAR( sin_contact ); sin_remote = *CONN_XADDRESS( SERVER_CONNECTION( serp ) ) ; sin_contact = sin_remote; memcpy( &sin_bind, &sin_local, sizeof(sin_bind) ) ; local_port = 0; remote_port = 0; if( sin_remote.sa.sa_family == AF_INET ) { local_port = ntohs( sin_local.sa_in6.sin6_port ) ; remote_port = ntohs( sin_remote.sa_in6.sin6_port ) ; sin_contact.sa_in6.sin6_port = htons( IDENTITY_SERVICE_PORT ) ; sin_bind.sa_in.sin_port = 0 ; } else if( sin_remote.sa.sa_family == AF_INET6 ) { local_port = ntohs( sin_local.sa_in.sin_port ) ; remote_port = ntohs( sin_remote.sa_in.sin_port ) ; sin_contact.sa_in.sin_port = htons( IDENTITY_SERVICE_PORT ) ; sin_bind.sa_in6.sin6_port = 0 ; } /* * Create a socket, bind it, and set the close-on-exec flag on the * descriptor. We set the flag in case we are called as part of a * successful attempt to start a server (i.e. execve will follow). * The socket must be bound to the receiving address or ident might * fail for multi-homed hosts. */ sd = socket( sin_remote.sa.sa_family, SOCK_STREAM, 0 ) ; if ( sd == -1 ) { msg( LOG_ERR, func, "socket creation: %m" ) ; return( IDR_ERROR ) ; } if ( bind(sd, &sin_bind.sa, sizeof(sin_bind.sa)) == -1 ) { msg( LOG_ERR, func, "socket bind: %m" ) ; (void) Sclose( sd ) ; return( IDR_ERROR ) ; } if ( fcntl( sd, F_SETFD, FD_CLOEXEC ) == -1 ) { msg( LOG_ERR, func, "fcntl F_SETFD: %m" ) ; (void) Sclose( sd ) ; return( IDR_ERROR ) ; } if ( timeout ) { if ( sigsetjmp( env, 1 ) == 0 ) START_TIMER( timeout ) ; else { Sclose( sd ) ; return( IDR_TIMEDOUT ) ; } } if ( connect( sd, &sin_contact.sa, sizeof( sin_contact ) ) == -1 ) { if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } Sclose( sd ); return( IDR_NOSERVER ) ; } cc = strx_nprint( buf, sizeof( buf ), "%d,%d\r\n", remote_port, local_port ) ; if ( write_buf( sd, buf, cc ) == FAILED ) { if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } Sclose( sd ); return( IDR_ERROR ) ; } p = get_line( sd, buf, sizeof( buf ) ) ; if ( timeout ) { STOP_TIMER() ; signal ( SIGALRM, SIG_DFL ) ; } if ( p == NULL ) { Sclose( sd ); return( IDR_RESPERR ) ; } /* * Verify that the received line is OK */ if ( ( p = verify_line( buf, local_port, remote_port ) ) == NULL ) { msg(LOG_ERR, func, "Bad line received from identity server at %s: %s", xaddrname( &sin_remote ), buf ) ; Sclose( sd ); return( IDR_BADRESP ) ; } svc_logprint( SERVER_CONNSERVICE( serp ), USERID_ENTRY, "%s", p ) ; return( IDR_OK ) ; }