This is identd.c in view mode; [Download] [Up]
/** $Header: /usr/src/nidentd-2.0/RCS/identd.c,v 2.0 92/05/04 17:42:54 nigelm Exp $
***
*** identd.c - An implementation of an RFC931 Authentication Daemon
*** ~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
***
***
*** Author: Nigel Metheringham
*** Department of Electronics, University of York, UK
*** nigelm@ohm.york.ac.uk
***
*** Very heavily based on:-
*** pauthd version 1.2
*** by Peter Eriksson (pen@lysator.liu.se) 30 Oct 1991
*** and
*** authd version 3.01
*** by Dan Bernstein (brnstnd@nyu.edu) 7 Feb 1991
***
*** $Log: identd.c,v $
*** Revision 2.0 92/05/04 17:42:54 nigelm
*** Version for release at base 2.0 version.
***
***
***
**/
/**
***
*** Include files - it needs tons of these, and every system has
*** a slightly different set!!!!!
***
**/
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef SYS5_INCLUDES
#include <bsd/sys/types.h>
#endif
#include <sys/param.h>
#include <netdb.h>
#include <nlist.h>
#include <pwd.h>
#include <signal.h>
#include "paths.h"
#ifdef SYS5_INCLUDES
#include <bsd/sys/socket.h>
#include <bsd/sys/socketvar.h>
#else
#include <sys/socket.h>
#include <sys/socketvar.h>
#endif
#include <sys/ioctl.h>
#define KERNEL
#define KERNEL_FEATURES
#include <sys/file.h>
#undef KERNEL
#undef KERNEL_FEATURES
#ifdef SYS5_INCLUDES
#include <sys/pcb.h>
#endif
#include <sys/user.h>
#ifdef SYS5_INCLUDES
#include <bsd/net/if.h>
#include <bsd/net/route.h>
#include <bsd/netinet/in.h>
#include <bsd/netinet/in_pcb.h>
#include <bsd/netinet/ip_var.h>
#include <bsd/netinet/tcp.h>
#include <bsd/netinet/tcp_timer.h>
#include <bsd/netinet/tcp_var.h>
#else
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#endif
#ifdef SYS5_KERNEL
/* This declares the kernel structures and is needed even
if you are compiling for a BSD universe on a mainly sys5
machine */
#include <sysv/sys/var.h>
#endif
/**
***
*** Global variables and things that go bump in the night
***
**/
int flagidentd = 0; /* Are we running as identd */
int flagpwnam = 0; /* Do we want the user name returned */
int debug_flag = 0; /* Squash that bug.... */
char * progname = "NOT-SET"; /* The name of this here program */
int local_port = 0; /* The local port for identd checking */
int remote_port = 0; /* The remote port for identd checking */
extern char optarg; /* For getopt */
extern int optind; /* For getopt */
extern int opterr; /* For getopt */
int kmemfd; /* File handle for kmem */
/**
***
*** Name list of things to fish out the kernel...
*** Of course this is different for every machine!
*** NB some compilers may gripe at the initialisers!
**/
struct nlist nl[] =
{
#ifdef SYS5_KERNEL
{ "file" },
{ "v" },
{ "tcb" },
{ "" }
#else
#ifdef NeXT
{ {"_file_list"} },
{ {"_max_file"} },
{ {"_tcb"} },
{ {""} }
#else
{ "_file" },
{ "_nfile" },
{ "_tcb" },
{ "" }
#endif
#endif
#define N_FILE 0 /* These are common for all architectures */
#define N_NFILE 1 /* at present, but may need moving into the */
#define N_TCB 2 /* machine dependant bits for wierder machines */
};
/**
***
*** Usage instructions
***
**/
usage()
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, "\tidentd [-d]\n");
fprintf(stderr, "\ttcpuname [-d] remote-addr remote-port local-addr local-port\n");
fprintf(stderr, "\ttcpuid [-d] remote-addr remote-port local-addr local-port\n");
fprintf(stderr, "\n");
exit(1);
}
/**
***
*** Version
***
**/
version()
{
fprintf(stderr, "%s\n\t$Revision: 2.0 $\n\t$Date\n");
fprintf(stderr, "\tby\tNigel Metheringham, University of York, UK (nigelm@ohm.york.ac.uk)\n");
fprintf(stderr, "\t\tPeter Eriksson (pen@lysator.liu.se)\n");
fprintf(stderr, "\t\tDan Bernstein (brnstnd@nyu.edu)\n");
exit(1);
}
/**
***
*** Last despairing message and suicide....!
***
**/
Error_Die(char * where,
char * message,
unsigned long x1,
unsigned long x2,
unsigned long x3,
unsigned long x4)
{
if (flagidentd && !debug_flag) {
printf("%d, %d: ERROR: UNKNOWN-ERROR\r\n", local_port, remote_port);
} else {
fprintf(stderr, "%s: ", progname);
fprintf(stderr, message, x1, x2, x3, x4);
fprintf(stderr, "/");
perror(where);
}
exit(3);
}
/**
***
*** Get a piece of kernel memory
*** The "what" parameter is just for error messages
***
**/
getbuf( unsigned long addr,
void * buf,
int len,
char * what)
{
if (lseek(kmemfd, addr, 0) == -1) {
Error_Die("getbuf", "lseek(0x%08x, \"%s\")/",
(unsigned long) addr, (unsigned long) what, 0, 0);
}
if (read(kmemfd, buf, len) < len) {
Error_Die("getbuf", "&%08x: read(%08x, %d, \"%s\")/", (unsigned long) addr,
(unsigned long) buf, (unsigned long) len, (unsigned long) what);
}
}
/**
***
*** Traverse the inpcb list until a match is found
***
***
**/
struct socket *getlist( struct inpcb *pcbp,
struct in_addr faddr,
int fport,
struct in_addr laddr,
int lport)
{
struct inpcb *head;
if (!pcbp)
return NULL; /* Someone gave us a duff one here */
head = pcbp->inp_prev;
do {
if (pcbp->inp_faddr.s_addr == faddr.s_addr &&
pcbp->inp_laddr.s_addr == laddr.s_addr &&
pcbp->inp_fport == fport &&
pcbp->inp_lport == lport )
return pcbp->inp_socket;
} while (pcbp->inp_next != head &&
getbuf((long) pcbp->inp_next, pcbp, sizeof(struct inpcb), "tcblist"));
return NULL; /* Not found */
}
/**
***
*** Locate the 'ucred' information
*** This routine varies a lot according to the type of machine
*** its being compiled for
**/
struct ucred *getinfo(struct in_addr faddr,
int fport,
struct in_addr laddr,
int lport)
{
static struct ucred ucb;
struct socket *sockp;
int i;
int nfile;
struct inpcb tcb; /* */
#ifdef NeXT
struct file file_entry;
#else
struct file * file_tab;
#endif
void * addr;
#ifdef SYS5_KERNEL
struct var kernel_params;
#endif
void * calloc(); /* I hate it when things are missed from header files */
/* -------------------- TCP PCB LIST -------------------- */
getbuf(nl[N_TCB].n_value, &tcb, sizeof(tcb), "tcb");
tcb.inp_prev = (struct inpcb *) nl[N_TCB].n_value;
sockp = getlist(&tcb, faddr, fport, laddr, lport);
if (!sockp)
return NULL;
/* -------------------- FILE DESCRIPTOR TABLE -------------------- */
/* These bits are for semi-conventional - ie non-Mach - machines */
#ifndef NeXT
/* Get the size of the file table */
#ifdef SYS5_KERNEL
getbuf(nl[N_NFILE].n_value, &kernel_params, sizeof(kernel_params), "kernel_params");
nfile = kernel_params.v_file; /* Size of file table */
#else SYS5_KERNEL
getbuf(nl[N_NFILE].n_value, &nfile, sizeof(nfile), "nfile");
#endif SYS5_KERNEL
/* Allocate space for a file table copy */
if ((file_tab = calloc((size_t) nfile, (size_t) sizeof(struct file))) == NULL) {
Error_Die("getinfo", "No memory", 0, 0, 0, 0);
}
/* Now get a copy of the file table */
#ifdef SYS5_KERNEL
/* NB for SYS5 the file parameter points directly to the start of the file table */
getbuf(nl[N_FILE].n_value, file_tab, sizeof(struct file)*nfile, "file[]");
#else SYS5_KERNEL
getbuf(nl[N_FILE].n_value, &addr, sizeof(addr), "&file");
getbuf(addr, file_tab, sizeof(struct file)*nfile, "file[]");
#endif SYS5_KERNEL
/* Locate the file descriptor that has the socket in question
open so that we can get the 'ucred' information */
for (i = 0; i < nfile; i++) {
/* This next bit is implemented as 2 ifs, cos its easier to debug that way! */
if (file_tab[i].f_count &&
file_tab[i].f_type == DTYPE_SOCKET) {
if ((struct socket *) file_tab[i].f_data == sockp) {
getbuf(file_tab[i].f_cred, &ucb, sizeof(ucb), "ucb");
return &ucb;
}
}
}
#else NeXT
/* So now we hit the fun Mach kernel structures */
getbuf(nl[N_FILE].n_value, &addr, sizeof(addr), "&file_table");
/* We only use nfile as a failsafe in case something goes wrong! */
getbuf(nl[N_NFILE].n_value, &nfile, sizeof(nfile), "nfile");
file_entry.links.next = addr;
do {
getbuf((unsigned long) file_entry.links.next, &file_entry,
sizeof(file_entry), "struct file");
if (file_entry.f_count &&
file_entry.f_type == DTYPE_SOCKET) {
if ((void *) file_entry.f_data == (void *) sockp) {
getbuf((unsigned long) file_entry.f_cred, &ucb, sizeof(ucb), "ucb");
return &ucb;
}
}
} while ((file_entry.links.next != addr) && (--nfile));
#endif NeXT
return NULL;
}
/**
***
*** Processes the flag arguments to the program,
*** also determines the invoking name of the program.
***
**/
void process_flags(int argc, char * argv[])
{
int c;
char * ptr;
/* Set up program name variable */
progname = argv[0];
for(ptr = argv[0]; *ptr; ptr++) {
if (*ptr == '/')
progname = ptr;
}
if (*progname == '/')
progname++;
/* Check the program name and modify behavior accordingly */
if ((!strcmp(progname,"identd"))
|| (!strcmp(progname,"in.identd"))) {
flagidentd = flagpwnam = 1;
} else {
if (!strcmp(progname,"tcpuname"))
flagpwnam = 1;
}
/* Work through any command line flags */
while ((c = getopt(argc, argv, "dV")) != EOF) {
switch (c) {
case 'V':
version();
break;
case 'd':
debug_flag = 1;
break;
default:
usage();
}
}
}
/**
***
*** Gets addresses of the current machine and the
*** Querying machine, reads socket numbers from stdin
***
**/
void get_identd_addresses(struct in_addr * local_addr,
int * local_port,
struct in_addr * remote_addr,
int * remote_port)
{
struct sockaddr_in sin;
int len;
/* Get the local/foreign port pair from the luser */
if (scanf("%d , %d", local_port, remote_port) != 2) {
Error_Die("get_identd_addresses", "Incorrect input/", 0, 0, 0, 0);
}
/* Foreign internet address */
len = sizeof(struct sockaddr_in);
if (getpeername(0,&sin,&len) == -1) {
Error_Die("get_identd_addresses", "getpeername failed/", 0, 0, 0, 0);
}
*remote_addr = sin.sin_addr;
/* Local internet address */
len = sizeof(struct sockaddr_in);
if (getsockname(0,&sin,&len) == -1) {
Error_Die("get_identd_addresses", "getsockname failed/", 0, 0, 0, 0);
}
*local_addr = sin.sin_addr;
}
/**
***
*** Gets addresses of the current machine and the
*** Querying machine, reads socket numbers from command line.
***
**/
void get_given_addresses(int argc,
char * argv[],
struct in_addr * local_addr,
int * local_port,
struct in_addr * remote_addr,
int * remote_port)
{
int x1, x2, x3, x4;
if(optind == 0)
optind++; /* Tacky getopts bug - grrrgh! */
if ((argc - optind) < 3)
Error_Die("get_given_addresses","need four arguments", 0, 0, 0, 0);
if(sscanf(argv[optind++],"%d.%d.%d.%d",&x1,&x2,&x3,&x4) < 4)
Error_Die("get_given_addresses","arg 1 must be a.b.c.d", 0, 0, 0, 0);
remote_addr->s_addr = (x1 << 24) | (x2 << 16) | (x3 <<8) | x4;
if(sscanf(argv[optind++],"%d",remote_port) < 1)
Error_Die("get_given_addresses","arg 2 must be integer", 0, 0, 0, 0);
if(sscanf(argv[optind++],"%d.%d.%d.%d",&x1,&x2,&x3,&x4) <4)
Error_Die("get_given_addresses","arg 3 must be a.b.c.d", 0, 0, 0, 0);
local_addr->s_addr = (x1 << 24) | (x2 << 16) | (x3 <<8) | x4;
if(sscanf(argv[optind++],"%d",local_port) < 1)
Error_Die("get_given_addresses","arg 4 must be integer", 0, 0, 0, 0);
}
/**
***
*** Dives into the deep end and drags out the kernel
*** addresses for required things from the name list
*** The list of things looked for is set at compile
*** time but may (will!) be different for each type
*** of machine!!
***
**/
void kernel_namelist(struct nlist nl[])
{
/* There are (of course) two main ways of doing this,
and one assumes that people will find other ways as well!
The #ifdef-ed sections select out the different techniques.
** The KVM method has been removed since I can't test it **
*/
#ifdef NeXT
/* I normally quite like NeXT, but fiddling with the names of
components of the nlist structure could change that, hence
the horrible defines just here */
#define N_NAME n_un.n_name
#else
#define N_NAME n_name
#endif
/* First we open the kernel memory special file */
if ((kmemfd = open(_PATH_KMEM, 0)) <= 0) {
Error_Die("kernel_namelist", "open %s", _PATH_KMEM, 0, 0, 0);
}
/* Dig into the Unix kernel... */
if (nlist(_PATH_UNIX, nl) != 0) {
if (debug_flag) { /* Here's some additional debug tracking */
int i;
for (i=0; (*(nl[i].N_NAME)); i++) {
if (nl[i].n_type == 0)
fprintf(stderr, "\t%s not found.\n", nl[i].N_NAME);
}
}
Error_Die("kernel_namelist", "nlist", 0, 0, 0 , 0);
}
}
/**
***
*** The main bit...
***
**/
main(int argc, char * argv[])
{
struct sockaddr_in sin;
int len;
struct in_addr local_addr, remote_addr;
struct ucred *ucp;
struct passwd *pwp;
/* Process the command flags, and figure out what program we are! */
process_flags(argc, argv);
/* Get the ether & socket addresses involved */
if (flagidentd) {
get_identd_addresses(&local_addr, &local_port, &remote_addr, &remote_port);
} else {
get_given_addresses(argc, argv, &local_addr, &local_port, &remote_addr, &remote_port);
}
/* Get data from the kernel namelists */
kernel_namelist(nl);
/* Next - get locate the specific TCP connection and return the
'ucred' - user credentials - information */
if ((ucp = getinfo(remote_addr,
htons(remote_port),
local_addr,
htons(local_port))) == NULL) {
/* This bit is done as a discrete error message because its a
different message to all the others! */
printf("%d, %d: ERROR: NO-USER\r\n", local_port, remote_port);
exit(37);
}
/* Then we should try to get the username - when required */
if (flagpwnam) {
if ((pwp = getpwuid(ucp->cr_ruid)) == NULL) {
Error_Die("main", "unable to lookup UID", 0, 0, 0, 0);
}
/* Hey! We finally made it!!! - output style depends on what we are! */
if (flagidentd) {
printf("%d, %d: USERID: UNIX: %s\r\n", local_port, remote_port, pwp->pw_name);
fflush(stdout);
} else
printf("%s\n", pwp->pw_name);
} else {
/* Just need to print UID */
printf("%d\n", ucp->cr_ruid);
}
/* Time for bed said..... */
exit(0);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.