/*
** CNS Secure identd (linux, and no masquerading option).
**  $Id: CNSidentd.c,v 1.2 2001/05/27 21:47:46 lkm Exp lkm $
**
** gcc -D_LINUX_ -O2 -o CNSidentd CNSidentd.c
** gcc -D_FREEBSD_ -O2 -o CNSidentd CNSidentd.c
**
**
** fake ident can be in ~/.CNSident
** (must be readable for all)
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <pwd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#ifdef _FREEBSD_
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/protosw.h>

#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_seq.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>
#endif

#ifndef IDENTD_PORT
#define IDENTD_PORT 113
#endif

#define NOBODY_UID 99 
#define NOBODY_GID 99 

int my_sock;
struct sigaction gotsig;

int slain(int signal)
{
printf("Closed cuz of signal %i\n",signal);
close(my_sock);
exit(-1);
return(0);
}

#ifdef _FREEBSD_
int get_uid_FREEBSD (struct in_addr laddr, u_short lp,
						struct in_addr raddr, u_short rp)
{
        char *mibvar = "net.inet.tcp.pcblist";
        char *buf;
        struct tcpcb *tp;
        struct inpcb *inp;
        struct xinpgen *xig, *oxig;
        struct xsocket *so;
        size_t len;

        if (sysctlbyname (mibvar, 0, &len, 0, 0) < 0) {
                return NOBODY_UID;
        }
//        buf = xmalloc (len);
		buf = (char*)malloc(sizeof(char) * len);
        
        if (sysctlbyname (mibvar, buf, &len, 0, 0) < 0) {
                free (buf);
                return NOBODY_UID;
        }
        
        oxig = xig = (struct xinpgen *)buf;
        for (xig = (struct xinpgen *)((char *)xig + xig->xig_len);
                        xig->xig_len > sizeof(struct xinpgen);
                        xig = (struct xinpgen *)((char *)xig + xig->xig_len)) {
                tp = &((struct xtcpcb *)xig)->xt_tp;
                inp = &((struct xtcpcb *)xig)->xt_inp;
                so = &((struct xtcpcb *)xig)->xt_socket;
                if (so->xso_protocol != IPPROTO_TCP)    continue;
                if (inp->inp_gencnt > oxig->xig_gen)    continue;
                if (inet_lnaof(inp->inp_laddr) == INADDR_ANY)
                        continue;

                if ((raddr.s_addr) == inp->inp_faddr.s_addr
                &&  (laddr.s_addr) == inp->inp_laddr.s_addr
                && rp == ntohs (inp->inp_fport)
                && lp == ntohs (inp->inp_lport)) {
                        free (buf);
                        return so->so_uid;
                }       
        }
        free (buf);
        return NOBODY_UID;
}
	
#endif

#ifdef _LINUX_
int get_uid_LINUX(struct in_addr me, int port1, struct in_addr peer, int port2)
{
FILE *fp = NULL;
u_char buf[1024];
struct in_addr local, remote;
int rport1, rport2, uid;
fp = fopen("/proc/net/tcp", "r");
if (fp == NULL)
	{
	perror("fopen");
	return(NOBODY_UID);
	}

fgets(buf, sizeof(buf) - 1, fp);

while (fgets(buf, sizeof(buf) - 1, fp)) {
	if (sscanf(buf, "%*d: %lX:%x %lX:%x %*x %*X:%*X %*x:%*X %*x %d %*d %*d",
		(long *) &local, &rport1, (long *) &remote, &rport2, &uid) == 5)
		{
		if (local.s_addr == me.s_addr &&
			remote.s_addr == peer.s_addr &&
			port1 == rport1 && port2 == rport2)
			{
			fclose(fp);
			return(uid);
			}
		}
	}
fclose(fp);
return(NOBODY_UID);
}
#endif

int process(int sock, int port1, int port2)
{
char *answer, *rep;
struct sockaddr_in me, peer;
struct passwd *myuser;
int len_me, len_peer, uid;
FILE *fp = NULL;
u_char buf[256];

answer = (char*) malloc(sizeof(char) * 1024);
len_me = sizeof(struct sockaddr);
len_peer = sizeof(struct sockaddr);
if(getpeername(sock,(struct sockaddr *) &peer, &len_peer))
		perror("getpeername"),exit(1);
if(getsockname(sock,(struct sockaddr *) &me,   &len_me))
		perror("getsockname"),exit(1);

uid = NOBODY_UID;

#ifdef _LINUX_
uid = get_uid_LINUX(me.sin_addr,port1,peer.sin_addr,port2);
#endif
#ifdef _FREEBSD_
uid = get_uid_FREEBSD(me.sin_addr,port1,peer.sin_addr,port2);
#endif

myuser = (struct passwd *)getpwuid(uid);
//printf("%s\n",myuser->pw_name);
	
sprintf(answer,"%d , %d : USERID : UNIX : %s\r\n",
            port1, port2,(myuser==NULL)?"nobody":myuser->pw_name);

// We have now to see if ~user/.CNSident exists, and read it, and get our 
// fake ident.
if (myuser != NULL)
	{
	rep = (char*)malloc(sizeof(char) * strlen(myuser->pw_dir) + 10);
	sprintf(rep,"%s/.CNSident", myuser->pw_dir);
	fp = fopen(rep, "r");
	if (fp != NULL)
		{
		fgets(buf, sizeof(buf) - 1, fp);
		fclose(fp);
		if(strlen(buf) > 0)
			{
			buf[strlen(buf)-1] = '\0';
			if (getpwnam(buf) == NULL)
				sprintf(answer,"%d , %d : USERID : UNIX : %s\r\n",
													port1, port2,buf);
			}
		} 
	}


if (write(sock, answer, strlen(answer)) == -1 )
	{
	perror("write");
	}
close(sock);
return(0);
}

int got_cnx(int my_sock)
{
char *buf;
int port1,port2;
int l;
buf = (char*) malloc(sizeof(char) * 256);

l = read(my_sock,buf,256);

if (2==sscanf(buf,"%d , %d",&port1,&port2))
	{
	free(buf);
	process(my_sock,port1,port2);
	}
else
	{
	free(buf);
	close(my_sock);
	}
}

/* Main function */
int main(int argn, char **argv)
{
int cl_sock;
struct sockaddr_in adresse;
int longueur, st, i;

gotsig.sa_handler=(void*)slain;
sigemptyset(&gotsig.sa_mask);
sigaction(SIGINT,&gotsig,NULL);

for (i=0; i<NOFILE; i++)
      close(i);

my_sock = socket(AF_INET, SOCK_STREAM, 0);
adresse.sin_family=AF_INET;
adresse.sin_addr.s_addr=INADDR_ANY;
adresse.sin_port=htons(IDENTD_PORT);
longueur = sizeof(adresse);

// attachement sur le port (bind) & ouverture (listen)
if(bind(my_sock,(struct sockaddr*)&adresse,longueur))
        perror("bind"), exit(1);
if(listen(my_sock,128))
        perror("listen"), exit(2);
setuid(NOBODY_UID);
setgid(NOBODY_UID);
seteuid(NOBODY_UID);

if(fork()) exit(0);
else setsid();

while(1)
	{
	if((cl_sock = accept(my_sock,(struct sockaddr*)&adresse, &longueur)) == -1)
		{
		perror("accept");
		}
	else
		{
		switch(fork())
			{
			case -1:printf("No more ressources\n");break;
			case 0:  // fils
			close(my_sock);
			alarm(30);	
			got_cnx(cl_sock);
			_exit(0);
			break;
			default:
			close(cl_sock);	
			wait(&st);
			break;
			}
		}
	}
return(0);
}

