fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / ident.c
1 /*
2  * (c) Copyright 1992 by Panagiotis Tsirigotis
3  * (c) Sections Copyright 1998-2001 by Rob Braun
4  * All rights reserved.  The file named COPYRIGHT specifies the terms 
5  * and conditions for redistribution.
6  */
7
8
9 #include "config.h"
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <netdb.h>
15 #include <signal.h>
16 #include <syslog.h>
17 #include <setjmp.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24
25
26 #include "str.h"
27 #include "ident.h"
28 #include "msg.h"
29 #include "server.h"
30 #include "connection.h"
31 #include "util.h"
32 #include "log.h"
33 #include "sconst.h"
34
35
36 static char *get_line( int sd, register char *buf, unsigned bufsize );
37 static char *verify_line( char *line, unsigned local_port, unsigned remote_port );
38
39
40 #define IBUFSIZE      1024      /* RFC-1413 suggests 1000 */
41
42 #define START_TIMER( t )   (void) alarm( t )
43 #define STOP_TIMER()      (void) alarm( 0 )
44
45
46
47 static sigjmp_buf env ;
48
49 #ifdef __GNUC__
50 __attribute__ ((noreturn))
51 #endif
52 static void sigalrm_handler(int signum)
53 {
54    siglongjmp( env, 1 ) ;
55 }
56
57
58 /*
59  * This function always runs in a forked process.
60  */
61 idresult_e log_remote_user( const struct server *serp, unsigned timeout )
62 {
63    static char         buf[ IBUFSIZE ] ;
64    int                 cc ;
65    union xsockaddr     sin_local, sin_remote, sin_contact, sin_bind;
66    volatile unsigned   local_port;
67    volatile unsigned   remote_port;
68    int                 sd ;
69    socklen_t           sin_len ;
70    char               *p ;
71    const char         *func = "log_remote_user" ;
72
73    if ( timeout && signal( SIGALRM, sigalrm_handler ) == SIG_ERR )
74    {
75       msg( LOG_ERR, func, "signal: %m" ) ;
76       return( IDR_ERROR ) ;
77    }
78
79    /*
80     * Determine local and remote addresses
81     */
82    sin_len = sizeof( sin_local ) ;
83    if ( getsockname( SERVER_FD( serp ), &sin_local.sa, &sin_len ) == -1 )
84    {
85       msg( LOG_ERR, func, "(%d) getsockname: %m", getpid() ) ;
86       return( IDR_ERROR ) ;
87    }
88
89    if ( CONN_XADDRESS( SERVER_CONNECTION( serp ) ) == NULL )
90    {
91       /*
92        * This shouldn't happen since identification only works for
93        * connection-based services.
94        */
95       msg( LOG_ERR, func, "connection has no address" ) ;
96       return( IDR_ERROR ) ;
97    }
98
99    CLEAR( sin_contact );
100    sin_remote = *CONN_XADDRESS( SERVER_CONNECTION( serp ) ) ;
101    sin_contact = sin_remote;
102    memcpy( &sin_bind, &sin_local, sizeof(sin_bind) ) ;
103    local_port = 0;
104    remote_port = 0;
105    if( sin_remote.sa.sa_family == AF_INET ) {
106       local_port = ntohs( sin_local.sa_in6.sin6_port ) ;
107       remote_port = ntohs( sin_remote.sa_in6.sin6_port ) ;
108       sin_contact.sa_in6.sin6_port = htons( IDENTITY_SERVICE_PORT ) ;
109       sin_bind.sa_in.sin_port = 0 ;
110    } else if( sin_remote.sa.sa_family == AF_INET6 ) {
111       local_port = ntohs( sin_local.sa_in.sin_port ) ;
112       remote_port = ntohs( sin_remote.sa_in.sin_port ) ;
113       sin_contact.sa_in.sin_port = htons( IDENTITY_SERVICE_PORT ) ;
114       sin_bind.sa_in6.sin6_port = 0 ;
115    }
116
117    /*
118     * Create a socket, bind it, and set the close-on-exec flag on the
119     * descriptor. We set the flag in case we are called as part of a 
120     * successful attempt to start a server (i.e. execve will follow). 
121     * The socket must be bound to the receiving address or ident might 
122     * fail for multi-homed hosts.
123     */
124    sd = socket( sin_remote.sa.sa_family, SOCK_STREAM, 0 ) ;
125    if ( sd == -1 )
126    {
127       msg( LOG_ERR, func, "socket creation: %m" ) ;
128       return( IDR_ERROR ) ;
129    }
130    if ( bind(sd, &sin_bind.sa, sizeof(sin_bind.sa)) == -1 )
131    { 
132       msg( LOG_ERR, func, "socket bind: %m" ) ;
133       (void) Sclose( sd ) ;
134       return( IDR_ERROR ) ;
135    }
136    if ( fcntl( sd, F_SETFD, FD_CLOEXEC ) == -1 )
137    {
138       msg( LOG_ERR, func, "fcntl F_SETFD: %m" ) ;
139       (void) Sclose( sd ) ;
140       return( IDR_ERROR ) ;
141    }
142
143    if ( timeout ) {
144       if ( sigsetjmp( env, 1 ) == 0 )
145          START_TIMER( timeout ) ;
146       else {
147          Sclose( sd ) ;
148          return( IDR_TIMEDOUT ) ;
149       }
150    }
151
152    if ( connect( sd, &sin_contact.sa, sizeof( sin_contact ) ) == -1 )
153    {
154       if ( timeout ) {
155          STOP_TIMER() ;
156          signal ( SIGALRM, SIG_DFL ) ;
157       }
158       Sclose( sd );
159       return( IDR_NOSERVER ) ;
160    }
161
162    cc = strx_nprint( buf, sizeof( buf ), 
163       "%d,%d\r\n", remote_port, local_port ) ;
164    if ( write_buf( sd, buf, cc ) == FAILED )
165    {
166       if ( timeout ) {
167          STOP_TIMER() ;
168          signal ( SIGALRM, SIG_DFL ) ;
169       }
170       Sclose( sd );
171       return( IDR_ERROR ) ;
172    }
173
174    p = get_line( sd, buf, sizeof( buf ) ) ;
175
176    if ( timeout ) {
177       STOP_TIMER() ;
178       signal ( SIGALRM, SIG_DFL ) ;
179    }
180
181    if ( p == NULL ) {
182       Sclose( sd );
183       return( IDR_RESPERR ) ;
184    }
185    
186    /*
187     * Verify that the received line is OK
188     */
189    if ( ( p = verify_line( buf, local_port, remote_port ) ) == NULL )
190    {
191       msg(LOG_ERR, func, "Bad line received from identity server at %s: %s",
192          xaddrname( &sin_remote ), buf ) ;
193       Sclose( sd );
194       return( IDR_BADRESP ) ;
195    }
196
197    svc_logprint( SERVER_CONNSERVICE( serp ), USERID_ENTRY, "%s", p ) ;
198    return( IDR_OK ) ;
199 }
200
201
202 static char *verify_line( char *line, 
203                            unsigned local_port, 
204                            unsigned remote_port )
205 {
206    char   *p ;
207    char   *start = line ;
208    int     port;
209
210    /*
211     * Verify port numbers
212     */
213    p = strchr( start, ',' ) ;
214    if ( p == NULL )
215       return( NULL ) ;
216    *p = NUL ;
217    if ( parse_base10( start, &port ) ||
218         port < 0 || (unsigned)port != remote_port ) {
219       *p = ',';
220       return( NULL ) ;
221    }
222    *p = ',' ;
223    
224    start = p+1 ;
225    p = strchr( start, ':' ) ;
226    if ( p == NULL )
227       return( NULL ) ;
228    *p = NUL ;
229    if ( parse_base10( start, &port ) ||
230         port < 0 || (unsigned)port != local_port ) {
231       *p = ':';
232       return( NULL ) ;
233    }
234    *p = ':';
235    
236    /*
237     * Look for the 'USERID' string
238     */
239    {
240       const char *line_id = "USERID" ;
241       unsigned int line_id_len = strlen( line_id ) ;
242
243       start = p+1 ;
244       for ( p = start ; isspace( *p ) ; p++ ) ;
245       if ( *p == NUL )
246          return( NULL ) ;
247       start = p ;
248       if ( strncmp( start, line_id, line_id_len ) != 0 )
249          return( NULL ) ;
250       start += line_id_len ;      /* skip it */
251    }
252
253    for ( p = start ; isspace( *p ) ; p++ ) ;      /* skip any white-space */
254    if ( *p != ':' )
255       return( NULL ) ;
256    for ( p++ ; isspace( *p ) ; p++ ) ;
257    if ( *p == NUL )
258       return( NULL ) ;
259    return( p ) ;
260 }
261    
262
263 /*
264  * Get a line terminated by CR-LF.
265  * Replace the CR-LF with NUL.
266  */
267 static char *get_line( int sd, char *buf, unsigned bufsize )
268 {
269    int     size ;
270    int     cc ;
271    char   *p ;
272    char   *s ;
273    const char *func = "get_line" ;
274
275    for ( p = buf, size = bufsize ; size > 0 ; p += cc, size -= cc )
276    {
277       cc = read( sd, p, size ) ;
278       if ( cc == -1 ) {
279          if ( errno == EINTR )
280          {
281             cc = 0 ;
282             continue ;
283          }
284          else
285          {
286             msg( LOG_ERR, func, "read: %m" ) ;
287             return( CHAR_NULL ) ;
288          }
289       }
290
291       if ( cc == 0 )
292       {
293          msg( LOG_ERR, func, "identd server reply missing ending CR-LF" ) ;
294          return( CHAR_NULL ) ;
295       }
296       for ( s = p ; s < p + cc ; s++ )
297       {
298          if ( (*s == '\n') && (s != buf) && (*(s-1) == '\r') )
299          {
300             *(s-1) = NUL ;
301             return( buf ) ;
302          }
303       }
304    }
305    msg( LOG_ERR, func, "Too much input from identity server" ) ;
306    return( CHAR_NULL ) ;
307 }
308
309
310 const char *idresult_explain( idresult_e result )
311 {
312    const char *reason = "UNKNOWN" ;
313
314    switch ( result )
315    {
316       case IDR_OK:
317          reason = "no error" ;
318          break ;
319
320       case IDR_NOSERVER:
321          reason = "no server" ;
322          break ;
323
324       case IDR_TIMEDOUT:
325          reason = "timeout" ;
326          break ;
327       
328       case IDR_ERROR:
329          reason = "system error" ;
330          break ;
331       
332       case IDR_RESPERR:
333          reason = "error while receiving response" ;
334          break ;
335       
336       case IDR_BADRESP:
337          reason = "bad response" ;
338          break ;
339    }
340    return( reason ) ;
341 }
342