Dears,

I reported a bug in mlterm a year ago. The report is still available at
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=329194 It is concerned
with managing records that are used by who command to list users'
sessions (utmp records).

Because I noticed no activity to fix the bug I decided to do it myself.
Now I would like to share the fix. Let me note that I use version 2.9.2
included in Sarge bug the fix will work with 2.9.3 too because the relevant
files have not changed.

The problem explanation. It is possible to enable in configuration time
(before the compilation) utmp records management. In the end this
implies calling of the libc function login (see man 3 login). The call
is unfortunately done in a wrong way.

There is an idea of connecting login and logout calls with an instance
of a data structure in mlterm. It nearly implies that the instance must
be created in the parental process (because the child is going to do an
exec call to start (probably) a shell). It is the case in the current
implementation.

On the other hand the couple of the (newly started) terminal's master
and slave file descriptors is going only to become stdin and stdout (due
to dup2 calls) in the child process.

The used login function derives some information (ut_line, see man 3
login) from stdin, stdout or stderr file descriptors. At least one of
them is expected to be tty. None is when the terminal is launched
directly from a window manager. Thus login fails guessing ut_line and
gives adding a new utmp record up. (Consequence: In another case when
the terminal is launched from another terminal its parent is
registered.)

It is possible to imaginate many solutions. I had considered many and
have choosen one that I have found to be the best and also keeping
original ideas. It only patches the misuse of the library login
function. Instead of let the library function to guess what we know (and
fail) I suggest to do the same work in our own and use the known
information.

I have implemented and debugged the suggested change (file
kiklib/src/kik_utmp_login.c). Consider please the change applying.

Best regards,
        David Kolovratník
/*
 *	$Id: kik_utmp_login.c,v 1.2 2002/05/09 11:02:49 h_minami Exp $
 */

#include  "kik_utmp.h"

#include  <stdio.h>	/* NULL */
#include  <pwd.h>
#include  <sys/types.h>
#include  <unistd.h>	/* getuid */
#include  <string.h>	/* strncmp */
#include  <time.h>	/* time */
#if  1
/* *BSD has utmp.h anyway though login/logout aren't defined in it */
#include  <utmp.h>	/* login/logout(glibc2) you have to link libutil. */
#endif
#if  0
/* glibc(linux) doesn't have util.h */
#include  <util.h>	/* login/logout(*BSD) you have to link libutil. */
#endif

#include  "kik_util.h"		/* K_MIN */
#include  "kik_mem.h"		/* malloc/free */
#include  "kik_config.h"	/* HAVE_SETUTENT */
#include  "kik_privilege.h"

struct  kik_utmp
{
	char  ut_line[UT_LINESIZE] ;
} ;

/* simplified implementation of login library function */
/* glibc-2.3.2.ds1/glibc-2.3.2/login/login.c */
static void kik_login(struct utmp *ut)
{
  /* Fill in those fields we supply.  */
  /* I do not hesitate to modifi the given ut structure in a private function */
#if _HAVE_UT_TYPE - 0
  ut->ut_type = USER_PROCESS;
#endif
#if _HAVE_UT_PID - 0
  ut->ut_pid = getpid ();
#endif

  /* ut->ut_line is supposed to be filled in */

  /* Tell that we want to use the UTMP file.  */
  if (utmpname (_PATH_UTMP) == 0)
    {
      /* Open UTMP file.  */
      setutent ();

      /* Write the entry.  */
      pututline (ut);

      /* Close UTMP file.  */
      endutent ();
    }

  /* Update the WTMP file.  Here we have to add a new entry.  */
  updwtmp (_PATH_WTMP, ut);
}


/* --- global functions --- */

kik_utmp_t
kik_utmp_new(
	char *  tty ,
	char *  host ,
	int  pty_fd
	)
{
	kik_utmp_t  utmp ;
	struct utmp ut;
	struct passwd *  pwent;
	char *  pw_name;

	if( ( utmp = malloc( sizeof( *utmp))) == NULL)
	{
		return  NULL ;
	}

/* unnecessary ? */
#if  0
#ifdef  HAVE_SETUTENT
	setutent();
#endif
#endif

	memset( &ut , 0 , sizeof( ut)) ;

	if( ( pwent = getpwuid( getuid())) == NULL || pwent->pw_name == NULL)
	{
		pw_name = "?" ;
	}
	else
	{
		pw_name = pwent->pw_name ;
	}

	/*
	 * user name field is named ut_name in *BSD and is ut_user in glibc2 ,
	 * but glibc2 also defines ut_name as an alias of ut_user for backward
	 * compatibility.
	 */
	strncpy( ut.ut_name, pw_name, K_MIN(sizeof( ut.ut_name)-2, strlen(pw_name))) ;
	ut.ut_name[sizeof( ut.ut_name)-1] = 0;
	
	if( strncmp( tty, "/dev/", K_MIN(5,strlen(tty))) == 0)
	{
		/* skip /dev/ prefix */
		tty += 5 ;
	}
	
	if( strncmp( tty, "pts", K_MIN(3,strlen(tty))) != 0 &&
	    strncmp( tty, "pty", K_MIN(3,strlen(tty))) != 0 &&
	    strncmp( tty, "tty", K_MIN(3,strlen(tty))) != 0)
	{
		free(utmp);

		return NULL;
	}

#ifndef  HAVE_SETUTENT
	/* ut.ut_line must be filled before login() on bsd. */
	memcpy( ut.ut_line , tty , K_MIN(sizeof(ut.ut_line),strlen(tty))) ;
#endif
	
	ut.ut_time =  time(NULL);
	memcpy( ut.ut_host , host , K_MIN(sizeof( ut.ut_host),strlen(host)));
	kik_priv_restore_euid() ;/* useless? */
	kik_priv_restore_egid() ;

	strcpy(ut.ut_line, tty);
	/* login does not give us error information... */
	kik_login(&ut);
	
	kik_priv_change_euid( getuid()) ;
	kik_priv_change_egid( getgid()) ;
	memcpy(utmp->ut_line , ut.ut_line ,  sizeof( utmp->ut_line)) ;

	return utmp;
}

int
kik_utmp_delete(
	kik_utmp_t  utmp
	)
{
	kik_priv_restore_euid() ;
	kik_priv_restore_egid() ;

	logout(utmp->ut_line);
	logwtmp(utmp->ut_line,"","") ;
	
	kik_priv_change_euid( getuid()) ;
	kik_priv_change_egid( getgid()) ;

/* unnecessary ? */
#if  0
#ifdef  HAVE_SETUTENT
	endutent();
#endif
#endif

	free(utmp) ;

	return  1 ;
}

Reply via email to