diff options
| author | christos <christos@NetBSD.org> | 2004-01-31 21:47:17 +0000 |
|---|---|---|
| committer | christos <christos@NetBSD.org> | 2004-01-31 21:47:17 +0000 |
| commit | 6310d36554b6280128ae168b79b2d7afd80128cc (patch) | |
| tree | acc68fb5a840844089b09d222894c6f2dddefc1a /libexec/identd | |
| parent | a71e78c796edda21a044bcafb625c26f0e7efbd3 (diff) | |
new identd from Peter Postma.
Diffstat (limited to 'libexec/identd')
| -rw-r--r-- | libexec/identd/Makefile | 12 | ||||
| -rw-r--r-- | libexec/identd/identd.8 | 606 | ||||
| -rw-r--r-- | libexec/identd/identd.c | 1451 |
3 files changed, 881 insertions, 1188 deletions
diff --git a/libexec/identd/Makefile b/libexec/identd/Makefile index f77725b74fb..2462ab6f6c8 100644 --- a/libexec/identd/Makefile +++ b/libexec/identd/Makefile @@ -1,13 +1,7 @@ -# $NetBSD: Makefile,v 1.9 2003/04/19 20:57:35 christos Exp $ +# $NetBSD: Makefile,v 1.10 2004/01/31 21:47:17 christos Exp $ +WARNS=3 PROG= identd -MAN= identd.8 - -CPPFLAGS+= -DINCLUDE_EXTENSIONS -DSTRONG_LOG -DALLOW_FORMAT -.ifdef DEFAULT_LIE_USER -CPPFLAGS+= -DDEFAULT_LIE_USER=\"${DEFAULT_LIE_USER}\" -.endif - -SRCS= netbsd.c version.c proxy.c config.c parse.c identd.c +MAN= identd.8 .include <bsd.prog.mk> diff --git a/libexec/identd/identd.8 b/libexec/identd/identd.8 index 7a901698dcf..cbee33b01af 100644 --- a/libexec/identd/identd.8 +++ b/libexec/identd/identd.8 @@ -1,447 +1,185 @@ -.\" $NetBSD: identd.8,v 1.16 2003/03/15 23:48:30 wiz Exp $ +.\" $NetBSD: identd.8,v 1.17 2004/01/31 21:47:17 christos Exp $ .\" -.\" @(#)identd.8 1.9 92/02/11 Lysator -.\" Copyright (c) 1992 Peter Eriksson, Lysator, Linkoping University. -.\" This software has been released into the public domain. +.\" This software is in the public domain. +.\" Written by Peter Postma <peter@pointless.nl> .\" -.TH IDENTD 8 "27 May 1992" -.SH NAME -identd \- TCP/IP IDENT protocol server -.SH SYNOPSIS -.B identd -.RB [ \-i | \-w | \-b ] -.RB [ \-t\*[Lt]seconds\*[Gt] ] -.RB [ \-u\*[Lt]uid\*[Gt] ] -.RB [ \-g\*[Lt]gid\*[Gt] ] -.RB [ \-p\*[Lt]port\*[Gt] ] -.RB [ \-a\*[Lt]address\*[Gt] ] -.RB [ \-c\*[Lt]charset\*[Gt] ] -.\".RB [ \-C [ \*[Lt]keyfile\*[Gt] ]] -.RB [ \-o ] -.RB [ \-e ] -.RB [ \-l ] -.RB [ \-V ] -.RB [ \-m ] -.RB [ \-N ] -.RB [ \-d ] -.RB [ \-F\*[Lt]format\*[Gt] ] -.RB [ \-L\*[Lt]user name\*[Gt] ] -.RB [ "kernelfile" [ "kmemfile" ] ] -.SH DESCRIPTION -.IX "identd daemon" "" \fLidentd\fP daemon" -.B identd -is a server which implements the ill-conceived -.SM IDENT -protocol specified in -.SM RFC\s0 1413. -.PP -.B identd -operates by looking up specific -.SM TCP/IP -connections and returning information which may or may not be -associated with the process owning the connection. -.PP -The -.B identd -server provides little other than a false sense of security; however, -some legacy services require their clients to run ident servers. -.SH ARGUMENTS -The highly-recommended -.B \-L\*[Lt]user name\*[Gt] -option instructs -.B identd -to always return a fixed user identity in response to all requests, -resulting in both improved efficiency and increased user privacy (you didn't -.I really -intend to trust my assertion about who I was anyway, right?) -.PP -This flag provides a way for a site to support services requiring the ident -protocol while providing a standard answer to all ident queries. -All queries to identd will respond with a host type -of `OTHER' and a username of -.B \*[Lt]user name\*[Gt]. -.PP -The -.B \-i -flag, enabled by default, should be used when starting the daemon from -.B inetd -with the "nowait" option in the -.B /etc/inetd.conf -file. -Use of this mode will make -.B inetd -start one -.B identd -daemon for each connection request. -.PP -The -.B \-w -flag should be used when starting the daemon from -.B inetd -with the "wait" option in the -.B /etc/inetd.conf -file. -This is the preferred mode of operation since that will start a copy of -.B identd -at the first connection request and then -.B identd -will handle subsequent requests without having to do the -nlist lookup in the kernel file for every request as in the -.B \-i -mode above. -The -.B identd -daemon will run either forever, until a bug -makes it crash or a timeout, as specified by the -.B \-t -flag, occurs. -.PP -The -.B \-b -flag can be used to make the daemon run in standalone -mode without the assistance from -.BR inetd. -This mode is the least preferred mode since -a bug or any other fatal condition in the server will make it terminate -and it will then have to be restarted manually. -Other than that it has the same advantage as the -.B \-w -mode in that it parses the nlist only once. -.PP -The -.B \-t\*[Lt]seconds\*[Gt] -option is used to specify the timeout limit. -This is the number of seconds a server started with the -.B \-w -flag will wait for new connections before terminating. -The server is automatically restarted by -.B inetd -whenever a new connection is requested if it has terminated. -A suitable value for this is 120 (2 minutes), if used. -It defaults to no timeout (i.e. will wait forever, or until a -fatal condition occurs in the server). -.PP -The -.B \-u\*[Lt]uid\*[Gt] -option is used to specify a user id number which the -.BR ident -server should switch to after binding itself to the -.SM TCP/IP -port if using the -.B \-b -mode of operation. -.PP -The -.B \-g\*[Lt]gid\*[Gt] -option is used to specify a group id number which the -.BR ident -server should switch to after binding itself to the -.SM TCP/IP -port if using the -.B \-b -mode of operation. -.PP -The -.B \-p\*[Lt]port\*[Gt] -option is used to specify an alternative port number -to bind to if using the -.B \-b -mode of operation. -It can be specified by name or by number. -Defaults to the -.SM IDENT -port (113). -.PP -The -.B \-a\*[Lt]address\*[Gt] -option is used to specify the local address to bind -the socket to if using the -.B \-b -mode of operation. -Can only be specified by IP address and not by domain name. -Defaults to the -.SM INADDR_ANY -address which normally means all local addresses. -.PP -The -.B \-V -flag makes -.B identd -display the version number and then exit. -.PP -The -.B \-l -flag tells -.B identd -to use the System logging daemon -.B syslogd -for logging purposes. -.PP -The -.B \-o -flag tells -.B identd -to not reveal the operating system type it is run on and to instead -always return "OTHER". -.PP -The -.B \-e -flag tells -.B identd -to always return "UNKNOWN-ERROR" instead of the "NO-USER" or -"INVALID-PORT" errors. -.PP -The -.B \-c\*[Lt]charset\*[Gt] -flags tells -.B identd -to add the optional (according to the IDENT protocol) character set -designator to the reply generated. -.I charset -should be a valid character set as described in the MIME RFC in upper -case characters. -.\".PP -.\"The -.\".BR \-C [ \*[Lt]keyfile\*[Gt] ] -.\"option tells -.\".B identd -.\"to return encrypted tokens instead of user names. -.\"The local and remote IP -.\"addresses and TCP port numbers, the local user's uid number, a timestamp, -.\"a random number, and a checksum, are all encrypted using DES -.\"with a secret key derived from the first line of the -.\".I keyfile -.\"(using -.\".BR des_string_to_key (3)). -.\"The encrypted binary information is then encoded in a base64 string -.\"(32 characters in length) and enclosed in square brackets to produce -.\"a token that is transmitted to the remote client. -.\"The encrypted token can later be decrypted by -.\".BR idecrypt (8). -.\"There may not be a space between the -.\".B \-C -.\"and the name of the -.\".IR keyfile . -.\"If the -.\".I keyfile -.\"is not specified, it defaults to -.\".BR /etc/identd.key . -.PP -The -.B \-n -flag tells -.B identd -to always return user numbers instead of user names if you wish to -keep the user names a secret. -The -.B \-N -flag makes -.B identd -check for a file ".noident" in each home directory for a user which the -daemon is about to return the user name for. -It that file exists then the daemon will give the error -.B HIDDEN-USER -instead of the normal USERID response. -.PP -.B \-m -flag makes -.B identd -use a mode of operation that will allow multiple requests to be -processed per session. -Each request is specified one per line and -the responses will be returned one per line. -The connection will not be closed until the connecting -part closes its end of the line. -PLEASE NOTE THAT THIS MODE VIOLATES THE PROTOCOL SPECIFICATION AS -IT CURRENTLY STANDS. -.PP -The -.B \-d -flag enables some debugging code that normally should NOT -be enabled since that breaks the protocol and may reveal information -that should not be available to outsiders. -.PP -The -.B \-F\*[Lt]format\*[Gt] -option makes -.B identd -use the specified format to display info. +.Dd January 31, 2004 +.Dt IDENTD 8 +.Os +.Sh NAME +.Nm identd +.Nd TCP/IP Ident protocol server. +.Sh SYNOPSIS +.Nm +.Op Fl 4 +.Op Fl 6 +.Op Fl a Ar address +.Op Fl b +.Op Fl c Ar charset +.Op Fl e +.Op Fl f Ar fallback +.Op Fl F Ar format +.Op Fl g Ar uid +.Op Fl i +.Op Fl I +.Op Fl l +.Op Fl L Ar username +.Op Fl n +.Op Fl N +.Op Fl o Ar osname +.Op Fl p Ar portno +.Op Fl r +.Op Fl t Ar seconds +.Op Fl u Ar uid +.Sh DESCRIPTION +.Nm +is a TCP/IP server which implements the user identification protocol +as specified in RFC 1413. +.Pp +.Nm +operates by looking up specific TCP/IP connections and returning +information which may or may not be associated with the process owning +the connection. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl 4 +Bind to IPv4 addresses only. (flag +.Fl b +only) +.It Fl 6 +Bind to IPv6 addresses only. (flag +.Fl b +only) +.It Fl a Ar address +Bind to the specified +.Ar address . +This may be an IPv4 or IPv6 address or even a hostname. +If a hostname is specified then +.Nm +will resolve it to an address (or addresses) and will bind this address. (flag +.Fl b +only) +.It Fl b +Run in the background (as daemon). +.It Fl c Ar charset +Specfify an optional character set designator to be included in replies. +.Ar charset +should be a valid charset set as described in the +.Tn MIME RFC +in upper case characters. +.It Fl e +Return +.Dq UNKNOWN-ERROR +instead of the usual +.Dq NO-USER +or +.Dq INVALID-PORT +error reply's. +.It Fl f Ar fallback +Specify a +.Ar fallback +username. +If the lookup fails then this username will be returned. +This can be useful for when running this service on a NAT host. +.It Fl F Ar format +Specify the format to display info. The allowed format specifiers are: .in +.5i .nf -%u print user name -%U print user number -%g print (primary) group name -%G print (primary) group number -%l print list of all groups by name -%L print list of all groups by number -%p print process ID of running process -%c print command name -%C print command and arguments +%u print user name +%U print user number +%g print (primary) group name +%G print (primary) group number +%l print list of all groups by name +%L print list of all groups by number .in -.5i .fi +.Pp The lists of groups (%l, %L) are comma-separated, and start with the primary group which is not repeated. -The %p and the %c and %C formats are not supported on all architecture -implementations (printing 0 or empty string instead). -.br Any other characters (preceded by %, and those not preceded by it) are printed literally. The "default" format is %u, and you should not use -anything else without the -.B \-o -flag. -.br -Not implemented yet, but on my wish-list are the following: -.in +.5i -.nf -%w print working (current) directory -%h print home (login, naming) directory -%e print the environment -.in -.5i -.fi -.PP -.B kernelfile -defaults to the normally running kernel file. -.PP -.B kmemfile -defaults to the memory space of the normally running kernel. -.SH UNDERDOCUMENTED FLAGS -The -.B \-v -flag enables more verbose output or messages. -(Further occurrences of the -.B -v -flag make things even more verbose.) -Currently not used: ignored. -.PP -The -.B \-f\*[Lt]config-file\*[Gt] -option causes -.B identd -to use the named config file (instead of the default /etc/identd.conf ?). -Currently not used: ignored, no config files are used. -.PP -The -.B \-r\*[Lt]indirect_host\*[Gt] -option is used in some way (for proxy queries?). -.PP -The -.B \-C\*[Lt]keyfile\*[Gt] -option is used in some way for DES encryption. -.SH INSTALLATION -.B identd -is invoked either by the internet server (see -.BR inetd (8C) -) for requests to connect to the -.SM IDENT -port as indicated by the -.B /etc/services -file (see -.BR services (5) -) when using the -.B \-w -or -.B \-i -modes of operation or started manually by using the -.B \-b -mode of operation. -.SH EXAMPLES -Assuming the server is located in -.B /usr/libexec/identd -one can put either: -.PP -ident stream tcp wait sys /usr/libexec/identd identd -w -t120 -.PP -or: -.PP -ident stream tcp nowait sys /usr/libexec/identd identd -i -.PP -into the -.B /etc/inetd.conf -file. -User "sys" should have enough rights to READ the kernel -but NOT to write to it. -.PP -To start it using the -.B \-b -mode of operation one can put a line like this into the -.B /etc/rc.local -file: -.PP -/usr/libexec/identd -b -u2 -g2 -.PP -This will make it run in the background as user 2, group 2 -(user "sys", group "kmem" on SunOS 4.1.1). -.SH NOTES -There should be no need to ever use this; -if you think you need this, you really need protocols -which do strong host and/or user authentication such as ssh and IPsec -in conjunction with audit trails. +anything else without using the flag +.Ar -o OTHER . +.It Fl g Ar gid +Specify the group id number or name which the server should switch to after +binding itself to the TCP/IP port. +.It Fl i +If the +.Pa .ident +file exists in the home directory of the identified user, return the username +found in that file instead of the real username. +If the username found in +.Pa .ident +is that of an existing user, then the real username will be returned. +.It Fl I +Same as +.Fl i +but without the restriction that the username in +.Pa .ident +must not match an existing user. +.It Fl l +Use syslog for logging. +.It Fl L Ar username +Specify a +.Dq lie +.Ar username . +Return this name for all ident requests. +.It Fl n +Return numeric user IDs instead of usernames. +.It Fl N +Enable +.Pa .noident +files. +If this file exists in the home directory of the identified user then return +.Dq HIDDEN-USER +instead of the normal USERID response. +.It Fl o Ar osname +Return +.Ar osname +instead of the default +.Dq UNIX . +.It Fl p Ar portno +Specify an alternative port number where the server should run under. +The default is port 113. (flag +.Fl b +only) +.It Fl r +Return a random name of alphanumeric characters. +If the +.Fl n +flag is also enabled then a random number will be returned. +.It Fl t Ar seconds +Specify a timeout for the service. +The default timeout is 30 seconds. +.It Fl u Ar uid +Specify the user id number or name which the server should switch to after +binding itself to the TCP/IP port. +.El +.Sh EXAMPLES +.Nm +operates from +.Xr inetd 8 +or as standalone daemon. +Put the following lines into +.Xr inetd.conf 5 +to enable +.Nm +as an IPv4 and IPv6 service via inetd: +.Pp +ident stream tcp nowait nobody /usr/libexec/identd identd -l +.Pp +ident stream tcp6 nowait nobody /usr/libexec/identd identd -l .Pp -The username (or UID) returned ought to be the login name. -However it (probably, for most architecture implementations) -is the "real user ID" as stored with the process; -there is no provision for returning the "effective user ID". -Thus the UID returned may be different from the login name for -setuid programs (or those running as root) which done a -.BR setuid (2) -call and their children. -For example, it may (should?) be wrong for an incoming -.B ftpd -; and we are probably interested in the running shell, not the -.B telnetd -for an incoming telnet session. -(But of course -.B identd -returns info for outgoing connections, not incoming ones.) -.PP -The group or list of groups returned (with the -.B \-F -option) are as looked up in the -.B /etc/passwd -and -.B /etc/group -files, based on the UID returned. -Thus these may not relate well to the group(s) of the running -process for setuid or setgid programs or their children. -.PP -The command names returned with formats %c and %C may be different, -use one or the other or both. -.SH FILES -.TP -.B /etc/identd.conf -This file is as yet un-used, but will eventually contain configuration -options for -.B identd -.TP -.B /etc/identd.key -If compiled with -.I \-ldes -this file can be used to specify a secret key for encrypting replies. -.SH SECURITY CONSIDERATIONS -May leak information generally considered "private" unless the -.B \-e -flag or -.\"either the -.B \-L -.\"or -.\".B \-C -flags are used. -.PP -The protocol is unprotected and is vulnerable to man-in-the-middle -attacks. -.SH "SEE ALSO" -.BR inetd.conf (5) -.SH BUGS -The -.B \-e -and -.B \-L -flags should be enabled by default. -.PP -The whole concept of this service is a bug; cryptographic -authentication should be integrated into services which think they -need this. -.PP -The handling of fatal errors could be better. +If you want to run +.Nm +as standalone daemon then please use the +.Fl b +flag (background). +.Sh FILES +/etc/inetd.conf +.Sh SEE ALSO +.Xr inetd 8 , +.Xr inetd.conf 5 diff --git a/libexec/identd/identd.c b/libexec/identd/identd.c index fa799ff9ba1..2a5c9782d1d 100644 --- a/libexec/identd/identd.c +++ b/libexec/identd/identd.c @@ -1,806 +1,767 @@ -/* $NetBSD: identd.c,v 1.19 2003/09/14 22:38:23 christos Exp $ */ +/* $NetBSD: identd.c,v 1.20 2004/01/31 21:47:17 christos Exp $ */ /* -** identd.c A TCP/IP link identification protocol server -** -** This program is in the public domain and may be used freely by anyone -** who wants to. -** -** Last update: 7 Oct 1993 -** -** Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se> -*/ - -#if defined(IRIX) || defined(SVR4) || defined(NeXT) || (defined(sco) && sco >= 42) || defined(_AIX4) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(ultrix) -# define SIGRETURN_TYPE void -# define SIGRETURN_TYPE_IS_VOID -#else -# define SIGRETURN_TYPE int -#endif - -#ifdef SVR4 -# define STRNET -#endif - -#ifdef NeXT31 -# include <libc.h> -#endif - -#ifdef sco -# define USE_SIGALARM -#endif + * identd.c - TCP/IP Ident protocol server. + * + * This software is in the public domain. + * Written by Peter Postma <peter@pointless.nl> + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <netinet/ip_var.h> +#include <netinet/tcp.h> +#include <netinet/tcp_timer.h> +#include <netinet/tcp_var.h> + +#include <arpa/inet.h> -#include <stdio.h> #include <ctype.h> +#include <err.h> #include <errno.h> -#include <netdb.h> -#include <signal.h> #include <fcntl.h> +#include <grp.h> +#include <netdb.h> #include <poll.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#define OPSYS_NAME "UNIX" +#define IDENT_SERVICE "auth" +#define TIMEOUT 30 /* seconds */ + +static int idhandle(int, const char *, const char *, const char *, + const char *, int); +static void idparse(int, int, int, const char *, const char *, const char *); +static void iderror(int, int, int, const char *); +static const char *gethost(struct sockaddr_storage *); +static int *socketsetup(const char *, const char *, int); +static int sysctl_getuid(struct sockaddr_storage *, socklen_t, uid_t *); +static int check_noident(const char *); +static int check_userident(const char *, char *, size_t); +static void random_string(char *, size_t); +static void change_format(const char *, struct passwd *, char *, size_t); +static void timeout_handler(int); +static void waitchild(int); +static void fatal(const char *); + +static int bflag, eflag, fflag, Fflag, iflag, Iflag; +static int lflag, Lflag, nflag, Nflag, rflag; + +int +main(int argc, char *argv[]) +{ + int IPv4or6, ch, *socks, timeout; + char *address, *charset, *fmt; + const char *osname, *portno; + char *p; + char user[LOGIN_NAME_MAX]; + struct group *grp; + struct passwd *pw; + gid_t gid; + uid_t uid; + + IPv4or6 = AF_UNSPEC; + osname = OPSYS_NAME; + portno = IDENT_SERVICE; + timeout = TIMEOUT; + address = NULL; + charset = NULL; + fmt = NULL; + socks = NULL; + gid = uid = 0; + bflag = eflag = fflag = Fflag = iflag = Iflag = 0; + lflag = Lflag = nflag = Nflag = rflag = 0; + + /* Started from a tty? then run as daemon */ + if (isatty(0)) + bflag = 1; + + /* Parse arguments */ + while ((ch = getopt(argc, argv, "46a:bceF:f:g:IiL:lNno:p:rt:u:")) != -1) + switch (ch) { + case '4': + IPv4or6 = AF_INET; + break; + case '6': + IPv4or6 = AF_INET6; + break; + case 'a': + address = optarg; + break; + case 'b': + bflag = 1; + break; + case 'c': + charset = optarg; + break; + case 'e': + eflag = 1; + break; + case 'F': + Fflag = 1; + fmt = optarg; + break; + case 'f': + fflag = 1; + (void)strlcpy(user, optarg, sizeof(user)); + break; + case 'g': + gid = (gid_t)strtol(optarg, &p, 0); + if (*p != '\0') { + if ((grp = getgrnam(optarg)) != NULL) + gid = grp->gr_gid; + else + errx(1, "No such group '%s'", optarg); + } + break; + case 'I': + Iflag = 1; + /* FALLTHROUGH */ + case 'i': + iflag = 1; + break; + case 'L': + Lflag = 1; + (void)strlcpy(user, optarg, sizeof(user)); + break; + case 'l': + lflag = 1; + break; + case 'N': + Nflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'o': + osname = optarg; + break; + case 'p': + portno = optarg; + break; + case 'r': + rflag = 1; + break; + case 't': + timeout = (int)strtol(optarg, &p, 0); + if (*p != '\0') + errx(1, "Invalid timeout value '%s'", optarg); + break; + case 'u': + uid = (uid_t)strtol(optarg, &p, 0); + if (*p != '\0') { + if ((pw = getpwnam(optarg)) != NULL) { + uid = pw->pw_uid; + gid = pw->pw_gid; + } else + errx(1, "No such user '%s'", optarg); + } + break; + default: + exit(1); + } -#include <sys/types.h> -#include <sys/param.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#ifndef _AUX_SOURCE -# include <sys/file.h> -#endif -#include <sys/time.h> -#include <sys/wait.h> + if (lflag) + openlog("identd", LOG_PID, LOG_DAEMON); -#include <pwd.h> -#include <grp.h> + /* Setup sockets if -b flag */ + if (bflag) { + socks = socketsetup(address, portno, IPv4or6); + if (socks == NULL) + return 1; + } -#include <netinet/in.h> + /* Switch to another uid/gid ? */ + if (gid && setgid(gid) == -1) { + if (lflag) + syslog(LOG_ERR, "setgid: %m"); + if (bflag) + warn("setgid"); + exit(1); + } + if (uid && setuid(uid) == -1) { + if (lflag) + syslog(LOG_ERR, "setuid: %m"); + if (bflag) + warn("setuid"); + exit(1); + } -#ifndef HPUX7 -# include <arpa/inet.h> -#endif + /* Daemonize, setup pollfds and start mainloop if -b flag */ + if (bflag) { + int fd, i, nfds, rv; + struct pollfd *rfds; + + (void)signal(SIGCHLD, waitchild); + (void)daemon(0, 0); + + rfds = malloc(*socks * sizeof(struct pollfd)); + if (rfds == NULL) + fatal("malloc"); + nfds = *socks; + for (i = 0; i < nfds; i++) { + rfds[i].fd = socks[i+1]; + rfds[i].events = POLLIN; + rfds[i].revents = 0; + } + /* Mainloop for daemon */ + for (;;) { + rv = poll(rfds, nfds, INFTIM); + if (rv < 0 && errno == EINTR) + continue; + if (rv < 0) + fatal("poll"); + for (i = 0; i < nfds; i++) { + if (rfds[i].revents & POLLIN) { + fd = accept(rfds[i].fd, NULL, NULL); + if (fd < 0) { + if (lflag) + syslog(LOG_ERR, + "accept: %m"); + continue; + } + switch (fork()) { + case -1: /* error */ + if (lflag) + syslog(LOG_ERR, + "fork: %m"); + (void)sleep(1); + break; + case 0: /* child */ + (void)idhandle(fd, charset, + fmt, osname, user, timeout); + _exit(0); + default: /* parent */ + (void)close(fd); + } + } + } + } + } else + (void)idhandle(STDIN_FILENO, charset, fmt, osname, user, + timeout); -#if defined(MIPS) || defined(BSD43) -extern int errno; -#endif + return 0; +} -#if defined(SOLARIS) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__linux__) || defined(_AIX) -# include <unistd.h> -# include <stdlib.h> -# include <string.h> -#endif +static int +idhandle(int fd, const char *charset, const char *fmt, const char *osname, + const char *user, int timeout) +{ + struct sockaddr_storage ss[2]; + char userbuf[LOGIN_NAME_MAX]; + char idbuf[LOGIN_NAME_MAX]; + char buf[BUFSIZ], *p; + int n, lport, fport; + struct passwd *pw; + socklen_t len; + uid_t uid; + + lport = fport = 0; + + (void)strlcpy(idbuf, user, sizeof(idbuf)); + (void)signal(SIGALRM, timeout_handler); + (void)alarm(timeout); + + /* Get foreign internet address */ + len = sizeof(ss[0]); + if (getpeername(fd, (struct sockaddr *)&ss[0], &len) < 0) + fatal("getpeername"); + + if (lflag) + syslog(LOG_INFO, "Connection from %s", gethost(&ss[0])); + + /* Get local internet address */ + len = sizeof(ss[1]); + if (getsockname(fd, (struct sockaddr *)&ss[1], &len) < 0) + fatal("getsockname"); + + /* Be sure to have the same address family's */ + if (ss[0].ss_family != ss[1].ss_family) { + if (lflag) + syslog(LOG_ERR, "Foreign/local AF are different!"); + return 1; + } -#include "identd.h" -#include "error.h" -#include "paths.h" + /* Receive data from the client */ + if ((n = recv(fd, buf, sizeof(buf) - 1, 0)) < 0) { + fatal("recv"); + } else if (n == 0) { + if (lflag) + syslog(LOG_NOTICE, "recv: EOF"); + iderror(fd, 0, 0, "UNKNOWN-ERROR"); + return 1; + } + buf[n] = '\0'; + + /* Get local and remote ports from the received data */ + p = buf; + while (*p != '\0' && isspace(*p)) + p++; + if ((p = strtok(p, " \t,")) != NULL) { + lport = atoi(p); + if ((p = strtok(NULL, " \t,")) != NULL) + fport = atoi(p); + } + /* Are the ports valid? */ + if (lport < 1 || lport > 65535 || fport < 1 || fport > 65535) { + if (lflag) + syslog(LOG_NOTICE, "Invalid port(s): %d, %d from %s", + lport, fport, gethost(&ss[0])); + iderror(fd, 0, 0, eflag ? "UNKNOWN-ERROR" : "INVALID-PORT"); + return 1; + } -/* Antique unixes do not have these things defined... */ -#ifndef FD_SETSIZE -# define FD_SETSIZE 256 -#endif + /* If there is a 'lie' user enabled, then handle it now and quit */ + if (Lflag) { + if (lflag) + syslog(LOG_NOTICE, "Lying with name %s to %s", + idbuf, gethost(&ss[0])); + idparse(fd, lport, fport, charset, osname, idbuf); + return 0; + } -#ifndef FD_SET -# ifndef NFDBITS -# define NFDBITS (sizeof(int) * NBBY) /* bits per mask */ -# endif -# define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) -#endif + /* Protocol dependent stuff */ + switch (ss[0].ss_family) { + case AF_INET: + ((struct sockaddr_in *)&ss[0])->sin_port = htons(fport); + ((struct sockaddr_in *)&ss[1])->sin_port = htons(lport); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss[0])->sin6_port = htons(fport); + ((struct sockaddr_in6 *)&ss[1])->sin6_port = htons(lport); + break; + default: + if (lflag) + syslog(LOG_ERR, "Unsupported protocol, proto no. %d", + ss[0].ss_family); + return 1; + } -#ifndef FD_ZERO -# define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) -#endif + /* Do sysctl call */ + if (sysctl_getuid(ss, sizeof(ss), &uid) == -1) { + if (lflag) + syslog(LOG_ERR, "sysctl: %m"); + if (fflag) { + if (lflag) + syslog(LOG_NOTICE, "Using fallback name %s " + "to %s", idbuf, gethost(&ss[0])); + idparse(fd, lport, fport, charset, osname, idbuf); + return 0; + } + iderror(fd, lport, fport, eflag ? "UNKNOWN-ERROR" : "NO-USER"); + return 1; + } + /* Get username with the uid */ + if ((pw = getpwuid(uid)) == NULL) { + if (lflag) + syslog(LOG_ERR, "Couldn't map uid (%u) to name", uid); + (void)snprintf(idbuf, sizeof(idbuf), "%u", uid); + idparse(fd, lport, fport, charset, osname, idbuf); + return 0; + } -char *path_unix = (char *) NULL; -char *path_kmem = (char *) NULL; + if (lflag) + syslog(LOG_INFO, "Successfull lookup: %d, %d: %s for %s", + lport, fport, pw->pw_name, gethost(&ss[0])); + + /* No ident enabled? */ + if (Nflag && check_noident(pw->pw_dir)) { + if (lflag) + syslog(LOG_NOTICE, "Returning HIDDEN-USER for user %s" + " to %s", pw->pw_name, gethost(&ss[0])); + iderror(fd, lport, fport, "HIDDEN-USER"); + return 1; + } -int verbose_flag = 0; -int debug_flag = 0; -int syslog_flag = 0; -int multi_flag = 0; -int other_flag = 0; -int unknown_flag = 0; -int noident_flag = 0; -int crypto_flag = 0; -int liar_flag = 0; + /* User ident enabled ? */ + if (iflag && check_userident(pw->pw_dir, idbuf, sizeof(idbuf))) { + (void)strlcpy(userbuf, pw->pw_name, sizeof(userbuf)); + if (!Iflag) { + if (strspn(idbuf, "0123456789") && + getpwuid(atoi(idbuf)) != NULL) + (void)strlcpy(idbuf, userbuf, sizeof(idbuf)); + else if (getpwnam(idbuf) != NULL) + (void)strlcpy(idbuf, userbuf, sizeof(idbuf)); + } + if (lflag) + syslog(LOG_NOTICE, "Returning user specified '%s' for " + "user %s to %s", idbuf, userbuf, gethost(&ss[0])); + idparse(fd, lport, fport, charset, osname, idbuf); + return 0; + } -int lport = 0; -int fport = 0; + /* Send random crap? */ + if (rflag) { + /* Random number or string? */ + if (nflag) + (void)snprintf(idbuf, sizeof(idbuf), "%u", + (unsigned int)(arc4random() % 65535)); + else + random_string(idbuf, sizeof(idbuf)); -char *charset_name = (char *) NULL; -char *indirect_host = (char *) NULL; -char *indirect_password = (char *) NULL; -char *lie_string = (char *) NULL; + if (lflag) + syslog(LOG_NOTICE, "Returning random '%s' for user %s" + " to %s", idbuf, pw->pw_name, gethost(&ss[0])); + idparse(fd, lport, fport, charset, osname, idbuf); + return 0; + } -#ifdef ALLOW_FORMAT - int format_flag = 0; - char *format = "%u"; -#endif + /* Return number? */ + if (nflag) + (void)snprintf(idbuf, sizeof(idbuf), "%u", uid); + else + (void)strlcpy(idbuf, pw->pw_name, sizeof(idbuf)); -static int child_pid; + if (Fflag) { + /* RFC 1413 says that 512 is the limit */ + change_format(fmt, pw, buf, 512); + idparse(fd, lport, fport, charset, osname, buf); + } else + idparse(fd, lport, fport, charset, osname, idbuf); -#ifdef LOG_DAEMON -static int syslog_facility = LOG_DAEMON; -#endif + return 0; +} -static int comparemem __P((void *, void *, int)); -char *clearmem __P((void *, int)); -static SIGRETURN_TYPE child_handler __P((int)); -int main __P((int, char *[])); +/* Send/parse the ident result */ +static void +idparse(int fd, int lport, int fport, const char *charset, const char *osname, + const char *user) +{ + char *p; + + if (asprintf(&p, "%d , %d : USERID : %s%s%s : %s\r\n", lport, fport, + osname, charset ? " , " : "", charset ? charset : "", user) < 0) + fatal("asprintf"); + if (send(fd, p, strlen(p), 0) < 0) { + free(p); + fatal("send"); + } + free(p); +} -/* -** The structure passing convention for GCC is incompatible with -** Suns own C compiler, so we define our own inet_ntoa() function. -** (This should only affect GCC version 1 I think, a well, this works -** for version 2 also so why bother.. :-) -*/ -#if defined(__GNUC__) && defined(__sparc__) && !defined(NeXT) - -#ifdef inet_ntoa -#undef inet_ntoa -#endif - -char *inet_ntoa(ad) - struct in_addr ad; +/* Return a specified ident error */ +static void +iderror(int fd, int lport, int fport, const char *error) { - unsigned long int s_ad; - int a, b, c, d; - static char addr[20]; - - s_ad = ad.s_addr; - d = s_ad % 256; - s_ad /= 256; - c = s_ad % 256; - s_ad /= 256; - b = s_ad % 256; - a = s_ad / 256; - snprintf(addr, sizeof(addr), "%d.%d.%d.%d", a, b, c, d); - - return addr; + char *p; + + if (asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, error) < 0) + fatal("asprintf"); + if (send(fd, p, strlen(p), 0) < 0) { + free(p); + fatal("send"); + } + free(p); } -#endif -static int comparemem(vp1, vp2, len) - void *vp1; - void *vp2; - int len; +/* Return the IP address of the connecting host */ +static const char * +gethost(struct sockaddr_storage *ss) { - unsigned char *p1 = (unsigned char *) vp1; - unsigned char *p2 = (unsigned char *) vp2; - int c; + static char host[NI_MAXHOST]; - while (len-- > 0) - if ((c = (int) *p1++ - (int) *p2++) != 0) - return c; + if (getnameinfo((struct sockaddr *)ss, ss->ss_len, host, + sizeof(host), NULL, 0, NI_NUMERICHOST) == 0) + return host; - return 0; + return "UNKNOWN"; } -/* -** Return the name of the connecting host, or the IP number as a string. -*/ -char *gethost(addr) - struct in_addr *addr; +/* Setup sockets, for daemon mode */ +static int * +socketsetup(const char *address, const char *port, int af) { - int i; - struct hostent *hp; - - - hp = gethostbyaddr((char *) addr, sizeof(struct in_addr), AF_INET); - if (hp) - { - char *hname = strdup(hp->h_name); - - if (! hname) { - syslog(LOG_ERR, "strdup(%s): %m", hp->h_name); - exit(1); + struct addrinfo hints, *res, *res0; + int error, maxs, *s, *socks, y = 1; + const char *cause = NULL; + + (void)memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = af; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(address, port, &hints, &res0); + if (error) { + if (lflag) + syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(error)); + errx(1, "%s", gai_strerror(error)); + /*NOTREACHED*/ + } + + /* Count max number of sockets we may open */ + for (maxs = 0, res = res0; res != NULL; res = res->ai_next) + maxs++; + + socks = malloc((maxs + 1) * sizeof(int)); + if (socks == NULL) { + if (lflag) + syslog(LOG_ERR, "malloc: %m"); + err(1, "malloc"); + /* NOTREACHED */ } - /* Found a IP -> Name match, now try the reverse for security reasons */ - hp = gethostbyname(hname); - (void) free(hname); - if (hp) -#ifdef h_addr - for (i = 0; hp->h_addr_list[i]; i++) - if (comparemem(hp->h_addr_list[i], - (unsigned char *) addr, - (int) sizeof(struct in_addr)) == 0) - return (char *) hp->h_name; -#else - if (comparemem(hp->h_addr, addr, sizeof(struct in_addr)) == 0) - return hp->h_name; -#endif - } - - return inet_ntoa(*addr); + + *socks = 0; + s = socks + 1; + for (res = res0; res != NULL; res = res->ai_next) { + *s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (*s < 0) { + cause = "socket"; + continue; + } + (void)setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)); + if (bind(*s, res->ai_addr, res->ai_addrlen) < 0) { + cause = "bind"; + (void)close(*s); + continue; + } + if (listen(*s, 5) < 0) { + cause = "listen"; + (void)close(*s); + continue; + } + *socks = *socks + 1; + s++; + } + + if (*socks == 0) { + free(socks); + if (lflag) + syslog(LOG_ERR, "%s: %m", cause); + err(1, "%s", cause); + /* NOTREACHED */ + } + if (res0) + freeaddrinfo(res0); + + return socks; } -#ifdef USE_SIGALARM -/* -** Exit cleanly after our time's up. -*/ -static SIGRETURN_TYPE -alarm_handler(int s) +/* Return the UID for the connection owner */ +static int +sysctl_getuid(struct sockaddr_storage *ss, socklen_t len, uid_t *uid) { - if (syslog_flag) - syslog(LOG_DEBUG, "SIGALRM triggered, exiting"); - - exit(0); + int mib[4]; + uid_t myuid; + size_t uidlen; + + uidlen = sizeof(myuid); + + mib[0] = CTL_NET; + mib[1] = ss->ss_family; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_IDENT; + + if (sysctl(mib, sizeof(mib)/ sizeof(int), &myuid, &uidlen, ss, len) < 0) + return -1; + *uid = myuid; + + return 0; } -#endif +/* Check if a .noident file exists in the user home directory */ +static int +check_noident(const char *homedir) +{ + struct stat sb; + char *path; + int ret; + + if (homedir == NULL) + return 0; + if (asprintf(&path, "%s/.noident", homedir) < 0) + return 0; + ret = stat(path, &sb); + + free(path); + return (ret == 0); +} -#if !defined(hpux) && !defined(__hpux) && !defined(SVR4) && \ - !defined(_CRAY) && !defined(sco) && !defined(LINUX) /* -** This is used to clean up zombie child processes -** if the -w or -b options are used. -*/ -static SIGRETURN_TYPE -child_handler(dummy) - int dummy; + * Check if a .ident file exists in the user home directory and + * return the contents of that file. + */ +static int +check_userident(const char *homedir, char *username, size_t len) { -#if defined(NeXT) || (defined(__sgi) && defined(__SVR3)) - union wait status; -#else - int status; -#endif - int saved_errno = errno; - - while (wait3(&status, WNOHANG, NULL) > 0) - ; - - errno = saved_errno; - -#ifndef SIGRETURN_TYPE_IS_VOID - return 0; -#endif -} -#endif + struct stat sb; + char *path, *p; + int fd, n; + + if (len == 0 || homedir == NULL) + return 0; + if (asprintf(&path, "%s/.ident", homedir) < 0) + return 0; + if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < 0) { + free(path); + return 0; + } + if (fstat(fd, &sb) < 0 || !S_ISREG(sb.st_mode)) { + (void)close(fd); + free(path); + return 0; + } + if ((n = read(fd, username, len - 1)) < 1) { + (void)close(fd); + free(path); + return 0; + } + username[n] = '\0'; + if ((p = strpbrk(username, "\r\n"))) + *p = '\0'; -char *clearmem(vbp, len) - void *vbp; - int len; -{ - char *bp = (char *) vbp; - char *cp; - - cp = bp; - while (len-- > 0) - *cp++ = 0; - - return bp; + (void)close(fd); + free(path); + return 1; } +/* Generate a random string */ +static void +random_string(char *str, size_t len) +{ + static const char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890"; + char *p; + + if (len == 0) + return; + for (p = str; len > 1; len--) + *p++ = chars[arc4random() % (sizeof(chars) - 1)]; + *p = '\0'; +} -/* -** Main entry point into this daemon -*/ -int main(argc,argv) - int argc; - char *argv[]; +/* Change the output format */ +static void +change_format(const char *format, struct passwd *pw, char *dest, size_t len) { - int i, len; - struct sockaddr_in sin; - struct in_addr laddr, faddr; - int one = 1; - - int background_flag = 0; - int timeout = 0; - char *portno = "113"; - char *bind_address = (char *) NULL; - int set_uid = 0; - int set_gid = 0; - int inhibit_default_config = 0; - int opt_count = 0; /* Count of option flags */ - -#ifdef __convex__ - argc--; /* get rid of extra argument passed by inetd */ -#endif - - - if (isatty(0)) - background_flag = 1; - - /* - ** Prescan the arguments for "-f<config-file>" switches - */ - inhibit_default_config = 0; - for (i = 1; i < argc && argv[i][0] == '-'; i++) - if (argv[i][1] == 'f') - inhibit_default_config = 1; - - /* - ** Parse the default config file - if it exists - */ - if (!inhibit_default_config) - parse_config(NULL, 1); - - /* - ** Parse the command line arguments - */ - for (i = 1; i < argc && argv[i][0] == '-'; i++) { - opt_count++; - switch (argv[i][1]) - { - case 'b': /* Start as standalone daemon */ - background_flag = 1; - break; - - case 'w': /* Start from Inetd, wait mode */ - background_flag = 2; - break; - - case 'i': /* Start from Inetd, nowait mode */ - background_flag = 0; - break; - - case 't': - timeout = atoi(argv[i]+2); - break; - - case 'p': - portno = argv[i]+2; - break; - - case 'a': - bind_address = argv[i]+2; - break; - - case 'u': - if (isdigit(argv[i][2])) - set_uid = atoi(argv[i]+2); - else - { - struct passwd *pwd; - - pwd = getpwnam(argv[i]+2); - if (!pwd) - ERROR1("no such user (%s) for -u option", argv[i]+2); - else - { - set_uid = pwd->pw_uid; - set_gid = pwd->pw_gid; + struct group *gr; + const char *cp; + char **gmp; + int bp; + + if (len == 0) + return; + if ((gr = getgrgid(pw->pw_gid)) == NULL) + return; + + for (bp = 0, cp = format; *cp != '\0' && bp < 490; cp++) { + if (*cp != '%') { + dest[bp++] = *cp; + continue; } - } - break; - - case 'g': - if (isdigit(argv[i][2])) - set_gid = atoi(argv[i]+2); - else - { - struct group *grp; - - grp = getgrnam(argv[i]+2); - if (!grp) - ERROR1("no such group (%s) for -g option", argv[i]+2); - else - set_gid = grp->gr_gid; - } - break; - - case 'c': - charset_name = argv[i]+2; - break; - - case 'r': - indirect_host = argv[i]+2; - break; - - case 'l': /* Use the Syslog daemon for logging */ - syslog_flag++; - break; - - case 'o': - other_flag = 1; - break; - - case 'e': - unknown_flag = 1; - break; - - case 'V': /* Give version of this daemon */ - printf("[in.identd, version %s]\r\n", version); - exit(0); - break; - - case 'v': /* Be verbose */ - verbose_flag++; - break; - - case 'd': /* Enable debugging */ - debug_flag++; - break; - - case 'm': /* Enable multiline queries */ - multi_flag++; - break; - - case 'N': /* Enable users ".noident" files */ - noident_flag++; - break; - -#ifdef INCLUDE_CRYPT - case 'C': /* Enable encryption. */ - { - FILE *keyfile; - - if (argv[i][2]) - keyfile = fopen(argv[i]+2, "r"); - else - keyfile = fopen(PATH_DESKEY, "r"); - - if (keyfile == NULL) - { - ERROR("cannot open key file for option -C"); - } - else - { - char buf[1024]; - - if (fgets(buf, 1024, keyfile) == NULL) - { - ERROR("cannot read key file for option -C"); - } - else - { - init_encryption(buf); - crypto_flag++; - } - fclose(keyfile); + if (*++cp == '\0') + break; + switch (*cp) { + case 'u': + (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp, + pw->pw_name); + break; + case 'U': + (void)snprintf(&dest[bp], len - bp, "%d", pw->pw_uid); + break; + case 'g': + (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp, + gr->gr_name); + break; + case 'G': + (void)snprintf(&dest[bp], len - bp, "%d", gr->gr_gid); + break; + case 'l': + (void)snprintf(&dest[bp], len - bp, "%.*s", 490 - bp, + gr->gr_name); + bp += strlen(&dest[bp]); + if (bp >= 490) + break; + setgrent(); + while ((gr = getgrent()) != NULL) { + if (gr->gr_gid == pw->pw_gid) + continue; + for (gmp = gr->gr_mem; *gmp && **gmp; gmp++) { + if (strcmp(*gmp, pw->pw_name) == 0) { + (void)snprintf(&dest[bp], + len - bp, ",%.*s", + 490 - bp, gr->gr_name); + bp += strlen(&dest[bp]); + break; + } + } + if (bp >= 490) + break; + } + endgrent(); + break; + case 'L': + (void)snprintf(&dest[bp], len - bp, "%u", gr->gr_gid); + bp += strlen(&dest[bp]); + if (bp >= 490) + break; + setgrent(); + while ((gr = getgrent()) != NULL) { + if (gr->gr_gid == pw->pw_gid) + continue; + for (gmp = gr->gr_mem; *gmp && **gmp; gmp++) { + if (strcmp(*gmp, pw->pw_name) == 0) { + (void)snprintf(&dest[bp], + len - bp, ",%u", + gr->gr_gid); + bp += strlen(&dest[bp]); + break; + } + } + if (bp >= 490) + break; + } + endgrent(); + break; + default: + dest[bp] = *cp; + dest[bp+1] = '\0'; + break; } - } - break; -#endif - -#ifdef ALLOW_FORMAT - case 'n': /* Compatibility flag - just send the user number */ - format_flag = 1; - format = "%U"; - break; - - case 'F': /* Output format */ - format_flag = 1; - format = argv[i]+2; - break; -#endif - - case 'L': /* lie brazenly */ - liar_flag = 1; - if (*(argv[i]+2) != '\0') - lie_string = argv[i]+2; - else -#ifdef DEFAULT_LIE_USER - lie_string = DEFAULT_LIE_USER; -#else - ERROR("-L specified with no user name"); -#endif - break; - - default: - ERROR1("Bad option %s", argv[i]); - break; + bp += strlen(&dest[bp]); } - } - -#if defined(_AUX_SOURCE) || defined (SUNOS35) - /* A/UX 2.0* & SunOS 3.5 calls us with an argument XXXXXXXX.YYYY - ** where XXXXXXXXX is the hexadecimal version of the callers - ** IP number, and YYYY is the port/socket or something. - ** It seems to be impossible to pass arguments to a daemon started - ** by inetd. - ** - ** Just in case it is started from something else, then we only - ** skip the argument if no option flags have been seen. - */ - if (opt_count == 0) - argc--; -#endif - - /* - ** Path to kernel namelist file specified on command line - */ - if (i < argc) - path_unix = argv[i++]; - - /* - ** Path to kernel memory device specified on command line - */ - if (i < argc) - path_kmem = argv[i++]; - - - if (i < argc) - ERROR1("Too many arguments: ignored from %s", argv[i]); - - - /* - ** We used to call k_open here. But then the file descriptor - ** kd->fd open on /dev/kmem is shared by all child processes. - ** From the fork(2) man page: - ** o The child process has its own copy of the parent's descriptors. These - ** descriptors reference the same underlying objects. For instance, file - ** pointers in file objects are shared between the child and the parent - ** so that an lseek(2) on a descriptor in the child process can affect a - ** subsequent read(2) or write(2) by the parent. - ** Thus with concurrent (simultaneous) identd client processes, - ** they step on each other's toes when they use kvm_read. - ** - ** Calling k_open here was a mistake for another reason too: we - ** did not yet honor -u and -g options. Presumably we are - ** running as root (unless the in.identd file is setuid), and - ** then we can open kmem regardless of -u and -g values. - ** - ** - ** Open the kernel memory device and read the nlist table - ** - ** if (k_open() < 0) - ** ERROR("main: k_open"); - */ - - /* - ** Do the special handling needed for the "-b" flag - */ - if (background_flag == 1) - { - struct sockaddr_in addr; - struct servent *sp; - int fd; - - - if (!debug_flag) - { - if (fork()) - exit(0); - - close(0); - close(1); - close(2); - - if (fork()) - exit(0); - } - - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd == -1) - ERROR("main: socket"); - - if (fd != 0) - dup2(fd, 0); - - clearmem((void *) &addr, (int) sizeof(addr)); - - addr.sin_family = AF_INET; - if (bind_address == (char *) NULL) - addr.sin_addr.s_addr = htonl(INADDR_ANY); - else - { - if (isdigit(bind_address[0])) - addr.sin_addr.s_addr = inet_addr(bind_address); - else - { - struct hostent *hp; - - hp = gethostbyname(bind_address); - if (!hp) - ERROR1("no such address (%s) for -a switch", bind_address); - - /* This is ugly, should use memcpy() or bcopy() but... */ - addr.sin_addr.s_addr = * (unsigned long *) (hp->h_addr); - } - } - - if (isdigit(portno[0])) - addr.sin_port = htons(atoi(portno)); - else - { - sp = getservbyname(portno, "tcp"); - if (sp == (struct servent *) NULL) - ERROR1("main: getservbyname: %s", portno); - addr.sin_port = sp->s_port; + if (bp >= 490) { + (void)snprintf(&dest[490], len - 490, "..."); + bp = 493; } + dest[bp] = '\0'; +} -#ifdef SO_REUSEADDR - setsockopt(0, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one)); -#endif - - if (bind(0, (struct sockaddr *) &addr, sizeof(addr)) < 0) - ERROR("main: bind"); - } - - if (background_flag) - { - if (listen(0, 3) < 0) - ERROR("main: listen"); - } - - if (set_gid) - { - if (setgid(set_gid) == -1) - ERROR("main: setgid"); - /* Call me paranoid... PSz */ - if (getgid() != set_gid) - ERROR2("main: setgid failed: wanted %d, got GID %d", set_gid, getgid()); - if (getegid() != set_gid) - ERROR2("main: setgid failed: wanted %d, got EGID %d", set_gid, getegid()); - } - - if (set_uid) - { - if (setuid(set_uid) == -1) - ERROR("main: setuid"); - /* Call me paranoid... PSz */ - if (getuid() != set_uid) - ERROR2("main: setuid failed: wanted %d, got UID %d", set_uid, getuid()); - if (geteuid() != set_uid) - ERROR2("main: setuid failed: wanted %d, got EUID %d", set_uid, geteuid()); - } - - if (syslog_flag) { -#ifdef LOG_DAEMON - openlog("identd", LOG_PID, syslog_facility); -#else - openlog("identd", LOG_PID); -#endif - } - (void)getpwnam("xyzzy"); - (void)gethostbyname("xyzzy"); - - /* - ** Do some special handling if the "-b" or "-w" flags are used - */ - if (background_flag) - { - int nfds, fd; - struct pollfd set[1]; - struct sockaddr sad; - int sadlen; - - - /* - ** Set up the SIGCHLD signal child termination handler so - ** that we can avoid zombie processes hanging around and - ** handle childs terminating before being able to complete the - ** handshake. - */ -#if (defined(SVR4) || defined(hpux) || defined(__hpux) || defined(IRIX) || \ - defined(_CRAY) || defined(_AUX_SOURCE) || defined(sco) || \ - defined(LINUX)) - signal(SIGCHLD, SIG_IGN); -#else - signal(SIGCHLD, child_handler); -#endif - - set[0].fd = 0; - set[0].events = POLLIN; - - /* - ** Loop and dispatch client handling processes - */ - do - { -#ifdef USE_SIGALARM - /* - ** Terminate if we've been idle for 'timeout' seconds - */ - if (background_flag == 2 && timeout) - { - signal(SIGALRM, alarm_handler); - alarm(timeout); - } -#endif - - /* - ** Wait for a connection request to occur. - ** Ignore EINTR (Interrupted System Call). - */ - do - { -#ifndef USE_SIGALARM - if (timeout) - nfds = poll(set, 1, timeout * 1000); - else -#endif - nfds = poll(set, 1, INFTIM); - } while (nfds < 0 && errno == EINTR); - - /* - ** An error occurred in select? Just die - */ - if (nfds < 0) - ERROR("main: poll"); - - /* - ** Timeout limit reached. Exit nicely - */ - if (nfds == 0) - exit(0); - -#ifdef USE_SIGALARM - /* - ** Disable the alarm timeout - */ - alarm(0); -#endif - - /* - ** Accept the new client - */ - sadlen = sizeof(sad); - errno = 0; - fd = accept(0, &sad, &sadlen); - if (fd == -1) { - WARNING1("main: accept. errno = %d", errno); - continue; - } - - /* - ** And fork, then close the fd if we are the parent. - */ - child_pid = fork(); - } while (child_pid && (close(fd), 1)); - - /* - ** We are now in child, the parent has returned to "do" above. - */ - if (dup2(fd, 0) == -1) - ERROR("main: dup2: failed fd 0"); - - if (dup2(fd, 1) == -1) - ERROR("main: dup2: failed fd 1"); - - if (dup2(fd, 2) == -1) - ERROR("main: dup2: failed fd 2"); - } - - /* - ** Get foreign internet address - */ - len = sizeof(sin); - if (getpeername(0, (struct sockaddr *) &sin, &len) == -1) - { - /* - ** A user has tried to start us from the command line or - ** the network link died, in which case this message won't - ** reach to other end anyway, so lets give the poor user some - ** errors. - */ - perror("in.identd: getpeername()"); - exit(1); - } - - faddr = sin.sin_addr; - - -#ifdef STRONG_LOG - if (syslog_flag) - syslog(LOG_INFO, "Connection from %s", gethost(&faddr)); -#endif - - - /* - ** Get local internet address - */ - len = sizeof(sin); -#ifdef ATTSVR4 - if (t_getsockname(0, (struct sockaddr *) &sin, &len) == -1) -#else - if (getsockname(0, (struct sockaddr *) &sin, &len) == -1) -#endif - { - /* - ** We can just die here, because if this fails then the - ** network has died and we haven't got anyone to return - ** errors to. - */ +/* Just exit when we caught SIGALRM */ +static void +timeout_handler(int s) +{ + if (lflag) + syslog(LOG_DEBUG, "SIGALRM triggered, exiting..."); exit(1); - } - laddr = sin.sin_addr; - +} - /* - ** Get the local/foreign port pair from the luser - */ - parse(stdin, &laddr, &faddr); +/* This is to clean up zombie processes when in daemon mode */ +static void +waitchild(int s) +{ + while (waitpid(-1, NULL, WNOHANG) > 0) + continue; +} - exit(0); +/* Report errno through syslog and quit */ +static void +fatal(const char *func) +{ + if (lflag) + syslog(LOG_ERR, "%s: %m", func); + exit(1); } |
