diff options
Diffstat (limited to 'src/util')
42 files changed, 2393 insertions, 574 deletions
diff --git a/src/util/ChangeLog b/src/util/ChangeLog index 3862b25..39731b1 100644 --- a/src/util/ChangeLog +++ b/src/util/ChangeLog @@ -1,3 +1,22 @@ +2001-02-21 Tom Yu <tlyu@mit.edu> + + * mkrel: When generating multiple tarballs, also generate a + consolidated tarball. + +2001-02-06 Tom Yu <tlyu@mit.edu> + + * mkrel: Default to making a single tarball. + +2001-01-28 Tom Yu <tlyu@mit.edu> + + * Makefile.in (all-prerecurse): Move aix.bincmds rule to + all-prerecurse so it gets built before the subdirectories of + src/util. + + * makeshlib.sh: Use the linker flag -berok so that unresolved + symbols don't turn into link-time errors for building shared libs + on AIX. + 1999-10-26 Tom Yu <tlyu@mit.edu> * Makefile.in: Clean up usage of CFLAGS, CPPFLAGS, DEFS, DEFINES, diff --git a/src/util/Makefile.in b/src/util/Makefile.in index 3b4fd5d..f5d91d4 100644 --- a/src/util/Makefile.in +++ b/src/util/Makefile.in @@ -77,7 +77,7 @@ makeshlib: $(srcdir)/makeshlib.sh Makefile # # We only need this for AIX, but we generate it for all systems. # -all-unix:: aix.bincmds +all-prerecurse:: aix.bincmds aix.bincmds: Makefile echo libpath $(KRB5_LIBDIR):`pwd`/$(TOPLIBD):/usr/lib:/lib >aix.bincmds diff --git a/src/util/db2/ChangeLog b/src/util/db2/ChangeLog index 7972728..9ce240a 100644 --- a/src/util/db2/ChangeLog +++ b/src/util/db2/ChangeLog @@ -1,3 +1,7 @@ +2000-05-01 Nalin Dahyabhai <nalin@redhat.com> + + * hash/dbm.c (kdb2_dbm_open): Don't overflow buffer "path". + 1999-08-15 Tom Yu <tlyu@mit.edu> * README.NOT.SLEEPYCAT.DB: New file; pointer to README to diff --git a/src/util/db2/hash/dbm.c b/src/util/db2/hash/dbm.c index 50921de..aa96766 100644 --- a/src/util/db2/hash/dbm.c +++ b/src/util/db2/hash/dbm.c @@ -168,8 +168,9 @@ kdb2_dbm_open(file, flags, mode) info.cachesize = 0; info.hash = NULL; info.lorder = 0; - (void)strcpy(path, file); - (void)strcat(path, DBM_SUFFIX); + (void)strncpy(path, file, sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + (void)strncat(path, DBM_SUFFIX, sizeof(path) - 1 - strlen(path)); return ((DBM *)__hash_open(path, flags, mode, &info, 0)); } diff --git a/src/util/et/ChangeLog b/src/util/et/ChangeLog index 345416e..2cda2d7 100644 --- a/src/util/et/ChangeLog +++ b/src/util/et/ChangeLog @@ -1,3 +1,51 @@ +2001-10-31 Miro Jurisic <meeroh@mit.edu> + + * et_c.awk, et_c.pl, et_h.awk, et_h.pl: Fixed the incorrect + #if defined(unix) ... which was accidentally reintroduced + +2001-10-31 Danilo Almeida <dalmeida@mit.edu> + + * com_err.h: Windows should include <win-mac.h> as opposed + to <Kerberos5/win-mac.h> + +2001-10-29 Miro Jurisic <meeroh@mit.edu> + * pullup from krb5-1-2 branch after krb5-1-2-2-bp + * error_message.c: fixed busted parentheses + * error_message.c: call through to strerror on Mac OS X + * error_message.c: call through to ErrorLib on Mac OS X + * et.pbexp: export com_err() on Mac OS X + * error_massage.c: #include <KerberosSupport/ErrorLib.h> on Mac OS + * com_err.h: #include <Kerberos5/win-mac.h> on Mac OS + * et_c.awk, et_c.pl, et_h.awk, et_h.pl: Updated Mac OS #defines + and #includes for new header layout and Mac OS X frameworks + +2000-10-08 Miro Jurisic <meeroh@mit.edu> + + * et_c.perl, et_h.perl: + Renamed to et_c.pl and et_h.pl because the extension is used + as a newline separator heuristic in MacPerl parser + +2000-10-08 Miro Jurisic <meeroh@mit.edu> + + * et_c.perl, et_h.perl: + Removed #! from the first line to avoid confusing MacPerl + +2000-10-02 Alexandra Ellwood <lxs@mit.edu> + + * com_err.h, error_message.c, et.pbexp, et_c.awk, et_h.awk: + conditionalized com_err so it doesn't need to export et_list + on Mac OS X + +2000-05-07 Miro Jurisic <meeroh@mit.edu> + + * com_err.c (default_com_err_proc): use strncpy + where strncpy was meant (typo in Nalin's patch) + +2000-05-01 Nalin Dahyabhai <nalin@redhat.com> + + * com_err.c (default_com_err_proc) [_MSDOS || _WIN32 || + macintosh]: Don't overflow buffer "errbuf". + 2000-02-23 Ken Raeburn <raeburn@mit.edu> * Makefile.in (com_err.o): Depends on com_err.c. diff --git a/src/util/et/com_err.c b/src/util/et/com_err.c index 31da130..7bb0810 100644 --- a/src/util/et/com_err.c +++ b/src/util/et/com_err.c @@ -50,15 +50,18 @@ static void default_com_err_proc(whoami, code, fmt, ap) char errbuf[1024] = ""; if (whoami) { - strcat (errbuf, whoami); - strcat (errbuf, ": "); + errbuf[sizeof(errbuf) - 1] = '\0'; + strncat (errbuf, whoami, sizeof(errbuf) - 1 - strlen(errbuf)); + strncat (errbuf, ": ", sizeof(errbuf) - 1 - strlen(errbuf)); } if (code) { - strcat (errbuf, error_message(code)); - strcat (errbuf, " "); + errbuf[sizeof(errbuf) - 1] = '\0'; + strncat (errbuf, error_message(code), sizeof(errbuf) - 1 - strlen(errbuf)); + strncat (errbuf, " ", sizeof(errbuf) - 1 - strlen(errbuf)); } if (fmt) vsprintf (errbuf + strlen (errbuf), fmt, ap); + errbuf[sizeof(errbuf) - 1] = '\0'; #ifdef macintosh MacMessageBox(errbuf); diff --git a/src/util/et/com_err.h b/src/util/et/com_err.h index 7a8858b..f1acb70 100644 --- a/src/util/et/com_err.h +++ b/src/util/et/com_err.h @@ -13,7 +13,11 @@ #ifndef __COM_ERR_H #if defined(_MSDOS) || defined(_WIN32) || defined(macintosh) +#ifdef _WIN32 #include <win-mac.h> +#else +#include <Kerberos5/win-mac.h> +#endif #if defined(macintosh) && defined(__CFM68K__) && !defined(__USING_STATIC_LIBS__) #pragma import on #endif @@ -80,7 +84,7 @@ KRB5_DLLIMP extern errcode_t KRB5_CALLCONV add_error_table KRB5_DLLIMP extern errcode_t KRB5_CALLCONV remove_error_table ET_P((const struct error_table FAR *)); -#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) +#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) && !defined(__MACH__) /* * The display routine should be application specific. A global hook, * may cause inappropriate display procedures to be called between diff --git a/src/util/et/error_message.c b/src/util/et/error_message.c index b4a0537..4a5de74 100644 --- a/src/util/et/error_message.c +++ b/src/util/et/error_message.c @@ -27,8 +27,9 @@ #include "com_err.h" #include "error_table.h" -#ifdef macintosh -#include <ErrorLib.h> +#if defined(macintosh) || (defined(__MACH__) && defined(__APPLE__)) + #include <KerberosSupport/KerberosSupport.h> + #include <KerberosSupport/ErrorLib.h> #endif #if defined(_MSDOS) || defined(_WIN32) @@ -48,7 +49,7 @@ extern const int sys_nerr; static char buffer[ET_EBUFSIZ]; -#if (defined(_MSDOS) || defined(_WIN32) || defined(macintosh)) +#if (defined(_MSDOS) || defined(_WIN32) || defined(macintosh) || (defined(__MACH__) && defined(__APPLE__))) static struct et_list * _et_list = (struct et_list *) NULL; #else /* Old interface compatibility */ @@ -150,12 +151,19 @@ KRB5_DLLIMP const char FAR * KRB5_CALLCONV error_message(code) oops: -#if defined(macintosh) +#if TARGET_OS_MAC { /* This may be a Mac OS Toolbox error or an MIT Support Library Error. Ask ErrorLib */ if (GetErrorLongString(code, buffer, ET_EBUFSIZ - 1) == noErr) { return buffer; } + +#if TARGET_API_MAC_OSX + /* ComErr and ErrorLib don't know about this error, ask the system */ + /* Of course there's no way to tell if it knew what error it got */ + return (strerror (code)); +#endif + } #endif diff --git a/src/util/et/et.pbexp b/src/util/et/et.pbexp new file mode 100644 index 0000000..3109761 --- /dev/null +++ b/src/util/et/et.pbexp @@ -0,0 +1,10 @@ +# +# comerr library Macintosh export file +# +# $Header$ + +_com_err +_com_err_va +_error_message +_add_error_table +_remove_error_table diff --git a/src/util/et/et_c.awk b/src/util/et/et_c.awk index 94b258f..c9ecc6f 100644 --- a/src/util/et/et_c.awk +++ b/src/util/et/et_c.awk @@ -209,14 +209,14 @@ END { tab_base_low, table_item_count) > outfile } print "" > outfile - print "#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh)" > outfile + print "#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) && !(defined(__MACH__) && defined(__APPLE__))" > outfile print "struct et_list {" > outfile print " struct et_list *next;" > outfile print " const struct error_table * table;" > outfile print "};" > outfile print "extern struct et_list *_et_list;" > outfile print "static struct et_list link = { 0, 0 };" > outfile - print "void initialize_" table_name "_error_table (NOARGS) {" > outfile + print "void initialize_" table_name "_error_table (NOARGS) {" > outfile print " if (!link.table) {" > outfile print " link.next = _et_list;" > outfile print " link.table = &et_" table_name "_error_table;" > outfile diff --git a/src/util/et/et_c.perl b/src/util/et/et_c.pl index 6af7179..83bce3b 100644 --- a/src/util/et/et_c.perl +++ b/src/util/et/et_c.pl @@ -1,9 +1,3 @@ -#!/afs/athena/contrib/perl5/p -eval 'exec /afs/athena/contrib/perl5/arch/sun4x_55/bin/perl -S $0 ${1+"$@"}' - if $running_under_some_shell; - # this emulates #! processing on NIH machines. - # (remove #! line above if indigestible) - eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift; # process any FOO=bar switches @@ -279,7 +273,7 @@ else { &Pick('>', $outfile) && (print $fh ''); &Pick('>', $outfile) && - (print $fh '#if defined(unix) || defined(_AIX)'); + (print $fh '#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) && !(defined(__MACH__) && defined(__APPLE__))'); &Pick('>', $outfile) && (print $fh 'struct et_list {'); &Pick('>', $outfile) && diff --git a/src/util/et/et_h.awk b/src/util/et/et_h.awk index 2521886..2ce75bf 100644 --- a/src/util/et/et_h.awk +++ b/src/util/et/et_h.awk @@ -111,7 +111,11 @@ c2n["_"]=63 print " * This file is automatically generated; please do not edit it." > outfile print " */" > outfile print "" > outfile + print "#if defined(macintosh) || (defined(__MACH__) && defined(__APPLE__))" > outfile + print "#include <KerberosComErr/KerberosComErr.h>" > outfile + print "#else" > outfile print "#include <com_err.h>" > outfile + print "#endif" > outfile print "" > outfile } @@ -148,7 +152,7 @@ END { print "" > outfile print "extern struct error_table et_" table_name "_error_table;" > outfile print "" > outfile - print "#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh)" > outfile + print "#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) && !(defined(__MACH__) && defined(__APPLE__))" > outfile print "/* for compatibility with older versions... */" > outfile print "extern void initialize_" table_name "_error_table ();" > outfile print "#define init_" table_name "_err_tbl initialize_" table_name "_error_table" > outfile diff --git a/src/util/et/et_h.perl b/src/util/et/et_h.pl index b477faf..a5d5507 100644 --- a/src/util/et/et_h.perl +++ b/src/util/et/et_h.pl @@ -1,9 +1,3 @@ -#!/afs/athena/contrib/perl5/p -eval 'exec /afs/athena/contrib/perl5/arch/sun4x_55/bin/perl -S $0 ${1+"$@"}' - if $running_under_some_shell; - # this emulates #! processing on NIH machines. - # (remove #! line above if indigestible) - eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_0-9]+=)(.*)/ && shift; # process any FOO=bar switches @@ -138,8 +132,16 @@ line: while (<>) { &Pick('>', $outfile) && (print $fh ''); &Pick('>', $outfile) && + (print $fh '#if defined(macintosh) || (defined(__MACH__) && defined(__APPLE__))'); + &Pick('>', $outfile) && + (print $fh '#include <KerberosComErr/KerberosComErr.h>'); + &Pick('>', $outfile) && + (print $fh '#else'); + &Pick('>', $outfile) && (print $fh '#include <com_err.h>'); &Pick('>', $outfile) && + (print $fh '#endif'); + &Pick('>', $outfile) && (print $fh ''); } @@ -192,7 +194,7 @@ else { &Pick('>', $outfile) && (print $fh ''); &Pick('>', $outfile) && - (print $fh '#if defined(unix) || defined(_AIX)'); + (print $fh '#if !defined(_MSDOS) && !defined(_WIN32) && !defined(macintosh) && !(defined(__MACH__) && defined(__APPLE__))'); &Pick('>', $outfile) && (print $fh '/* for compatibility with older versions... */'); &Pick('>', $outfile) && diff --git a/src/util/makeshlib.sh b/src/util/makeshlib.sh index ec485ca..303c0ce 100644 --- a/src/util/makeshlib.sh +++ b/src/util/makeshlib.sh @@ -38,16 +38,14 @@ case $host in stat=$? if [ $stat -eq 0 ] ; then if test "$HAVE_GCC" = "yes" ; then - - - $CC -o shr.o.$version $library -nostartfiles -Xlinker -bgcbypass:1 -Xlinker -bfilelist -Xlinker -bM:SRE -Xlinker -bE:${library}.syms $LDFLAGS -lc + $CC -o shr.o.$version $library -nostartfiles -Xlinker -bgcbypass:1 -Xlinker -bfilelist -Xlinker -bM:SRE -Xlinker -bE:${library}.syms -Xlinker -berok $LDFLAGS -lc else # Pull in by explicit pathname so we don't get gnu ld if # installed (it could be even if we chose not to use gcc). # Better still would be to do this through $CC -- how do # we get crt0.o left out? - echo /bin/ld -o shr.o.$version $library -H512 -T512 -bnoentry -bM:SRE $LDFLAGS -bgcbypass:1 -bnodelcsect -bE:${library}.syms $libdirfl $liblist -lc - /bin/ld -o shr.o.$version $library -H512 -T512 -bnoentry -bM:SRE $LDFLAGS -bgcbypass:1 -bnodelcsect -bE:${library}.syms -lc + echo /bin/ld -o shr.o.$version $library -H512 -T512 -bnoentry -bM:SRE $LDFLAGS -bgcbypass:1 -bnodelcsect -bE:${library}.syms -berok $libdirfl $liblist -lc + /bin/ld -o shr.o.$version $library -H512 -T512 -bnoentry -bM:SRE $LDFLAGS -bgcbypass:1 -bnodelcsect -bE:${library}.syms -berok -lc fi stat=$? if [ $stat -eq 0 ] ; then diff --git a/src/util/mkrel b/src/util/mkrel index e534a47..a086450 100644 --- a/src/util/mkrel +++ b/src/util/mkrel @@ -3,12 +3,15 @@ repository=:kserver:cvs.mit.edu:/cvs/krbdev dodoc=t dosrc=t checkout=t +multitar=nil while test $# -gt 2; do case $1 in --srconly) dodoc=nil;; --doconly) dosrc=nil;; + --multi*) + multitar=t;; --repository) shift; repository=$1;; --nocheckout) @@ -136,22 +139,25 @@ fi echo "Generating tarfiles..." GZIP=-9; export GZIP -if test $dosrc = t; then - gtar --exclude $reldir/src/lib/crypto \ - --exclude $reldir/src/lib/des425 \ - --exclude $reldir/doc \ - -zcf ${reldir}.src.tar.gz $reldir - - gtar zcf ${reldir}.crypto.tar.gz \ - $reldir/src/lib/crypto \ - $reldir/src/lib/des425 -fi - -if test $dodoc = t; then - gtar zcf ${reldir}.doc.tar.gz $reldir/doc $reldir/README +if test $multitar = t; then + if test $dosrc = t; then + gtar --exclude $reldir/src/lib/crypto \ + --exclude $reldir/src/lib/des425 \ + --exclude $reldir/doc \ + -zcf ${reldir}.src.tar.gz $reldir + + gtar zcf ${reldir}.crypto.tar.gz \ + $reldir/src/lib/crypto \ + $reldir/src/lib/des425 + fi + if test $dodoc = t; then + gtar zcf ${reldir}.doc.tar.gz $reldir/doc $reldir/README + fi + ls -l ${reldir}.*.tar.gz fi -ls -l ${reldir}.*.tar.gz +gtar zcf ${reldir}.tar.gz $reldir +ls -l ${reldir}.tar.gz echo "Done." diff --git a/src/util/profile/ChangeLog b/src/util/profile/ChangeLog index 172d7ac..0139378 100644 --- a/src/util/profile/ChangeLog +++ b/src/util/profile/ChangeLog @@ -1,3 +1,32 @@ +2001-11-05 Tom Yu <tlyu@mit.edu> + + * Makefile.in (LIBMINOR): Bump due to error table changes. + +2001-09-25 Ken Raeburn <raeburn@mit.edu> + + * prof_err.et (PROF_BAD_BOOLEAN, PROF_BAD_INTEGER): New error + codes. + +2001-02-02 Tom Yu <tlyu@mit.edu> + + * krb5.conf: Test with trailing whitespace on "default_realm" + line. [pullup from trunk] + + * krb5.conf: Test with a space after ']' and '{' [pullup from trunk] + + * prof_parse.c (parse_std_line): Spaces after '{' or ']' should + not be a fatal error. This is a common lossage in krb5.conf files. + [pullup from trunk] + +2000-10-7 Miro Jurisic <meeroh@mit.edu> + + * et.pbexp: Added the Mach-O initializer function + +2000-10-7 Miro Jurisic <meeroh@mit.edu> + + * et.pbexp: Added the Mac OS X export file (hopefully temporary, + until Apple fixes their tools to use the same format as Mac OS 9) + 1999-10-26 Tom Yu <tlyu@mit.edu> * Makefile.in: Clean up usage of CFLAGS, CPPFLAGS, DEFS, DEFINES, diff --git a/src/util/profile/Makefile.in b/src/util/profile/Makefile.in index 3955ca8..fa3f6db 100644 --- a/src/util/profile/Makefile.in +++ b/src/util/profile/Makefile.in @@ -40,7 +40,7 @@ MLIBS = -lcom_err $(GEN_LIB) LIB=profile LIBMAJOR=1 -LIBMINOR=0 +LIBMINOR=1 SHLIB_EXPDEPS = $(TOPLIBD)/libcom_err$(SHLIBEXT) SHLIB_EXPLIBS = -lcom_err SHLIB_DIRS = -L$(TOPLIBD) diff --git a/src/util/profile/krb5.conf b/src/util/profile/krb5.conf index 01eb66c..19c59c6 100644 --- a/src/util/profile/krb5.conf +++ b/src/util/profile/krb5.conf @@ -1,5 +1,5 @@ [libdefaults] - default_realm = ATHENA.MIT.EDU + default_realm = ATHENA.MIT.EDU default_tgs_enctypes = des-cbc-crc default_tkt_enctypes = des-cbc-crc krb4_config = /etc/athena/krb.conf @@ -8,8 +8,8 @@ kdc_timesync = 1 ccache_type = 4 -[realms] - ATHENA.MIT.EDU = { +[realms] + ATHENA.MIT.EDU = { # kdc = kerberos-2000.mit.edu kdc = kerberos.mit.edu:88 kdc = kerberos-1.mit.edu:88 @@ -17,7 +17,7 @@ kdc = kerberos-3.mit.edu:88 admin_server = kerberos.mit.edu default_domain = mit.edu - } + } MEDIA-LAB.MIT.EDU = { kdc = kerberos.media.mit.edu admin_server = kerberos.media.mit.edu diff --git a/src/util/profile/prof_err.et b/src/util/profile/prof_err.et index e6e35db..dc248f4 100644 --- a/src/util/profile/prof_err.et +++ b/src/util/profile/prof_err.et @@ -54,4 +54,10 @@ error_code PROF_FAIL_OPEN, "Couldn't open profile file" # error_code PROF_EXISTS, "Section already exists" +# +# generated by prof_get.c +# +error_code PROF_BAD_BOOLEAN, "Invalid boolean value" +error_code PROF_BAD_INTEGER, "Invalid integer value" + end diff --git a/src/util/profile/prof_parse.c b/src/util/profile/prof_parse.c index 0e3cffe..7e8bcb8 100644 --- a/src/util/profile/prof_parse.c +++ b/src/util/profile/prof_parse.c @@ -130,6 +130,10 @@ static errcode_t parse_std_line(line, state) profile_make_node_final(state->current_section); cp++; } + /* + * A space after ']' should not be fatal + */ + cp = skip_over_blanks(cp); if (*cp) return PROF_SECTION_SYNTAX; return 0; @@ -169,7 +173,7 @@ static errcode_t parse_std_line(line, state) } else if (value[0] == 0) { do_subsection++; state->state = STATE_GET_OBRACE; - } else if (value[0] == '{' && value[1] == 0) + } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0) do_subsection++; else { cp = value + strlen(value) - 1; diff --git a/src/util/profile/profile.pbexp b/src/util/profile/profile.pbexp new file mode 100644 index 0000000..10e9ba4 --- /dev/null +++ b/src/util/profile/profile.pbexp @@ -0,0 +1,32 @@ +# +# _profile library Macintosh export file +# +# $Header$ + +___InitializeProfileLib + +_profile_init +_profile_init_path +_profile_flush +_profile_abandon +_profile_release +_profile_get_values +_profile_free_list +_profile_get_string +_profile_get_integer +_profile_get_relation_names +_profile_get_subsection_names +_profile_iterator_create +_profile_iterator_free +_profile_iterator +_profile_release_string +_profile_update_relation +_profile_clear_relation +_profile_rename_section +_profile_add_relation + +### Temporary -- DO NOT USE + +_profile_ser_internalize +_profile_ser_externalize +_profile_ser_size diff --git a/src/util/pty/ChangeLog b/src/util/pty/ChangeLog index ff815b7..3dac1a3 100644 --- a/src/util/pty/ChangeLog +++ b/src/util/pty/ChangeLog @@ -1,3 +1,169 @@ +2001-11-28 Tom Yu <tlyu@mit.edu> + + * update_utmp.c (PTY_GETUTXENT): Fix typo. Thanks to Shawn + Stepper. [fixes krb5-build/1020] + +2001-11-19 Tom Yu <tlyu@mit.edu> + + * update_utmp.c (pty_update_utmp): Patch from Garry Zacheiss to + kludge around cases where we need to use more than 2 characters of + LINE in order to avoid conflicts in UT_ID. + +2001-11-05 Tom Yu <tlyu@mit.edu> + + * Makefile.in (LIBMINOR): Bump due to changes to internals. + +2001-09-25 Tom Yu <tlyu@mit.edu> + + * pty-int.h: Fix up botched merge: cause prototypes for + ptyint_update_wtmp{,x} to be correct. + +2001-09-07 Tom Yu <tlyu@mit.edu> + + * update_utmp.c (pty_update_utmp): Remember to chop off leading + "/dev/" for the non-sysV case. Handle lseek() returning non-zero + yet non-negative values (it usually does... :-), so that we can + actually write somewhere not at the beginning of the utmp file if + necessary. + + * update_utmp.c (pty_update_utmp): Don't copy host if it's a null + pointer. + + * dump-utmp.c (print_ut): Use size of ut_name field, not ut_user, + which may not exist, for width when printing ut_name field value. + Specify width when printing hostname, it may be unterminated. + (main): Move utp and utxp declarations closer to their usages, and + make both conditionalized so they're not declared if they're not + used. + + * getpty.c: Make pty_getpty() into ptyint_getpty_ext(), which has + an extra argument that determines whether to call grantpt() and + unlockpt() on systems that support it. The new pty_getpty() will + simply call the extended version. This is to support some + wackiness needed by pty_paranoia.c tests. + + * pty-int.h: Add prototype for ptyint_getpty_ext(). + + * pty_paranoia.c: Add rant about ptys and quirks therein. Needs + to be updated somewhat. Add some more paranoia for the case where + we actually succeed in opening the slave of a closed master and + then succeed in opening the same master. This program will get + rewritten at some point to actually see what things result in EOFs + and under what conditions data will actually get passed between + master and slave. + + * pty_paranoia.c: New file; do many paranoid checks about ctty + handling by the pty drivers. + + * Makefile.in: Add rules for pty_paranoia and check-paranoia, + which runs pty_paranoia. + + * configure.in: Define REVOKE_NEEDS_OPEN for Tru64. Add support + for program building and run flags for the sake of pty_paranoia. + + * open_slave.c: Fix somewhat; AIX doesn't like opening the ctty + twice, so only do initial open if we special-case it in + configure.in, e.g. for Tru64. + + * logwtmp.c: Delete code under "#if 0". Fix reversed test for + loggingin. Don't forget to set the ut_tv or ut_time for the + entry. + + * update_utmp.c: Update rant about Tru64; remove fetching of + ut_user from old entry. The existence of the old ut_user in the + logout entry in wtmp was confusing last. + + * cleanup.c: Call update_utmp() with the correct pid to assist in + finding the old utmp entry. + + * open_ctty.c: Reformat somewhat and revise comment. + + * open_slave.c: Rework significantly. Primarily, keep a fd open + to the slave if we need to reopen the slave device following + vhangup() or revoke(), to accommodate various OS quirks. + + * update_utmp.c: Revise history section somewhat to document more + HP-UX brokenness. Search via ut_pid before searching via + ut_line. Copy stuff around because entuxent() will clobber some + things. + + * void_assoc.c: Revise comment and reformat somewhat. + + * open_slave.c (pty_open_slave): If revoke() present on system but + VHANG_FIRST is not defined, declare local variable. + + * dump-utmp.c: Fix some off-by-one errors. Handle cases where we + have utmpname() but not utmpname(). + + * pty-int.h: Fix typo; VHANG_first -> VHANG_FIRST. + + * open_slave.c (pty_open_slave): Add workaround for Tru64 v5.0, + since its revoke() will fail if the slave isn't open already. + + * cleanup.c (pty_cleanup): Delcare local variable only if + VHANG_LAST defined. + + * logwtmp.c (pty_logwtmp): Only declare local variables if + logwtmp() not available on system. + + * update_utmp.c (pty_update_utmp): Fix typo (OWRONLY -> + O_WRONLY). + + * update_wtmp.c (ptyint_update_wtmpx): Add missing semi-colon in + code path if PTY_UTMP_E_EXIT and PTY_UTMPX_E_EXIT exist. + + * configure.in: Fix some quoting of shell variables when passing + to "test". Reorder some logic in consistency checks to validate + cache variables against "yes" to account for possible empty or + nonexistent values. + + * pty-int.h: Fix conditional prototype of update_wtmp(). + + * update_wtmp.c: Fix conditional compilation of update_wtmp() to + cover the case where we have setutxent() but don't have updwtmpx() + and WTMPX_FILE, as is the case on some Linux installations. + + * configure.in(K5_CHECK_UT_MEMBER): Fix typo in previous; make + sure to include the correct header when checking structure + members. + + * configure.in: Many changes to support the rewriting of the utmp + pieces of libpty. Do a large amount of checking for consistency + of various utmp and utmpx APIs as currently understood. See rant + in update_utmp.c. + + * dump-utmp.c: Rewrite; now has capability to use utmp{,x}name() + to extract entries from utmp and utmpx files. Adjusts field + widths when printing as appropriate. + + * libpty.h: Update call signature for update_utmp() and logwtmp(); + make prototypes unconditional. + + * logwtmp.c: Rewrite. Use pututline() or pututxline() API + whenever possible. + + * pty-int.h: Update call signatures for update_wtmp{,x}(); make + prototypes unconditional. + + * sane_hostname.c: Use the autoconf-correct macro names. + + * update_utmp.c: Rewrite. Basically, use functions from the + pututline() or pututxline() API whenever possible, to avoid + lossage. Inserted large rant about the conjectured history of BSD + utmp, sysV utmp, and utmpx, as well as documentation about some + known quirks. + + * update_wtmp.c: Rewrite. Add new function ptyint_logwtmpx() that + takes a utmpx rather than a utmp, so it can fail to lose data + converting to and from utmp. + + [many pullups from trunk] + +2000-03-24 Ken Raeburn <raeburn@mit.edu> + + * configure.in: Check for alpha*-dec-osf* instead of + alpha-dec-osf*. + 1999-10-26 Tom Yu <tlyu@mit.edu> * Makefile.in: Clean up usage of CFLAGS, CPPFLAGS, DEFS, DEFINES, diff --git a/src/util/pty/Makefile.in b/src/util/pty/Makefile.in index 83d61dc..135fc7b 100644 --- a/src/util/pty/Makefile.in +++ b/src/util/pty/Makefile.in @@ -6,9 +6,13 @@ RELDIR=../util/pty SED = sed +KRB5_RUN_ENV= @KRB5_RUN_ENV@ +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) + LIB=pty LIBMAJOR=1 -LIBMINOR=1 +LIBMINOR=2 STLIBOBJS= cleanup.o getpty.o init_slave.o open_ctty.o open_slave.o \ update_utmp.o update_wtmp.o vhangup.o void_assoc.o pty_err.o \ @@ -49,6 +53,12 @@ dump-utmp: dump-utmp.o $(CC) $(LDFLAGS) -o dump-utmp dump-utmp.o dump-utmp.o: dump-utmp.c +pty_paranoia: pty_paranoia.o $(COM_ERR_DEPLIB) $(PTY_DEPLIB) + $(CC_LINK) -o pty_paranoia pty_paranoia.o $(PTY_LIB) $(COM_ERR_LIB) $(LIBS) + +check-paranoia: pty_paranoia + $(KRB5_RUN_ENV) ./pty_paranoia + install-unix:: install-libs clean-unix:: diff --git a/src/util/pty/cleanup.c b/src/util/pty/cleanup.c index cf0b451..87a77c1 100644 --- a/src/util/pty/cleanup.c +++ b/src/util/pty/cleanup.c @@ -32,10 +32,12 @@ long pty_cleanup (slave, pid, update_utmp) int pid; /* May be zero for unknown.*/ int update_utmp; { +#ifdef VHANG_LAST int retval, fd; +#endif if (update_utmp) - pty_update_utmp(PTY_DEAD_PROCESS,0, "", slave, (char *)0, PTY_UTMP_USERNAME_VALID); + pty_update_utmp(PTY_DEAD_PROCESS, pid, "", slave, (char *)0, PTY_UTMP_USERNAME_VALID); (void)chmod(slave, 0666); (void)chown(slave, 0, 0); @@ -79,7 +81,7 @@ long pty_cleanup (slave, pid, update_utmp) return errno; case 0: ptyint_void_association(); - if ( retval = ( pty_open_ctty( slave, &fd ))) + if ((retval = pty_open_ctty(slave, &fd))) exit(retval); ptyint_vhangup(); exit(0); diff --git a/src/util/pty/configure.in b/src/util/pty/configure.in index 398b182..27fac92 100644 --- a/src/util/pty/configure.in +++ b/src/util/pty/configure.in @@ -30,11 +30,13 @@ ac_cv_func_setsid=no # setsid doesn't do the right thing under Ultrix even thoug # Moreover, strops.h trashes sys/ioctl.h krb5_cv_has_streams=no ;; -alpha-dec-osf*) +alpha*-dec-osf*) AC_CHECK_LIB(security,main, AC_DEFINE(HAVE_SETLUID) LOGINLIBS="$LOGINLIBS -lsecurity" ) + AC_MSG_RESULT(will open ctty prior to revoke due to OSF/1 lossage) + AC_DEFINE(REVOKE_NEEDS_OPEN) ;; *-*-solaris*) AC_DEFINE(PUSH_PTEM) @@ -48,23 +50,144 @@ esac dnl AC_SUBST(LOGINLIBS) dnl -AC_CHECK_LIB(util,openpty, AC_DEFINE(HAVE_OPENPTY) LIBS="$LIBS -lutil") +AC_CHECK_LIB(util,openpty, [AC_DEFINE(HAVE_OPENPTY) LIBS="$LIBS -lutil"]) AC_TYPE_MODE_T AC_CHECK_TYPE(time_t, long) -AC_FUNC_CHECK(strsave,AC_DEFINE(HAS_STRSAVE)) -AC_HAVE_FUNCS(getutent setreuid gettosbyname setsid ttyname line_push ptsname grantpt openpty logwtmp getutmpx) -AC_CHECK_HEADERS(unistd.h stdlib.h string.h utmpx.h utmp.h sys/filio.h sys/sockio.h sys/label.h sys/tty.h ttyent.h lastlog.h sys/select.h sys/ptyvar.h) +AC_CHECK_FUNC(strsave,[AC_DEFINE(HAS_STRSAVE)]) +AC_CHECK_FUNCS(setreuid gettosbyname setsid ttyname line_push ptsname grantpt openpty) +AC_CHECK_HEADERS(unistd.h stdlib.h string.h pty.h sys/filio.h sys/sockio.h sys/label.h sys/tty.h ttyent.h lastlog.h sys/select.h sys/ptyvar.h) AC_CHECK_HEADERS(sys/wait.h) -AC_CHECK_FUNCS(waitpid updwtmpx) +AC_CHECK_FUNCS(waitpid) DECLARE_SYS_ERRLIST KRB5_SIGTYPE CHECK_SIGNALS CHECK_SETJMP CHECK_DIRENT -AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK(cfsetispeed,AC_DEFINE(POSIX_TERMIOS))) -CHECK_UTMP -dnl -dnl +AC_CHECK_HEADER(termios.h,AC_CHECK_FUNC(cfsetispeed,AC_DEFINE(POSIX_TERMIOS))) + +###################################################################### +# +# utmp related hair here. There's lots of it. +# + +AC_CHECK_HEADERS(utmp.h utmpx.h) +AC_CHECK_FUNCS(setutent setutxent updwtmp updwtmpx logwtmp getutmp getutmpx) +AC_CHECK_FUNCS(utmpname utmpxname) + +AC_DEFUN(K5_CHECK_UT_MEMBER, +[AC_MSG_CHECKING([for $2 in struct $1]) +AC_CACHE_VAL([krb5_cv_struct_$1_$2], +[AC_TRY_COMPILE([#include <sys/types.h> +#include <$1.h>], [struct $1 u; u.$2;], +eval "krb5_cv_struct_$1_$2=yes", eval "krb5_cv_struct_$1_$2=no")]) +if eval "test \"`echo '$krb5_cv_struct_'$1'_'$2`\" = yes"; then + AC_MSG_RESULT(yes) + krb5_tr_ut=HAVE_STRUCT_`echo $1'_'$2 | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + AC_DEFINE_UNQUOTED($krb5_tr_ut) +else + AC_MSG_RESULT(no) +fi]) + +if test "$ac_cv_header_utmp_h" = yes; then + AC_MSG_RESULT(checking struct utmp members) + for krb5_mem in ut_host ut_syslen ut_addr ut_id ut_pid ut_type ut_exit; do + K5_CHECK_UT_MEMBER(utmp, $krb5_mem) + done +fi + +if test "$ac_cv_header_utmpx_h" = yes; then + AC_MSG_RESULT(checking struct utmpx members) + for krb5_mem in ut_host ut_syslen ut_addr ut_id ut_pid ut_type ut_exit; do + K5_CHECK_UT_MEMBER(utmpx, $krb5_mem) + done +fi + +AC_DEFUN(K5_CHECK_UT_EXIT_MEMBER, +[AC_MSG_CHECKING([for ut_exit.$2 in struct $1]) +AC_CACHE_VAL([krb5_cv_struct_$1_ut_exit_$2], +[AC_TRY_COMPILE([#include <sys/types.h> +#include <$1.h>], [struct $1 u; u.ut_exit.$2;], +eval "krb5_cv_struct_$1_ut_exit_$2=yes", +eval "krb5_cv_struct_$1_ut_exit_$2=no")]) +if eval "test \"`echo '$krb5_cv_struct_'$1'_ut_exit_'$2`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$3], , :, [$3]) +else + AC_MSG_RESULT(no) + ifelse([$4], , :, [$4]) +fi]) + +if test "$krb5_cv_struct_utmp_ut_exit" = yes; then + AC_MSG_RESULT(checking for working ut_exit.e_exit in struct utmp) + for krb5_mem in __e_exit ut_e_exit ut_exit e_exit; do + K5_CHECK_UT_EXIT_MEMBER(utmp, $krb5_mem, +[krb5_utmp_e_exit=$krb5_mem +krb5_utmp_e_termination=`echo $krb5_mem|sed -e 's%_exit$%_termination%'`], ) + done + if test "${krb5_utmp_e_exit+set}" = set; then + AC_MSG_RESULT([working ut_exit.e_exit in utmp is $krb5_utmp_e_exit]) + AC_DEFINE_UNQUOTED(PTY_UTMP_E_EXIT, $krb5_utmp_e_exit) + AC_DEFINE_UNQUOTED(PTY_UTMP_E_TERMINATION, $krb5_utmp_e_termination) + else + AC_MSG_RESULT([cannot find working ut_exit.e_exit in utmp]) + fi +fi + +if test "$krb5_cv_struct_utmpx_ut_exit" = yes; then + AC_MSG_RESULT(checking for working ut_exit.e_exit in struct utmpx) + for krb5_mem in __e_exit ut_e_exit ut_exit e_exit; do + K5_CHECK_UT_EXIT_MEMBER(utmpx, $krb5_mem, +[krb5_utmpx_e_exit=$krb5_mem +krb5_utmpx_e_termination=`echo $krb5_mem|sed -e 's%_exit$%_termination%'`], ) + done + if test "${krb5_utmpx_e_exit+set}" = set; then + AC_MSG_RESULT([working ut_exit.e_exit in utmpx is $krb5_utmpx_e_exit]) + AC_DEFINE_UNQUOTED(PTY_UTMPX_E_EXIT, $krb5_utmpx_e_exit) + AC_DEFINE_UNQUOTED(PTY_UTMPX_E_TERMINATION, $krb5_utmpx_e_termination) + else + AC_MSG_RESULT([cannot find working ut_exit.e_exit in utmpx]) + fi +fi + +if test "$ac_cv_func_setutent" = yes; then + AC_MSG_CHECKING(consistency of sysV-ish utmp API) + if test "$ac_cv_header_utmp_h" = yes; then + if test "$krb5_cv_struct_utmp_ut_id" = yes \ + && test "$krb5_cv_struct_utmp_ut_type" = yes \ + && test "$krb5_cv_struct_utmp_ut_pid" = yes; then + AC_MSG_RESULT(ok) + else + AC_MSG_RESULT(not ok) + AC_MSG_ERROR([have setutent but no ut_id, ut_type, or ut_pid in utmp]) + fi + else + AC_MSG_RESULT(not ok) + AC_MSG_ERROR([have setutent but no utmp.h]) + fi +fi + +if test "$ac_cv_header_utmpx_h" = yes; then + AC_MSG_CHECKING(consistency of utmpx API) + if test "$ac_cv_func_setutxent" = yes; then + if test "$krb5_cv_struct_utmpx_ut_id" = yes \ + && test "$krb5_cv_struct_utmpx_ut_type" = yes \ + && test "$krb5_cv_struct_utmpx_ut_pid" = yes; then + AC_MSG_RESULT(ok) + else + AC_MSG_RESULT(not ok) + AC_MSG_ERROR([have setutxent but no ut_id, ut_type, or ut_pid in utmpx]) + fi + else + AC_MSG_RESULT(not ok) + AC_MSG_ERROR([have utmpx.h but no setutxent]) + fi +fi + +# +# end of utmp-related hair +# +###################################################################### + AC_MSG_CHECKING([streams interface]) AC_CACHE_VAL(krb5_cv_has_streams, [AC_TRY_COMPILE( @@ -131,24 +254,10 @@ if test $krb5_cv_setpgrp_args = two; then AC_DEFINE(SETPGRP_TWOARG) fi dnl -dnl -if test $ac_cv_header_utmpx_h = yes -a $ac_cv_func_getutmpx = no; then -AC_MSG_CHECKING([if utmpx and utmp ut_exit structures differ]) -AC_CACHE_VAL(krb5_cv_utmp_utmpx_diff_exit_struct, -[AC_TRY_COMPILE( -[#include <sys/types.h> -#include <utmp.h> -#include <utmpx.h>],[struct utmpx utx; struct utmp ut; -utx.ut_exit.ut_exit = ut.ut_exit.e_exit], -krb5_cv_utmp_utmpx_diff_exit_struct=yes, krb5_cv_utmp_utmpx_diff_exit_struct=no)]) -AC_MSG_RESULT($krb5_cv_utmp_utmpx_diff_exit_struct) -if test $krb5_cv_utmp_utmpx_diff_exit_struct = yes; then -AC_DEFINE(UT_EXIT_STRUCTURE_DIFFER) -fi -fi -dnl ADD_DEF(-DKERBEROS) -AC_CONST +AC_C_CONST KRB5_BUILD_LIBRARY_WITH_DEPS KRB5_BUILD_LIBOBJS +KRB5_BUILD_PROGRAM +KRB5_RUN_FLAGS V5_AC_OUTPUT_MAKEFILE diff --git a/src/util/pty/dump-utmp.c b/src/util/pty/dump-utmp.c index 7cc8469..d4c303f 100644 --- a/src/util/pty/dump-utmp.c +++ b/src/util/pty/dump-utmp.c @@ -1,6 +1,29 @@ -#include <stdio.h> +/* + * Copyright 2001 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * M.I.T. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability + * of this software for any purpose. It is provided "as is" without + * express or implied warranty. + * + * dump-utmp.c: dump utmp and utmpx format files for debugging purposes. + */ + +#include <sys/types.h> #include <sys/file.h> #include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> #ifndef UTMPX #ifdef HAVE_UTMPX_H @@ -8,138 +31,251 @@ #endif #endif +#if defined(HAVE_UTMPNAME) || defined(HAVE_UTMPXNAME) +#define UTN /* we can set utmp or utmpx for getut*() */ +#endif + #ifdef UTMPX #include <utmpx.h> +void print_utx(int, const struct utmpx *); #endif #include <utmp.h> -extern char *ctime (); +void print_ut(int, const struct utmp *); + +void usage(const char *); #if defined (HAVE_STRUCT_UTMP_UT_TYPE) || defined (UTMPX) -char *ut_typename (t) { - switch (t) { +char *ut_typename(int); + +char * +ut_typename(int t) { + switch (t) { #define S(N) case N : return #N #define S2(N,N2) case N : return #N2 - S(EMPTY); - S(RUN_LVL); - S(BOOT_TIME); - S(OLD_TIME); - S(NEW_TIME); - S2(INIT_PROCESS,INIT); - S2(LOGIN_PROCESS,LOGIN); - S2(USER_PROCESS,USER); - S2(DEAD_PROCESS,DEAD); - S(ACCOUNTING); - default: return "??"; - } + S(EMPTY); + S(RUN_LVL); + S(BOOT_TIME); + S(OLD_TIME); + S(NEW_TIME); + S2(INIT_PROCESS,INIT); + S2(LOGIN_PROCESS,LOGIN); + S2(USER_PROCESS,USER); + S2(DEAD_PROCESS,DEAD); + S(ACCOUNTING); + default: return "??"; + } } #endif -int main (argc, argv) int argc; char *argv[]; { - int f; - char id[5], user[50], host[100]; - char *file = 0; - int all = 0; - int is_utmpx = 0; - - while (*++argv) - { - char *arg = *argv; - if (!arg) - break; - if (!strcmp ("-a", arg)) - all = 1; - else if (!strcmp ("-x", arg)) - is_utmpx = 1; - else if (arg[0] == '-') - { - fprintf (stderr, "unknown arg `%s'\n", arg); - return 1; - } - else if (file) - { - fprintf (stderr, "already got a file\n"); - return 1; +#define S2D(x) (sizeof(x) * 2.4 + 1.5) + +void +print_ut(int all, const struct utmp *u) +{ + int lu, ll; +#ifdef HAVE_STRUCT_UTMP_UT_ID + int lid; +#endif +#ifdef HAVE_STRUCT_UTMP_UT_PID + int lpid; +#endif +#ifdef PTY_UTMP_E_EXIT + int let, lee; +#endif + +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + if (!all && ((u->ut_type == EMPTY) || (u->ut_type == DEAD_PROCESS))) + return; +#endif + + lu = sizeof(u->ut_name); + ll = sizeof(u->ut_line); + printf("%-*.*s:", lu, lu, u->ut_name); + printf("%-*.*s:", ll, ll, u->ut_line); +#ifdef HAVE_STRUCT_UTMP_UT_ID + lid = sizeof(u->ut_id); + printf("%-*.*s:", lid, lid, u->ut_id); +#endif +#ifdef HAVE_STRUCT_UTMP_UT_PID + lpid = S2D(u->ut_pid); + printf("%*ld", lpid, (long)u->ut_pid); +#endif +#ifdef PTY_UTMP_E_EXIT + let = S2D(u->ut_exit.PTY_UTMP_E_TERMINATION); + lee = S2D(u->ut_exit.PTY_UTMP_E_EXIT); + printf("(%*ld,", let, (long)u->ut_exit.PTY_UTMP_E_TERMINATION); + printf("%*ld)", lee, (long)u->ut_exit.PTY_UTMP_E_EXIT); +#endif +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + printf(" %-9s", ut_typename(u->ut_type)); +#endif + printf(" %s", ctime(&u->ut_time) + 4); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + if (u->ut_host[0]) + printf(" %.*s\n", (int) sizeof(u->ut_host), u->ut_host); +#endif + + return; +} + +#ifdef UTMPX +void +print_utx(int all, const struct utmpx *u) +{ + int lu, ll, lid, lpid; +#ifdef PTY_UTMPX_E_EXIT + int let, lee; +#endif + + if (!all && ((u->ut_type == EMPTY) || (u->ut_type == DEAD_PROCESS))) + return; + + lu = sizeof(u->ut_user); + ll = sizeof(u->ut_line); + lid = sizeof(u->ut_id); + printf("%-*.*s:", lu, lu, u->ut_user); + printf("%-*.*s:", ll, ll, u->ut_line); + printf("%-*.*s", lid, lid, u->ut_id); + if (lu + ll + lid >= 60) + printf("\n"); + else + printf(":"); + lpid = S2D(u->ut_pid); + printf("%*ld", lpid, (long)u->ut_pid); +#ifdef PTY_UTMPX_E_EXIT + let = S2D(u->ut_exit.PTY_UTMPX_E_TERMINATION); + lee = S2D(u->ut_exit.PTY_UTMPX_E_EXIT); + printf("(%*ld,", let, (long)u->ut_exit.PTY_UTMPX_E_TERMINATION); + printf("%*ld)", lee, (long)u->ut_exit.PTY_UTMPX_E_EXIT); +#endif + printf(" %-9s", ut_typename(u->ut_type)); + printf(" %s", ctime(&u->ut_tv.tv_sec) + 4); +#ifdef HAVE_STRUCT_UTMPX_UT_HOST + if (u->ut_host[0]) + printf(" %s\n", u->ut_host); +#endif + + return; +} +#endif + +#ifdef UTMPX +#define OPTX "x" +#else +#define OPTX +#endif +#ifdef UTN +#define OPTG "g" +#else +#define OPTG +#endif +#define OPTS "a" OPTX OPTG + +void +usage(const char *prog) +{ + fprintf(stderr, "usage: %s [-" OPTS "] file\n", prog); + exit(1); +} + +int +main(int argc, char **argv) +{ + int c; + int all, is_utmpx, do_getut; + int f; + char *fn; + size_t recsize; + size_t nread; + union { + struct utmp ut; +#ifdef UTMPX + struct utmpx utx; +#endif + } u; + + all = is_utmpx = do_getut = 0; + recsize = sizeof(struct utmp); + + while ((c = getopt(argc, argv, OPTS)) != EOF) { + switch (c) { + case 'a': + all = 1; + break; +#ifdef UTMPX + case 'x': + is_utmpx = 1; + recsize = sizeof(struct utmpx); + break; +#endif +#ifdef UTN + case 'g': + do_getut = 1; + break; +#endif + default: + usage(argv[0]); } - else - file = arg; } - f = open (file, O_RDONLY); - if (f < 0) { - perror (file); - exit (1); - } - id[4] = 0; - if (is_utmpx) { + if (argc <= optind) + usage(argv[0]); + fn = argv[optind]; + if (!do_getut) { + f = open(fn, O_RDONLY); + if (f == -1) { + perror(fn); + exit(1); + } + while ((nread = read(f, &u, recsize)) > 0) { + if (nread < recsize) { + fprintf(stderr, "short read"); + close(f); + exit(1); + } + if (is_utmpx) { #ifdef UTMPX - struct utmpx u; - while (1) { - int nread = read (f, &u, sizeof (u)); - if (nread == 0) { - /* eof */ - return 0; - } else if (nread == -1) { - /* error */ - perror ("read"); - return 1; - } - if ((u.ut_type == DEAD_PROCESS - || u.ut_type == EMPTY) - && !all) - continue; - strncpy (id, u.ut_id, 4); - printf ("%-8s:%-12s:%-4s", u.ut_user, u.ut_line, id); - printf (":%5d", u.ut_pid); - printf ("(%5d,%5d)", u.ut_exit.e_termination, u.ut_exit.e_exit); - printf (" %-9s %s", ut_typename (u.ut_type), ctime (&u.ut_xtime) + 4); - if (u.ut_syslen && u.ut_host[0]) - printf (" %s\n", u.ut_host); - } - abort (); + print_utx(all, &u.utx); #else - fprintf (stderr, "utmpx support not compiled in\n"); - return 1; -#endif - } - /* else */ - { - struct utmp u; - while (read (f, &u, sizeof (u)) == sizeof (u)) { -#ifdef EMPTY - if ((u.ut_type == DEAD_PROCESS - || u.ut_type == EMPTY) - && !all) - continue; + abort(); #endif -#ifdef HAVE_STRUCT_UTMP_UT_PID - strncpy (id, u.ut_id, 4); - strncpy (user, u.ut_user, sizeof (u.ut_user)); - user[sizeof(u.ut_user)] = 0; - printf ("%-8s:%-12s:%-4s", user, u.ut_line, id); - printf (":%5d", u.ut_pid); + } else { + print_ut(all, &u.ut); + } + } + if (nread == -1) { + perror("read"); + exit(1); + } + close(f); + } else { + if (is_utmpx) { +#ifdef UTMPX +#ifdef HAVE_UTMPXNAME + struct utmpx *utxp; + utmpxname(fn); + setutxent(); + while ((utxp = getutxent()) != NULL) + print_utx(all, utxp); #else - strncpy (user, u.ut_name, sizeof (u.ut_name)); - user[sizeof(u.ut_name)] = 0; - printf ("%-8s:%-12s", user, u.ut_line); -#endif -#ifdef HAVE_STRUCT_UTMP_UT_HOST - { - char host[sizeof (u.ut_host) + 1]; - strncpy (host, u.ut_host, sizeof(u.ut_host)); - host[sizeof (u.ut_host)] = 0; - printf (":%-*s", sizeof (u.ut_host), host); - } + fprintf(stderr, "no utmpxname(); can't use getutxent()\n"); + exit(1); #endif -#ifdef HAVE_STRUCT_UTMP_UT_EXIT - printf ("(%5d,%5d)", u.ut_exit.e_termination, u.ut_exit.e_exit); +#else + abort(); #endif -#ifdef HAVE_STRUCT_UTMP_UT_TYPE - printf (" %-9s", ut_typename (u.ut_type)); + } else { +#ifdef HAVE_UTMPNAME + struct utmp *utp; + utmpname(fn); + setutxent(); + while ((utp = getutent()) != NULL) + print_ut(all, utp); +#else + fprintf(stderr, "no utmpname(); can't use getutent()\n"); + exit(1); #endif - /* this ends with a newline */ - printf (" %s", ctime (&u.ut_time) + 4); + } } - } - - return 0; + exit(0); } diff --git a/src/util/pty/getpty.c b/src/util/pty/getpty.c index 3683ba6..0e86514 100644 --- a/src/util/pty/getpty.c +++ b/src/util/pty/getpty.c @@ -24,15 +24,17 @@ #include "libpty.h" #include "pty-int.h" -long pty_getpty (fd, slave, slavelength) - int slavelength; - int *fd; char *slave; +long +ptyint_getpty_ext(int *fd, char *slave, int slavelength, int do_grantpt) { +#if !defined(HAVE__GETPTY) && !defined(HAVE_OPENPTY) char *cp; char *p; int i,ptynum; struct stat stb; char slavebuf[1024]; +#endif + #ifdef HAVE__GETPTY char *slaveret; /*Temporary to hold pointer to slave*/ #endif /*HAVE__GETPTY*/ @@ -76,7 +78,8 @@ long pty_getpty (fd, slave, slavelength) if (*fd >= 0) { #if defined(HAVE_GRANTPT)&&defined(HAVE_STREAMS) - if (grantpt(*fd) || unlockpt(*fd)) return PTY_GETPTY_STREAMS; + if (do_grantpt) + if (grantpt(*fd) || unlockpt(*fd)) return PTY_GETPTY_STREAMS; #endif #ifdef HAVE_PTSNAME @@ -139,3 +142,9 @@ long pty_getpty (fd, slave, slavelength) #endif /*HAVE__GETPTY*/ #endif /* HAVE_OPENPTY */ } + +long +pty_getpty(int *fd, char *slave, int slavelength) +{ + return ptyint_getpty_ext(fd, slave, slavelength, 1); +} diff --git a/src/util/pty/libpty.h b/src/util/pty/libpty.h index 82d2d81..ddd20c1 100644 --- a/src/util/pty/libpty.h +++ b/src/util/pty/libpty.h @@ -30,7 +30,6 @@ /* flags to update_utmp*/ #define PTY_TTYSLOT_USABLE (0x1) #define PTY_UTMP_USERNAME_VALID (0x2) -#ifdef __STDC__ /* use prototypes */ long pty_init(void); long pty_getpty ( int *fd, char *slave, int slavelength); @@ -39,24 +38,17 @@ long pty_open_slave (const char *slave, int *fd); long pty_open_ctty (const char *slave, int *fd); long pty_initialize_slave ( int fd); -long pty_update_utmp (int process_type,int pid, char *user, char *line, char *host, int flags); +long pty_update_utmp(int process_type, int pid, const char *user, + const char *line, const char *host, int flags); -long pty_logwtmp (char *tty, char * user, char *host); +long pty_logwtmp(const char *tty, const char *user, const char *host); long pty_cleanup(char *slave, int pid, int update_utmp); + +#ifndef SOCK_DGRAM +struct sockaddr_in; +#endif + long pty_make_sane_hostname(struct sockaddr_in *, int, int, int, char **); -#else /*__STDC__*/ -long pty_init(); -long pty_getpty(); - -long pty_open_slave(); -long pty_open_ctty(); -long pty_initialize_slave(); - -long pty_update_utmp(); -long pty_logwtmp(); -long pty_cleanup(); -long pty_make_sane_hostname(); -#endif /* __STDC__*/ #define __LIBPTY_H__ #endif diff --git a/src/util/pty/logwtmp.c b/src/util/pty/logwtmp.c index e3611f6..2047278 100644 --- a/src/util/pty/logwtmp.c +++ b/src/util/pty/logwtmp.c @@ -1,8 +1,7 @@ /* * pty_logwtmp: Implement the logwtmp function if not present. * - * Copyright 1995 by the Massachusetts Institute of Technology. - * + * Copyright 1995, 2001 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -24,42 +23,86 @@ #include "libpty.h" #include "pty-int.h" -long pty_logwtmp (tty, user, host ) - char *user, *tty, *host; +#if defined(HAVE_SETUTXENT) || defined(HAVE_SETUTENT) +#ifdef HAVE_SETUTXENT +#define PTY_STRUCT_UTMPX struct utmpx +#else +#define PTY_STRUCT_UTMPX struct utmp +#endif + +long +pty_logwtmp(const char *tty, const char *user, const char *host) { +#ifndef HAVE_LOGWTMP + PTY_STRUCT_UTMPX utx; + int loggingin; + size_t len; + const char *cp; + char utmp_id[5]; +#endif + #ifdef HAVE_LOGWTMP logwtmp(tty,user,host); return 0; #else - struct utmp ut; - char *tmpx; - char utmp_id[5]; - /* Will be empty for logout */ - int loggingin = user[0]; + loggingin = (user[0] != '\0'); + memset(&utx, 0, sizeof(utx)); + strncpy(utx.ut_line, tty, sizeof(utx.ut_line)); + strncpy(utx.ut_user, user, sizeof(utx.ut_user)); +#if (defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMPX_UT_HOST)) \ + || (!defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMP_UT_HOST)) + strncpy(utx.ut_host, host, sizeof(utx.ut_host)); + utx.ut_host[sizeof(utx.ut_host) - 1] = '\0'; +#endif +#ifdef HAVE_SETUTXENT + gettimeofday(&utx.ut_tv, NULL); +#else + (void)time(&utx.ut_time); +#endif + utx.ut_pid = (loggingin ? getpid() : 0); + utx.ut_type = (loggingin ? USER_PROCESS : DEAD_PROCESS); -#ifndef NO_UT_HOST - strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + len = strlen(tty); + if (len >= 2) + cp = tty + len - 2; + else + cp = tty; + sprintf(utmp_id, "kr%s", cp); + strncpy(utx.ut_id, utmp_id, sizeof(utx.ut_id)); + +#ifdef HAVE_SETUTXENT + return ptyint_update_wtmpx(&utx); +#else + return ptyint_update_wtmp(&utx); #endif - strncpy(ut.ut_line, tty, sizeof(ut.ut_line)); - ut.ut_time = time(0); - -#ifndef NO_UT_PID - ut.ut_pid = getpid(); - strncpy(ut.ut_user, user, sizeof(ut.ut_user)); +#endif /* !HAVE_LOGWTMP */ +} - tmpx = tty + strlen(tty) - 2; - sprintf(utmp_id, "kr%s", tmpx); - strncpy(ut.ut_id, utmp_id, sizeof(ut.ut_id)); - ut.ut_pid = (loggingin ? getpid() : 0); - ut.ut_type = (loggingin ? USER_PROCESS : DEAD_PROCESS); +#else /* !(defined(HAVE_SETUTXENT) || defined(HAVE_SETUTENT)) */ + +long +pty_logwtmp(const char *tty, const char *user, const char *host) +{ + struct utmp ut; + +#ifdef HAVE_LOGWTMP + logwtmp(tty,user,host); + return 0; #else - strncpy(ut.ut_name, user, sizeof(ut.ut_name)); + + memset(&ut, 0, sizeof(ut)); +#ifdef HAVE_STRUCT_UTMP_UT_HOST + strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + ut.ut_host[sizeof(ut.ut_host) - 1] = '\0'; #endif + strncpy(ut.ut_line, tty, sizeof(ut.ut_line)); + strncpy(ut.ut_name, user, sizeof(ut.ut_name)); + return ptyint_update_wtmp(&ut); - return ptyint_update_wtmp(&ut, host, user); -#endif /*HAVE_LOGWTMP*/ +#endif /* !HAVE_LOGWTMP */ } +#endif /* !(defined(HAVE_SETUTXENT) || defined(HAVE_SETUTENT)) */ diff --git a/src/util/pty/open_ctty.c b/src/util/pty/open_ctty.c index d02a8c1..5e41d95 100644 --- a/src/util/pty/open_ctty.c +++ b/src/util/pty/open_ctty.c @@ -24,35 +24,37 @@ #include "pty-int.h" /* - * This routine will be called twice. It's not particularly important - * that the setsid() or TIOCSTTY ioctls succeed (they may not the - * second time), but rather that we have a controlling terminal at the - * end. It is assumed that vhangup doesn't exist and confuse the - * process's notion of controlling terminal on any system without - * TIOCNOTTY. That is, either vhangup() leaves the controlling - * terminal in tact, breaks the association completely, or the system - * provides TIOCNOTTY to get things back into a reasonable state. In - * practice, vhangup() either breaks the association completely or - * doesn't effect controlling terminals, so this condition is met. + * This function will be called twice. The first time it will acquire + * a controlling terminal from which to vhangup() or revoke() (see + * comments in open_slave.c); the second time, it will be to open the + * actual slave device for use by the application. We no longer call + * ptyint_void_association(), as that will be called in + * pty_open_slave() to avoid spurious calls to setsid(), etc. + * + * It is assumed that systems where vhangup() exists and does break + * the ctty association will allow the slave to be re-acquired as the + * ctty. Also, if revoke() or vhangup() doesn't break the ctty + * association, we assume that we can successfully reopen the slave. + * + * This function doesn't check whether we actually acquired the ctty; + * we assume that the caller will check that, or that it doesn't + * matter in the particular case. */ long -pty_open_ctty (slave, fd) - const char * slave; - int *fd; +pty_open_ctty(const char *slave, int *fd) { - int retval; -/* First, dissociate from previous terminal */ - if ( (retval = ptyint_void_association()) != 0 ) - return retval; #ifdef ultrix - /* The Ultrix (and other BSD tty drivers) require the process group - * to be zero, in order to acquire the new tty as a controlling tty. */ + /* + * The Ultrix (and other BSD tty drivers) require the process + * group to be zero, in order to acquire the new tty as a + * controlling tty. This may actually belong in + * ptyint_void_association(). + */ (void) setpgrp(0, 0); #endif - *fd = open(slave, O_RDWR); - if (*fd < 0 ) + if (*fd < 0) return PTY_OPEN_SLAVE_OPENFAIL; #ifdef ultrix setpgrp(0, getpid()); diff --git a/src/util/pty/open_slave.c b/src/util/pty/open_slave.c index aea04de..cc52228 100644 --- a/src/util/pty/open_slave.c +++ b/src/util/pty/open_slave.c @@ -1,7 +1,8 @@ /* * pty_open_slave: open slave side of terminal, clearing for use. * - * Copyright 1995, 1996 by the Massachusetts Institute of Technology. + * Copyright 1995, 1996, 2001 by the Massachusetts Institute of + * Technology. * * * Permission to use, copy, modify, and distribute this software and @@ -24,76 +25,77 @@ #include "libpty.h" #include "pty-int.h" - -long pty_open_slave ( slave, fd) - const char *slave; - int *fd; +long +pty_open_slave(const char *slave, int *fd) { - int vfd, testfd; + int tmpfd; long retval; -#ifdef POSIX_SIGNALS - struct sigaction sa; - /* Initialize "sa" structure. */ - (void) sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - -#endif + /* Sanity check. */ + if (slave == NULL || *slave == '\0') + return PTY_OPEN_SLAVE_TOOSHORT; + + /* First, set up a new session and void old associations. */ + ptyint_void_association(); - /* First, chmod and chown the slave*/ /* - * If we have vhangup then we really need pty_open_ctty to make sure - * Our controlling terminal is the pty we're opening. However, if we - * are using revoke or nothing then we just need a file descriiptor - * for the pty. Considering some OSes in this category break on - * the second call to open_ctty (currently OSF but others may), - * we simply use a descriptor if we can. - */ -#ifdef VHANG_FIRST - if (( retval = pty_open_ctty ( slave, &vfd )) != 0 ) - return retval; - if (vfd < 0) + * Make a first attempt at acquiring the ctty under certain + * condisions. This is necessary for several reasons: + * + * Under Irix, if you open a pty slave and then close it, a + * subsequent open of the slave will cause the master to read EOF. + * To prevent this, don't close the first fd until we do the real + * open following vhangup(). + * + * Under Tru64 v5.0, if there isn't a fd open on the slave, + * revoke() fails with ENOTTY, curiously enough. + * + * Anyway, sshd seems to make a practice of doing this. + */ +#if defined(VHANG_FIRST) || defined(REVOKE_NEEDS_OPEN) + retval = pty_open_ctty(slave, fd); + if (retval) + return retval; + if (*fd < 0) return PTY_OPEN_SLAVE_OPENFAIL; - #endif - - if (slave == NULL || *slave == '\0') - return PTY_OPEN_SLAVE_TOOSHORT; - if (chmod(slave, 0)) - return PTY_OPEN_SLAVE_CHMODFAIL; - if ( chown(slave, 0, 0 ) == -1 ) - return PTY_OPEN_SLAVE_CHOWNFAIL; + /* chmod and chown the slave. */ + if (chmod(slave, 0)) + return PTY_OPEN_SLAVE_CHMODFAIL; + if (chown(slave, 0, 0) == -1) + return PTY_OPEN_SLAVE_CHOWNFAIL; -#ifdef VHANG_FIRST - ptyint_vhangup(); - (void) close(vfd); -#endif - - if ( (retval = ptyint_void_association()) != 0) - return retval; - #ifdef HAVE_REVOKE - if (revoke (slave) < 0 ) { + if (revoke(slave) < 0) { return PTY_OPEN_SLAVE_REVOKEFAIL; } -#endif /*HAVE_REVOKE*/ +#else /* !HAVE_REVOKE */ +#ifdef VHANG_FIRST + ptyint_vhangup(); +#endif +#endif /* !HAVE_REVOKE */ -/* Open the pty for real. */ - if (( retval = pty_open_ctty ( slave, fd)) != 0 ) { + /* Open the pty for real. */ + retval = pty_open_ctty(slave, &tmpfd); +#if defined(VHANG_FIRST) || defined(REVOKE_NEEDS_OPEN) + close(*fd); +#endif + if (retval) { + *fd = -1; return PTY_OPEN_SLAVE_OPENFAIL; } - retval = pty_initialize_slave (*fd); - + *fd = tmpfd; + retval = pty_initialize_slave(*fd); if (retval) - return retval; - testfd = open("/dev/tty", O_RDWR|O_NDELAY); - if ( testfd < 0 ) - { + return retval; + /* Make sure it's really our ctty. */ + tmpfd = open("/dev/tty", O_RDWR|O_NDELAY); + if (tmpfd < 0) { close(*fd); *fd = -1; return PTY_OPEN_SLAVE_NOCTTY; - } - close(testfd); + } + close(tmpfd); return 0; } diff --git a/src/util/pty/pty-int.h b/src/util/pty/pty-int.h index 2c47780..0135165 100644 --- a/src/util/pty/pty-int.h +++ b/src/util/pty/pty-int.h @@ -82,8 +82,13 @@ #endif #endif -#if defined(HAVE_VHANGUP) && !defined(OPEN_CTTY_ONLY_ONCE) -#define VHANG_first /* Breaks under Ultrix and others where you cannot get controlling terminal twice.*/ +#if defined(HAVE_VHANGUP) && !defined(OPEN_CTTY_ONLY_ONCE) \ + && !defined(HAVE_REVOKE) +/* + * Breaks under Ultrix and others where you cannot get controlling + * terminal twice. + */ +#define VHANG_FIRST #define VHANG_LAST #endif @@ -91,14 +96,27 @@ #ifdef __STDC__ long ptyint_void_association(void); long ptyint_open_ctty (char *slave, int *fd); -long ptyint_update_wtmp (struct utmp *ut, char *host, char *user); - +long ptyint_getpty_ext(int *, char *, int, int); +#ifdef HAVE_SETUTXENT +long ptyint_update_wtmpx(struct utmpx *utx); +#endif +#if !(defined(WTMPX_FILE) && defined(HAVE_UPDWTMPX)) \ + || !defined(HAVE_SETUXENT) +long ptyint_update_wtmp(struct utmp *ut); +#endif void ptyint_vhangup(void); #else /*__STDC__*/ long ptyint_void_association(); -void ptyint_vhangup(); +long ptyint_getpty_ext(); +#ifdef HAVE_SETUTXENT +long ptyint_update_wtmpx(); +#endif +#if !(defined(WTMPX_FILE) && defined(HAVE_UPDWTMPX)) \ + || !defined(HAVE_SETUXENT) long ptyint_update_wtmp(); +#endif +void ptyint_vhangup(); #endif /* __STDC__*/ #define __PTY_INT_H__ diff --git a/src/util/pty/pty_paranoia.c b/src/util/pty/pty_paranoia.c new file mode 100644 index 0000000..7311e08 --- /dev/null +++ b/src/util/pty/pty_paranoia.c @@ -0,0 +1,650 @@ +/* + * Copyright 2001 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * M.I.T. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability + * of this software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +/* + * A rant on the nature of pseudo-terminals: + * ----------------------------------------- + * + * Controlling terminals and job control: + * + * First, some explanation of job control and controlling terminals is + * necessary for background. This discussion applies to hardwired + * terminals as well as ptys. On most modern systems, all processes + * belong to a process group. A process whose process group id (pgid) + * is the sames as its pid is the process group leader of its process + * group. Process groups belong to sessions. On a modern system, a + * process that is not currently a process group leader may create a + * new session by calling setsid(), which makes it a session leader as + * well as a process group leader, and also removes any existing + * controlling terminal (ctty) association. Only a session leader may + * acquire a ctty. It's not clear how systems that don't have + * setsid() handle ctty acquisition, though probably any process group + * leader that doesn't have a ctty may acquire one that way. + * + * A terminal that is a ctty has an associated foreground process + * group, which is a member of the terminal's associated session. + * This process group gets read/write access to the terminal and will + * receive terminal-generated signals (e.g. SIGINT, SIGTSTP). Process + * groups belonging to the session but not in the foreground may get + * signals that suspend them if they try to read/write from the ctty, + * depending on various terminal settings. + * + * On many systems, the controlling process (the session leader + * associated with a ctty) exiting will cause the session to lose its + * ctty, even though some processes may continue to have open file + * descriptors on the former ctty. It is possible for a process to + * have no file descriptors open on its controlling tty, but to + * reacquire such by opening /dev/tty, as long as its session still + * has a ctty. + * + * On ptys in general: + * + * Ptys have a slave side and a master side. The slave side looks + * like a hardwired serial line to the application that opens it; + * usually, telnetd or rlogind, etc. opens the slave and hands it to + * the login program as stdin/stdout/stderr. The master side usually + * gets the actual network traffic written to/from it. Roughly, the + * master and slave are two ends of a bidirectional pair of FIFOs, + * though this can get complicated by other things. + * + * The master side of a pty is theoretically a single-open device. + * This MUST be true on systems that have BSD-style ptys, since there + * is usually no way to allocate an unused pty except by attempting to + * open all the master pty nodes in the system. + * + * Often, but not always, the last close of a slave device will cause + * the master to get an EOF. Closing the master device will sometimes + * cause the foreground process group of the slave to get a SIGHUP, + * but that may depend on terminal settings. + * + * BSD ptys: + * + * On a BSD-derived system, the master nodes are named like + * /dev/ptyp0, and the slave nodes are named like /dev/ttyp0. The + * last two characters are the variable ones, and a shell-glob type + * pattern for a slave device is usually of the form + * /dev/tty[p-z][0-9a-f], though variants are known to exist. + * + * System V cloning ptys: + * + * There is a cloning master device (usually /dev/ptmx, but the name + * can vary) that gets opened. Each open of the cloning master + * results in an open file descriptor of a unique master device. The + * application calls ptsname() to find the pathname to the slave node. + * + * In theory, the slave side of the pty is locked out until the + * process opening the master calls grantpt() to adjust permissions + * and unlockpt() to unlock the slave. It turns out that Unix98 + * doesn't require that the slave actually get locked out, or that + * unlockpt() actually do anything on such systems. At least AIX + * allows the slave to be opened prior to calling unlockpt(), but most + * other SysV-ish systems seem to actually lock out the slave. + * + * Pty security: + * + * It's not guaranteed on a BSD-ish system that a slave can't be + * opened when the master isn't open. It's even possible to acquire + * the slave as a ctty (!) if the open is done as non-blocking. It's + * possible to open the master corresponding to an open slave, which + * creates some security issues: once this master is open, data + * written to the slave will actually pass to the master. + * + * On a SysV-ish system, the close of the master will invalidate any + * open file descriptors on the slave. + * + * In general, there are two functions that can be used to "clean" a + * pty slave, revoke() and vhangup(). revoke() will invalidate all + * file descriptors open on a particular pathname (often this only + * works on terminal devices), usually by invalidating the underlying + * vnode. vhangup() will send a SIGHUP to the foreground process + * group of the control terminal. On many systems, it also has + * revoke() semantics. + * + * If a process acquires a controlling terminal in order to perform a + * vhangup(), the reopen of the controlling terminal after the + * vhangup() call should be done prior to the close of the file + * descriptor used to initially acquire the controlling terminal, + * since that will likely prevent the process on the master side from + * reading a spurious EOF due to all file descriptors to the slave + * being closed. + * + * Known quirks of various OSes: + * + * AIX 4.3.3: + * + * If the environment variable XPG_SUS_ENV is not equal to "ON", then + * it's possible to open the slave prior to calling unlockpt(). + */ + +/* + * NOTE: this program will get reworked at some point to actually test + * passing of data between master and slave, and to do general cleanup. + * + * This is rather complex, so it bears some explanation. + * + * There are multiple child processes and a parent process. These + * communicate via pipes (which we assume here to be unidirectional). + * The pipes are: + * + * pp1 - parent -> any children + * + * p1p - any children -> parent + * + * p21 - only child2 -> child1 + * + * A parent process will acquire a pty master and slave via + * pty_getpty(). It will then fork a process, child1. It then does a + * waitpid() for child1, and then writes to child2 via syncpipe pp1. + * It then reads from child3 via syncpipe p1p, then closes the + * master. It writes to child3 via syncpipe pp1 to indicate that it + * has closed the master. It then reads from child3 via syncpipe p1p + * and exits with a value appropriate to what it read from child3. + * + * child1 will acquire the slave as its ctty and fork child2; child1 + * will exit once it reads from the syncpipe p21 from child2. + * + * child2 will set a signal handler for SIGHUP and then write to + * child1 via syncpipe p21 to indicate that child2 has set up the + * handler. It will then read from the syncpipe pp1 from the parent + * to confirm that the parent has seen child1 exit, and then checks to + * see if it still has a ctty. Under Unix98, and likely earlier + * System V derivatives, the exiting of the session leader associated + * with a ctty (in this case, child1) will cause the entire session to + * lose its ctty. + * + * child2 will then check to see if it can reopen the slave, and + * whether it has a ctty after reopening it. This should fail on most + * systems. + * + * child2 will then fork child3 and immediately exit. + * + * child3 will write to the syncpipe p1p and read from the syncpipe + * pp1. It will then check if it has a ctty and then attempt to + * reopen the slave. This should fail. It will then write to the + * parent via syncpipe p1p and exit. + * + * If this doesn't fail, child3 will attempt to write to the open + * slave fd. This should fail unless a prior call to revoke(), + * etc. failed due to lack of permissions, e.g. NetBSD when running as + * non-root. + */ + +#include <com_err.h> +#include "libpty.h" +#include "pty-int.h" +#include <sys/wait.h> +#include <stdlib.h> + +char *prog; +int masterfd, slavefd; +char slave[64], slave2[64]; +pid_t pid1, pid2, pid3; +int status1, status2; +int pp1[2], p1p[2], p21[2]; + +void handler(int); +void rdsync(int, int *, const char *); +void wrsync(int, int, const char *); +void testctty(const char *); +void testex(int, const char *); +void testwr(int, const char *); +void child1(void); +void child2(void); +void child3(void); + +void +handler(int sig) +{ + printf("pid %ld got signal %d\n", (long)getpid(), sig); + fflush(stdout); + return; +} + +void +rdsync(int fd, int *status, const char *caller) +{ + int n; + char c; + +#if 0 + printf("rdsync: %s: starting\n", caller); + fflush(stdout); +#endif + while ((n = read(fd, &c, 1)) < 0) { + if (errno != EINTR) { + fprintf(stderr, "rdsync: %s", caller); + perror(""); + exit(1); + } else { + printf("rdsync: %s: got EINTR; looping\n", caller); + fflush(stdout); + } + } + if (!n) { + fprintf(stderr, "rdsync: %s: unexpected EOF\n", caller); + exit(1); + } + printf("rdsync: %s: got sync byte\n", caller); + fflush(stdout); + if (status != NULL) + *status = c; +} + +void +wrsync(int fd, int status, const char *caller) +{ + int n; + char c; + + c = status; + while ((n = write(fd, &c, 1)) < 0) { + if (errno != EINTR) { + fprintf(stderr, "wrsync: %s", caller); + perror(""); + exit(1); + } else { + printf("wrsync: %s: got EINTR; looping\n", caller); + fflush(stdout); + } + } +#if 0 + printf("wrsync: %s: sent sync byte\n", caller); +#endif + fflush(stdout); +} + +void +testctty(const char *caller) +{ + int fd; + + fd = open("/dev/tty", O_RDWR|O_NONBLOCK); + if (fd < 0) { + printf("%s: no ctty\n", caller); + } else { + printf("%s: have ctty\n", caller); + } +} + +void +testex(int fd, const char *caller) +{ + fd_set rfds, xfds; + struct timeval timeout; + int n; + char c; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&rfds); + FD_ZERO(&xfds); + FD_SET(fd, &rfds); + FD_SET(fd, &xfds); + + n = select(fd + 1, &rfds, NULL, &xfds, &timeout); + if (n < 0) { + fprintf(stderr, "testex: %s: ", caller); + perror("select"); + } + if (n) { + if (FD_ISSET(fd, &rfds) || FD_ISSET(fd, &xfds)) { + n = read(fd, &c, 1); + if (!n) { + printf("testex: %s: got EOF\n", caller); + fflush(stdout); + return; + } else if (n == -1) { + printf("testex: %s: got errno=%ld (%s)\n", + caller, (long)errno, strerror(errno)); + } else { + printf("testex: %s: read 1 byte!?\n", caller); + } + } + } else { + printf("testex: %s: no exceptions or readable fds\n", caller); + } +} + +void +testwr(int fd, const char *caller) +{ + fd_set wfds; + struct timeval timeout; + int n; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + + n = select(fd + 1, NULL, &wfds, NULL, &timeout); + if (n < 0) { + fprintf(stderr, "testwr: %s: ", caller); + perror("select"); + } + if (n) { + if (FD_ISSET(fd, &wfds)) { + printf("testwr: %s: is writable\n", caller); + fflush(stdout); + } + } +} + + +void +child3(void) +{ + int n; + + ptyint_void_association(); + slavefd = open(slave, O_RDWR|O_NONBLOCK); + if (slavefd < 0) { + wrsync(p1p[1], 1, "[02] child3->parent"); + printf("child3: failed reopen of slave\n"); + fflush(stdout); + exit(1); + } +#ifdef TIOCSCTTY + ioctl(slavefd, TIOCSCTTY, 0); +#endif + + printf("child3: reopened slave\n"); + testctty("child3: after reopen of slave"); + testwr(slavefd, "child3: after reopen of slave"); + testex(slavefd, "child3: after reopen of slave"); + close(slavefd); + testctty("child3: after close of slave"); + + /* + * Sync for parent to close master. + */ + wrsync(p1p[1], 0, "[02] child3->parent"); + rdsync(pp1[0], NULL, "[03] parent->child3"); + + testctty("child3: after close of master"); + printf("child3: attempting reopen of slave\n"); + fflush(stdout); + slavefd = open(slave, O_RDWR|O_NONBLOCK); + if (slavefd < 0) { + printf("child3: failed reopen of slave after master close: " + "errno=%ld (%s)\n", (long)errno, strerror(errno)); + wrsync(p1p[1], 0, "[04] child3->parent"); + fflush(stdout); + exit(0); + } + if (fcntl(slavefd, F_SETFL, 0) == -1) { + perror("child3: fcntl"); + wrsync(p1p[1], 2, "[04] child3->parent"); + exit(1); + } +#ifdef TIOCSCTTY + ioctl(slavefd, TIOCSCTTY, 0); +#endif + printf("child3: reopened slave after master close\n"); + testctty("child3: after reopen of slave after master close"); + testwr(slavefd, "child3: after reopen of slave after master close"); + testex(slavefd, "child3: after reopen of slave after master close"); + n = write(slavefd, "foo", 4); + if (n < 0) { + printf("child3: writing to slave of closed master: errno=%ld (%s)\n", + (long)errno, strerror(errno)); + wrsync(p1p[1], 1, "[04] child3->parent"); + } else { + printf("child3: wrote %d byes to slave of closed master\n", n); + fflush(stdout); + wrsync(p1p[1], 2, "[04] child3->parent"); + } + rdsync(pp1[0], NULL, "[05] parent->child3"); + testex(slavefd, "child3: after parent reopen of master"); + testwr(slavefd, "child3: after parent reopen of master"); + fflush(stdout); + n = write(slavefd, "bar", 4); + if (n < 0) { + perror("child3: writing to slave"); + } else { + printf("child3: wrote %d bytes to slave\n", n); + fflush(stdout); + } + wrsync(p1p[1], 0, "[06] child3->parent"); + rdsync(pp1[0], NULL, "[07] parent->child3"); + wrsync(p1p[1], 0, "[08] child3->parent"); + exit(0); +} + +void +child2(void) +{ + struct sigaction sa; + + close(p21[0]); + setpgid(0, 0); + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + if (sigaction(SIGHUP, &sa, NULL) < 0) { + wrsync(p21[1], 1, "[00] child2->child1"); + perror("child2: sigaction"); + fflush(stdout); + exit(1); + } + printf("child2: set up signal handler\n"); + testctty("child2: after start"); + testwr(slavefd, "child2: after start"); + wrsync(p21[1], 0, "[00] child2->child1"); + rdsync(pp1[0], NULL, "[01] parent->child2"); + + testctty("child2: after child1 exit"); + testex(slavefd, "child2: after child1 exit"); + testwr(slavefd, "child2: after child1 exit"); + close(slavefd); + testctty("child2: after close of slavefd"); + slavefd = open(slave, O_RDWR|O_NONBLOCK); + if (slavefd < 0) { + wrsync(p1p[1], 1, "[02] child2->parent"); + printf("child2: failed reopen of slave\n"); + fflush(stdout); + exit(1); + } +#ifdef TIOCSCTTY + ioctl(slavefd, TIOCSCTTY, 0); +#endif + printf("child2: reopened slave\n"); + testctty("child2: after reopen of slave"); + fflush(stdout); + close(slavefd); + pid3 = fork(); + if (!pid3) { + child3(); + } else if (pid3 == -1) { + wrsync(p1p[1], 1, "[02] child2->parent"); + perror("child2: fork of child3"); + exit(1); + } + printf("child2: forked child3=%ld\n", (long)pid3); + fflush(stdout); + exit(0); +} + +void +child1(void) +{ + int status; + +#if 0 + setuid(1); +#endif + close(pp1[1]); + close(p1p[0]); + close(masterfd); + ptyint_void_association(); + slavefd = open(slave, O_RDWR|O_NONBLOCK); + if (slavefd < 0) { + perror("child1: open slave"); + exit(1); + } +#ifdef TIOCSCTTY + ioctl(slavefd, TIOCSCTTY, 0); +#endif + + printf("child1: opened slave\n"); + testctty("child1: after slave open"); + + if (pipe(p21) < 0) { + perror("pipe child2->child1"); + exit(1); + } + pid2 = fork(); + if (!pid2) { + child2(); + } else if (pid2 == -1) { + perror("child1: fork child2"); + exit(1); + } + close(p21[1]); + printf("child1: forked child2=%ld\n", (long)pid2); + fflush(stdout); + rdsync(p21[0], &status, "[00] child2->child1"); + exit(status); +} + +int +main(int argc, char *argv[]) +{ + long retval; + int status; + char buf[4]; + int n; + + prog = argv[0]; + + printf("parent: pid=%ld\n", (long)getpid()); + + retval = ptyint_getpty_ext(&masterfd, slave, sizeof(slave), 0); + + if (retval) { + com_err(prog, retval, "open master"); + exit(1); + } +#if 0 + chown(slave, 1, -1); +#endif + printf("parent: master opened; slave=%s\n", slave); + fflush(stdout); + +#if defined(HAVE_GRANTPT) && defined(HAVE_STREAMS) +#ifdef O_NOCTTY + printf("parent: attempting to open slave before unlockpt\n"); + fflush(stdout); + slavefd = open(slave, O_RDWR|O_NONBLOCK|O_NOCTTY); + if (slavefd < 0) { + printf("parent: failed slave open before unlockpt errno=%ld (%s)\n", + (long)errno, strerror(errno)); + } else { + printf("parent: WARNING: " + "succeeded in opening slave before unlockpt\n"); + } + close(slavefd); +#endif + if (grantpt(masterfd) < 0) { + perror("parent: grantpt"); + exit(1); + } + if (unlockpt(masterfd) < 0) { + perror("parent: unlockpt"); + exit(1); + } +#endif /* HAVE_GRANTPT && HAVE_STREAMS */ + + if (pipe(pp1) < 0) { + perror("pipe parent->child1"); + exit(1); + } + if (pipe(p1p) < 0) { + perror("pipe child1->parent"); + exit(1); + } + + pid1 = fork(); + if (!pid1) { + child1(); + } else if (pid1 == -1) { + perror("fork of child1"); + exit(1); + } + printf("parent: forked child1=%ld\n", (long)pid1); + fflush(stdout); + if (waitpid(pid1, &status1, 0) < 0) { + perror("waitpid for child1"); + exit(1); + } + printf("parent: child1 exited, status=%d\n", status1); + if (status1) + exit(status1); + + wrsync(pp1[1], 0, "[01] parent->child2"); + rdsync(p1p[0], &status, "[02] child3->parent"); + if (status) { + fprintf(stderr, "child2 or child3 got an error\n"); + exit(1); + } + + printf("parent: closing master\n"); + fflush(stdout); + close(masterfd); + chmod(slave, 0666); + printf("parent: closed master\n"); + wrsync(pp1[1], 0, "[03] parent->child3"); + + rdsync(p1p[0], &status, "[04] child3->parent"); + switch (status) { + case 1: + break; + case 0: + exit(0); + default: + fprintf(stderr, "child3 got an error\n"); + fflush(stdout); + exit(1); + } + + retval = pty_getpty(&masterfd, slave2, sizeof(slave2)); + printf("parent: new master opened; slave=%s\n", slave2); +#if 0 +#ifdef HAVE_REVOKE + printf("parent: revoking\n"); + revoke(slave2); +#endif +#endif + fflush(stdout); + wrsync(pp1[1], 0, "[05] parent->child3"); + rdsync(p1p[0], NULL, "[06] child3->parent"); + + n = read(masterfd, buf, 4); + if (n < 0) { + perror("parent: reading from master"); + } else { + printf("parent: read %d bytes (%.*s) from master\n", n, n, buf); + fflush(stdout); + } + chmod(slave2, 0666); + close(masterfd); + wrsync(pp1[1], 0, "[07] parent->child3"); + rdsync(p1p[0], NULL, "[08] child3->parent"); + fflush(stdout); + exit(0); +} diff --git a/src/util/pty/sane_hostname.c b/src/util/pty/sane_hostname.c index 43814df..8881fde 100644 --- a/src/util/pty/sane_hostname.c +++ b/src/util/pty/sane_hostname.c @@ -42,7 +42,7 @@ pty_make_sane_hostname(struct sockaddr_in *addr, char **out) { struct hostent *hp; -#ifndef NO_UT_HOST +#ifdef HAVE_STRUCT_UTMP_UT_HOST struct utmp ut; #else struct utmpx utx; @@ -55,7 +55,7 @@ pty_make_sane_hostname(struct sockaddr_in *addr, if (maxlen && maxlen < 16) /* assume they meant 16, otherwise IP addr won't fit */ maxlen = 16; -#ifndef NO_UT_HOST +#ifdef HAVE_STRUCT_UTMP_UT_HOST ut_host_len = sizeof (ut.ut_host); #else ut_host_len = sizeof (utx.ut_host); diff --git a/src/util/pty/update_utmp.c b/src/util/pty/update_utmp.c index 0c089d6..72c41be 100644 --- a/src/util/pty/update_utmp.c +++ b/src/util/pty/update_utmp.c @@ -1,8 +1,7 @@ /* * pty_update_utmp: Update or create a utmp entry * - * Copyright 1995 by the Massachusetts Institute of Technology. - * + * Copyright 1995, 2001 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -17,7 +16,296 @@ * M.I.T. makes no representations about the suitability * of this software for any purpose. It is provided "as is" without * express or implied warranty. - * + */ + +/* + * Rant about the historical vagaries of utmp: + * ------------------------------------------- + * + * There exist many subtly incompatible incarnations of utmp, ranging + * from BSD to System V to Unix98 and everywhere in between. This + * rant attempts to collect in one place as much knowledge as possible + * about this portability nightmare. + * + * BSD: + * ---- + * + * The simplest (and earliest? possibly dating back to Version 7...) + * case is 4.x BSD utmp/wtmp. There are no auxiliary files. There is + * only a struct utmp, declared in utmp.h. Its contents usually + * include: + * + * char ut_line[] + * char ut_name[] + * char ut_host[] + * long ut_time + * + * The meanings of these fields follow their names reasonbly well. + * The ut_line field usually is the pathname of the tty device + * associated with the login, with the leading "/dev/" stripped off. + * + * It is believed that ut_host is nul-terminated, while the other + * strings are merely nul-padded. + * + * Generally, ut_name is an empty string for a logout record in both + * utmp and wtmp. For entries made by the window system or other + * terminal emulation stuff, ut_host is an empty string (at least + * under SunOS 4.x, it seems). The macro nonuser() is used to + * determine this if a utmp entry is made by the window system on at + * least SunOS 4.x. + * + * The native login never clears its own utmp entry or writes its own + * logout record; its parent (one of init, rlogind, telnetd, etc.) + * should handle that. In theory, getty could do that, but getty + * usually doesn't fork to exec login. + * + * Old (c. 1984) System V: + * ----------------------- + * + * This is partially conjecture, based on some reading of + * /usr/xpg2include/utmp.h on a SunOS 4.x system. There appears to + * only be a struct utmp, declared in utmp.h. It is likely used for + * both utmp and wtmp files. It is quite likely that the utmp is only + * supposed to be accessed via the getutline()/pututline() API. The + * contents of struct utmp seem to include: + * + * char ut_user[] + * char ut_id[] + * char ut_line[] + * short ut_pid + * short ut_type + * struct exit_status ut_exit + * time_t ut_time + * + * On these systems, ut_name is often #define'ed to be ut_user to be + * somewhat compatible with the BSD-style utmp. Note that there is + * not necessarily a ut_host field in this utmp structure. + * + * The ut_id field bears some explanation. The systems that use this + * style of utmp also use a sysV-ish init, which starts processes out + * of /etc/inittab rather than /etc/ttys, and has the concept of + * runlevels. The first field in each line of /etc/inittab contains a + * unique ID field. init probably gets really confused if there are + * conflicts here. Every process that init starts gets its own entry + * written to utmp. + * + * It is possible for multiple entries to have the same ut_line but + * different ut_id values, since the sysadmin will be responsible for + * assigning values to ut_id. Usually, ut_id is four characters, + * while the permissible unique ID values for entries in /etc/inittab + * are constrained to two characters, but this is not always the + * case. In the case where we are emulating the vendor's login + * program and being run out of getty, we need to account for which + * value of ut_id was used by the getty, since pututline() will search + * based on ut_id and not ut_line for some reason. + * + * The ut_pid and ut_type fields are used for bookkeeping by init. + * The ut_type field gets the value INIT_PROCESS for processes started + * by init. It gets the value LOGIN_PROCESS if it is a process that + * is prompting for a login name, and it gets the value USER_PROCESS + * for an actual valid login. When the process dies, either init + * cleans up after it and records a DEAD_PROCESS entry in utmp, or the + * process itself does so. It's not completely clear which actually + * happens, though it is quite possible that init only cleans up after + * processes that it starts itself. + * + * Other values of ut_type exist; they're largely internal bookkeeping + * for init's runlevels and such, and don't really interest this + * library at all. + * + * The ut_exit field contains the following members: + * + * short e_termination + * short e_exit + * + * It is not clear how these values are used; presumably they record + * the process termination status of dead processes. + * + * There is no uniform API for manipulating wtmp on systems that use + * this sort of utmp structure; it can be assumed that the structure + * can be directly written to the wtmp file. + * + * Unix98: + * ------- + * + * This description also likely applies to later System V derivatives + * as well as systems conforming to earlier X/Open standards such as + * XPG4. There is a new header, utmpx.h, which defines a struct utmpx + * and a new getutxline()/pututxline() API for accessing it. Some + * systems actually have a utmpx file on disk; others use the utmpx + * API to access a file named utmp, just to further confuse matters. + * + * The utmpx structure is guaranteed (by Unix98) to contain at least + * the following: + * + * char ut_user[] + * char ut_line[] + * char ut_id[] + * pid_t ut_pid + * short ut_type + * struct timeval ut_tv + * + * It is not guaranteed to contain, but often does contain, the + * following: + * + * char ut_host[] + * int ut_syslen + * int ut_session + * struct exit_status ut_exit + * + * The ut_syslen field, on systems that contain it, contains the + * number of significant characters in ut_host, including the + * terminating nul character. + * + * The main difference between this struct utmpx and the struct utmp + * used by early sysV derivatives is the change from a time_t or long + * for ut_time to a struct timeval for ut_tv. + * + * Comments in various header files imply that ut_session is used for + * window systems, but it's not clear how. Perhaps it contains the + * session ID of the session running the window system, e.g. the xdm + * or X server on an X11 system. + * + * Most of the description of the earlier sysV format probably applies + * here, with suitable changes of names. On systems that maintain + * utmpx and utmp files in parallel, it is assumed that using the + * pututxline() API is sufficient to keep them in sync. There are no + * known counterexamples to this. + * + * Nevertheless, there are, on some systems, API functions getutmp() + * and getutmpx() that appear to convert from struct utmpx to struct + * utmp and vice versa. This could be useful when there is a wtmp + * file but not a corresponding wtmpx file. + * + * Incidentally, ut_exit is sometimes present in the struct utmp but + * not the struct utmpx for a given system. Sometimes, it exists in + * both, but contains differently named members. It's probably one of + * the least portable pieces in this whole mess. + * + * Known Quirks of Specific OSes: + * ------------------------------ + * + * Solaris 2.x: + * + * Has utmpd, which will automatically clean up utmpx, utmp, wtmpx, + * wtmp after process termination, provided that pututxline() was + * used. + * + * Solaris 8 seems to have a bug in utmpname() that causes + * garbage filenames to be generated. Solaris 7 (and possibly Solaris + * 8) have a bug in utmpxname() that prevents them from looking at + * anything other than /var/adm/utmpx, it seems. For some reason, + * though, utmpname() goes and looks at the corresponding utmpx file. + * + * Solaris 7 (and may be 8 as well) has a bug in pututline() that + * interacts badly with prior invocation of getutline(): if + * getutline() finds an entry, calling pututline() without first + * calling setutent() will overwrite the record following the one that + * was intended. + * + * Also, ut_exit in utmpx contains ut_e_termination and + * ut_e_exit (otherwise it contains the expected e_termination and + * e_exit) only if _XPG4_2 is defined and __EXTENSIONS__ is not, which + * is not a compilation environment we're likely to encourage. The + * ut_exit field of utmp contains the expected fields. + * + * If _XPG4_2 is not defined or __EXTENSIONS__ is defined, the + * functions getutmp(), getutmpx(), updwtmp(), and updwtmpx() are + * available, as well as the undocumented functions makeutx() and + * modutx(). + * + * All the files utmp, utmpx, wtmp, and wtmpx exist. + * + * HP-UX 10.x: + * + * There is a curious interaction between how we allocate pty masters + * and how ttyname() works. It seems that if /dev/ptmx/clone is + * opened, a call to ptsname() on the master fd gets a filename of the + * form /dev/pty/tty[pqrs][0-9a-f], while ttyname() called on a fd + * opened with that filename returns a filename of the form + * /dev/tty[pqrs][0-9a-f] instead. These two filenames are actually + * hardlinks to the same special device node, so it shouldn't be a + * security problem. + * + * We can't call ttyname() in the parent because it would involve + * possibly acquiring a controlling terminal (which would be + * potentially problematic), so we have to resort to some trickery in + * order to ensure that the ut_line in the wtmp logout and login + * records match. If they don't match, various utilities such as last + * will get confused. Of course it's likely an OS bug that ttyname() + * and ptsname() are inconsistent in this way, but it's one that isn't + * too painful to work around. + * + * It seems that the HP-UX native telnetd has problems similar to ours + * in this area, though it manages to write the correct logout record + * to wtmp somehow. It probably does basically what we do here: + * search for a record with a matching ut_pid and grab its ut_line for + * writing into the logout record. Interestingly enough, its + * LOGIN_PROCESS record is of the form pty/tty[pqrs][0-9][a-f]. + * + * Uses four-character unique IDs for /etc/inittab, which means that + * programs not running out of init should use two-character ut_id + * fields to avoid conflict. + * + * In utmpx, ut_exit contains __e_termination and __e_exit, while + * ut_exit in utmp contains the expected fields. + * + * There is no wtmpx file, despite there being utmp and utmpx files. + * + * Irix 6.x: + * + * In utmpx, ut_exit contains __e_termination and __e_exit, which get + * #define aliases e_termination and e_exit if _NO_XOPEN4 is true. + * Curiously enough, utmp.h declares ut_exit to have __e_termination + * and __e_exit as well, but does #define e_termination + * __e_termination, etc. if another header (utmpx.h) hasn't already + * declared struct __exit_status. It seems that the default + * compilation environment has the effect of making _NO_XOPEN4 true + * though. + * + * If _NO_XOPEN4 is true, getutmp(), getutmpx(), updwtmp(), and + * updwtmpx() are available, as well as the undocumented functions + * makeutx() and modutx(). + * + * All the files utmp, utmpx, wtmp, and wtmpx exist. + * + * Tru64 Unix 4.x: + * + * In utmpx, ut_exit contains ut_termination and ut_exit, while utmp + * contains the expected fields. The files utmp and wtmp seem to + * exist, but not utmpx or wtmpx. + * + * When writing a logout entry, the presence of a non-empty username + * confuses last. + * + * AIX 4.3.x: + * + * The ut_exit field seems to exist in utmp, but not utmpx. The files + * utmp and wtmp seem to exist, but not utmpx, or wtmpx. + * + * libpty Implementation Decisions: + * -------------------------------- + * + * We choose to use the pututxline() whenever possible, falling back + * to pututline() and calling write() to write out struct utmp if + * necessary. The code to handle pututxline() and pututline() is + * rather similar, since the structure members are quite similar, and + * we make the assumption that it will never be necessary to call + * both. This allows us to avoid duplicating lots of code, by means + * of some slightly demented macros. + * + * If neither pututxline() nor pututline() are available, we assume + * BSD-style utmp files and behave accordingly, writing the structure + * out to disk ourselves. + * + * On systems where updwtmpx() or updwtmp() are available, we use + * those to update the wtmpx or wtmp file. When they're not + * available, we write the utmpx or utmp structure out to disk + * ourselves, though sometimes conversion from utmpx to utmp format is + * needed. + * + * We assume that at logout the system is ok with with having an empty + * username both in utmp and wtmp. */ #include <com_err.h> @@ -32,178 +320,387 @@ #ifndef UTMP_FILE #define UTMP_FILE "/etc/utmp" #endif -#ifndef NO_UT_PID -#define WTMP_REQUIRES_USERNAME -#endif -long pty_update_utmp (process_type, pid, username, line, host, flags) - int process_type; - int pid; - char *username, *line, *host; - int flags; -{ - struct utmp ent, ut; -#ifndef HAVE_SETUTENT - struct stat statb; - int tty; -#endif + +/* + * The following grossness exists to avoid duplicating lots of code + * between the cases where we have an old-style sysV utmp and where we + * have a modern (Unix98 or XPG4) utmpx. See the above history rant + * for further explanation. + */ +#if defined(HAVE_SETUTXENT) || defined(HAVE_SETUTENT) #ifdef HAVE_SETUTXENT - struct utmpx utx; -#endif -#ifndef NO_UT_PID - char *tmpx; - char utmp_id[5]; +#define PTY_STRUCT_UTMPX struct utmpx +#define PTY_SETUTXENT setutxent +#define PTY_GETUTXENT getutxent +#define PTY_GETUTXLINE getutxline +#define PTY_PUTUTXLINE pututxline +#define PTY_ENDUTXENT endutxent +#else +#define PTY_STRUCT_UTMPX struct utmp +#define PTY_SETUTXENT setutent +#define PTY_GETUTXENT getutent +#define PTY_GETUTXLINE getutline +#define PTY_PUTUTXLINE pututline +#define PTY_ENDUTXENT endutent #endif - char userbuf[32]; - int fd; - strncpy(ent.ut_line, line+sizeof("/dev/")-1, sizeof(ent.ut_line)); - ent.ut_time = time(0); -#ifdef NO_UT_PID - if (process_type == PTY_LOGIN_PROCESS) +static int better(const PTY_STRUCT_UTMPX *, const PTY_STRUCT_UTMPX *, + const PTY_STRUCT_UTMPX *); +static int match_pid(const PTY_STRUCT_UTMPX *, + const PTY_STRUCT_UTMPX *); +static PTY_STRUCT_UTMPX *best_utxent(const PTY_STRUCT_UTMPX *); + +/* + * Utility function to determine whether A is a better match for + * SEARCH than B. Should only be called by best_utxent(). + */ +static int +better(const PTY_STRUCT_UTMPX *search, + const PTY_STRUCT_UTMPX *a, const PTY_STRUCT_UTMPX *b) +{ + if (strncmp(search->ut_id, b->ut_id, sizeof(b->ut_id))) { + if (!strncmp(search->ut_id, a->ut_id, sizeof(a->ut_id))) { + return 1; + } + } + + if (strncmp(a->ut_id, b->ut_id, sizeof(b->ut_id))) { + /* Got different UT_IDs; find the right one. */ + if (!strncmp(search->ut_id, b->ut_id, sizeof(b->ut_id))) { + /* Old entry already matches; use it. */ + return 0; + } + if (a->ut_type == LOGIN_PROCESS + && b->ut_type != LOGIN_PROCESS) { + /* Prefer LOGIN_PROCESS */ + return 1; + } + if (search->ut_type == DEAD_PROCESS + && a->ut_type == USER_PROCESS + && b->ut_type != USER_PROCESS) { + /* + * Try USER_PROCESS if we're entering a DEAD_PROCESS. + */ + return 1; + } + return 0; + } else { + /* + * Bad juju. We shouldn't get two entries with identical + * ut_id fields for the same value of ut_line. pututxline() + * will probably pick the first entry, in spite of the strange + * state of utmpx, if we rewind with setutxent() first. + * + * For now, return 0, to force the earlier entry to be used. + */ + return 0; + } +} + +static int +match_pid(const PTY_STRUCT_UTMPX *search, const PTY_STRUCT_UTMPX *u) +{ + if (u->ut_type != LOGIN_PROCESS && u->ut_type != USER_PROCESS) return 0; -#else /* NO_UT_PID */ - ent.ut_pid = pid; + if (u->ut_pid == search->ut_pid) { + /* + * One of ut_line or ut_id should match, else some nastiness + * may result. We can fall back to searching by ut_line if + * need be. This should only really break if we're login.krb5 + * running out of getty, or we're cleaning up after the vendor + * login, and either the vendor login or the getty has + * different ideas than we do of what both ut_id and ut_line + * should be. It should be rare, though. We may want to + * remove this restriction later. + */ + if (!strncmp(u->ut_line, search->ut_line, sizeof(u->ut_line))) + return 1; + if (!strncmp(u->ut_id, search->ut_id, sizeof(u->ut_id))) + return 1; + } + return 0; +} + +/* + * This expects to be called with SEARCH pointing to a struct utmpx + * with its ut_type equal to USER_PROCESS or DEAD_PROCESS, since if + * we're making a LOGIN_PROCESS entry, we presumably don't care about + * preserving existing state. At the very least, the ut_pid, ut_line, + * ut_id, and ut_type fields must be filled in by the caller. + */ +static PTY_STRUCT_UTMPX * +best_utxent(const PTY_STRUCT_UTMPX *search) +{ + PTY_STRUCT_UTMPX utxtmp, *utxp; + int i, best; + + memset(&utxtmp, 0, sizeof(utxtmp)); + + /* + * First, search based on pid, but only if non-zero. + */ + if (search->ut_pid) { + i = 0; + PTY_SETUTXENT(); + while ((utxp = PTY_GETUTXENT()) != NULL) { + if (match_pid(search, utxp)) { + return utxp; + } + i++; + } + } + /* + * Uh-oh, someone didn't enter our pid. Try valiantly to search + * by terminal line. + */ + i = 0; + best = -1; + PTY_SETUTXENT(); + while ((utxp = PTY_GETUTXLINE(search)) != NULL) { + if (better(search, utxp, &utxtmp)) { + utxtmp = *utxp; + best = i; + } + memset(utxp, 0, sizeof(*utxp)); + i++; + } + if (best == -1) + return NULL; + PTY_SETUTXENT(); + for (i = 0; i <= best; i++) { + if (utxp != NULL) + memset(utxp, 0, sizeof(*utxp)); + utxp = PTY_GETUTXLINE(search); + } + return utxp; +} + +/* + * All calls to this function for a given login session must have the + * pids be equal; various things will break if this is not the case, + * since we do some searching based on the pid. Note that if a parent + * process calls this via pty_cleanup(), it should still pass the + * child's pid rather than its own. + */ +long +pty_update_utmp(int process_type, int pid, const char *username, + const char *line, const char *host, int flags) +{ + PTY_STRUCT_UTMPX utx, *utxtmp, utx2; + const char *cp; + size_t len; + char utmp_id[5]; + + /* + * Zero things out in case there are fields we don't handle here. + * They tend to be non-portable anyway. + */ + memset(&utx, 0, sizeof(utx)); + utxtmp = NULL; + cp = line; + if (strncmp(cp, "/dev/", sizeof("/dev/") - 1) == 0) + cp += sizeof("/dev/") - 1; + strncpy(utx.ut_line, cp, sizeof(utx.ut_line)); + utx.ut_pid = pid; switch (process_type) { case PTY_LOGIN_PROCESS: - ent.ut_type = LOGIN_PROCESS; + utx.ut_type = LOGIN_PROCESS; break; case PTY_USER_PROCESS: - ent.ut_type = USER_PROCESS; + utx.ut_type = USER_PROCESS; break; case PTY_DEAD_PROCESS: - ent.ut_type = DEAD_PROCESS; + utx.ut_type = DEAD_PROCESS; break; default: return PTY_UPDATE_UTMP_PROCTYPE_INVALID; } -#endif /*NO_UT_PID*/ - -#ifndef NO_UT_HOST - if (host) - strncpy(ent.ut_host, host, sizeof(ent.ut_host)); + len = strlen(line); + if (len >= 2) { + cp = line + len - 1; + if (*(cp - 1) != '/') + cp--; /* last two characters, unless it's a / */ + } else + cp = line; + /* + * HP-UX has mostly 4-character inittab ids, while most other sysV + * variants use only 2-charcter inittab ids, so to avoid + * conflicts, we pick 2-character ut_ids for our own use. We may + * want to feature-test for this, but it would be somewhat of a + * pain, and would eit cross-compiling. + */ +#ifdef __hpux + strcpy(utmp_id, cp); +#else + if (len > 2 && *(cp - 1) != '/') + sprintf(utmp_id, "k%s", cp - 1); else - ent.ut_host[0] = '\0'; + sprintf(utmp_id, "k0%s", cp); #endif + strncpy(utx.ut_id, utmp_id, sizeof(utx.ut_id)); + /* + * Get existing utmpx entry for PID or LINE, if any, so we can + * copy some stuff from it. This is particularly important if we + * are login.krb5 and are running out of getty, since getty will + * have written the entry for the line with ut_type == + * LOGIN_PROCESS, and what it has recorded in ut_id may not be + * what we come up with, since that's up to the whim of the + * sysadmin who writes the inittab entry. + * + * Note that we may be screwed if we try to write a logout record + * for a vendor's login program, since it may construct ut_line + * and ut_id differently from us; even though we search on ut_pid, + * we validate against ut_id or ut_line to sanity-check. We may + * want to rethink whether to actually include this check, since + * it should be highly unlikely that there will be a bogus entry + * in utmpx matching our pid. + */ + if (process_type != PTY_LOGIN_PROCESS) + utxtmp = best_utxent(&utx); -#ifndef NO_UT_PID - if (!strcmp (line, "/dev/console")) { -#if (defined(sun) && defined(__SVR4)) - strncpy (ent.ut_id, "co", 4); -#else - strncpy (ent.ut_id, "cons", 4); -#endif - } else { - tmpx = line + strlen(line)-1; - if (*(tmpx-1) != '/') tmpx--; /* last two characters, unless it's a / */ -#ifdef __hpux - strcpy(utmp_id, tmpx); +#ifdef HAVE_SETUTXENT + if (gettimeofday(&utx.ut_tv, NULL)) + return errno; #else - sprintf(utmp_id, "kl%s", tmpx); + (void)time(&utx.ut_time); #endif - strncpy(ent.ut_id, utmp_id, sizeof(ent.ut_id)); - } - strncpy(ent.ut_user, username, sizeof(ent.ut_user)); -#else - strncpy(ent.ut_name, username, sizeof(ent.ut_name)); + /* + * On what system is there not ut_host? Unix98 doesn't mandate + * this field, but we have yet to see a system that supports utmpx + * that doesn't have it. For what it's worth, some ancient utmp + * headers on svr4 systems imply that there's no ut_host in struct + * utmp... + */ +#if (defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMPX_UT_HOST)) \ + || (!defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMP_UT_HOST)) + if (host != NULL) { + strncpy(utx.ut_host, host, sizeof(utx.ut_host)); + /* Unlike other things in utmpx, ut_host is nul-terminated? */ + utx.ut_host[sizeof(utx.ut_host) - 1] = '\0'; + } else + utx.ut_host[0] = '\0'; +#if (defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMPX_UT_SYSLEN)) \ + || (!defined (HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMP_UT_SYSLEN)) + if (host != NULL) + utx.ut_syslen = strlen(utx.ut_host) + 1; + else + utx.ut_syslen = 0; #endif - if(username[0]) - strncpy(userbuf, username, sizeof(userbuf)); - else userbuf[0] = '\0'; - -#ifdef HAVE_SETUTENT - - utmpname(UTMP_FILE); - setutent(); -/* If we need to preserve the user name in the wtmp structure and - * Our flags tell us we can obtain it from the utmp and we succeed in - * obtaining it, we then save the utmp structure we obtain, write - * out the utmp structure and change the username pointer so it is used by - * update_wtmp.*/ -#ifdef WTMP_REQUIRES_USERNAME - if (( !username[0]) && (flags&PTY_UTMP_USERNAME_VALID) - &&line) - { - struct utmp *utptr; - strncpy(ut.ut_line, line, sizeof(ut.ut_line)); - utptr = getutline(&ut); - if (utptr) - strncpy(userbuf,utptr->ut_user,sizeof(ut.ut_user)); - } #endif - pututline(&ent); - endutent(); - + /* XXX deal with ut_addr? */ + + if (utxtmp != NULL) { + /* + * For entries not of type LOGIN_PROCESS, override some stuff + * with what was in the previous entry we found, if any. + */ + strncpy(utx.ut_id, utxtmp->ut_id, sizeof(utx.ut_id)); + utx.ut_pid = utxtmp->ut_pid; + } + + strncpy(utx.ut_user, username, sizeof(utx.ut_user)); + + /* + * Make a copy now and deal with copying relevant things out of + * utxtmp in case setutxline() or pututxline() clobbers utxtmp. + * (After all, the returned pointer from the getutx*() functions + * is allowed to point to static storage that may get overwritten + * by subsequent calls to related functions.) + */ + utx2 = utx; + if (process_type == PTY_DEAD_PROCESS && utxtmp != NULL) { + /* + * Use ut_line from old entry to avoid confusing last on + * HP-UX. + */ + strncpy(utx2.ut_line, utxtmp->ut_line, sizeof(utx2.ut_line)); + } + + PTY_SETUTXENT(); + PTY_PUTUTXLINE(&utx); + PTY_ENDUTXENT(); + + /* Don't record LOGIN_PROCESS entries. */ + if (process_type == PTY_LOGIN_PROCESS) + return 0; + #ifdef HAVE_SETUTXENT - setutxent(); -#ifdef HAVE_GETUTMPX - getutmpx(&ent, &utx); + return ptyint_update_wtmpx(&utx2); #else - /* For platforms like HPUX and Dec Unix which don't have getutmpx */ - strncpy(utx.ut_user, ent.ut_user, sizeof(ent.ut_user)); - strncpy(utx.ut_id, ent.ut_id, sizeof(ent.ut_id)); - strncpy(utx.ut_line, ent.ut_line, sizeof(ent.ut_line)); - utx.ut_pid = pid; /* kludge for Irix, etc. to avoid trunc. */ - utx.ut_type = ent.ut_type; -#ifdef UT_EXIT_STRUCTURE_DIFFER - utx.ut_exit.ut_exit = ent.ut_exit.e_exit; -#else -/* KLUDGE for now; eventually this will be a feature test... See PR#[40] */ -#ifdef __hpux - utx.ut_exit.__e_termination = ent.ut_exit.e_termination; - utx.ut_exit.__e_exit = ent.ut_exit.e_exit; -#else - /*xxx do nothing for now; we don't even know the structure member exists*/ -#endif -#endif - utx.ut_tv.tv_sec = ent.ut_time; - utx.ut_tv.tv_usec = 0; + return ptyint_update_wtmp(&utx2); #endif +} + +#else /* !(HAVE_SETUTXENT || HAVE_SETUTENT) */ + +long +pty_update_utmp(int process_type, int pid, const char *username, + const char *line, const char *host, int flags) +{ + struct utmp ent, ut; + const char *cp; + int tty, lc, fd; + off_t seekpos; + ssize_t ret; + struct stat statb; + + memset(&ent, 0, sizeof(ent)); +#ifdef HAVE_STRUCT_UTMP_UT_HOST if (host) - strncpy(utx.ut_host, host, sizeof(utx.ut_host)); - else - utx.ut_host[0] = 0; - pututxline(&utx); - endutxent(); -#endif /* HAVE_SETUTXENT */ + strncpy(ent.ut_host, host, sizeof(ent.ut_host)); +#endif + strncpy(ent.ut_name, username, sizeof(ent.ut_name)); + cp = line; + if (strncmp(cp, "/dev/", sizeof("/dev/") - 1) == 0) + cp += sizeof("/dev/") - 1; + strncpy(ent.ut_line, cp, sizeof(ent.ut_line)); + (void)time(&ent.ut_time); -#else /* HAVE_SETUTENT */ - if (flags&PTY_TTYSLOT_USABLE) + if (flags & PTY_TTYSLOT_USABLE) tty = ttyslot(); else { - int lc; - tty = -1; - if ((fd = open(UTMP_FILE, O_RDWR)) < 0) - return errno; - for (lc = 0; - lseek(fd, (off_t)(lc * sizeof(struct utmp)), SEEK_SET) != -1; - lc++) { - if (read(fd, (char *) &ut, sizeof(struct utmp)) != sizeof(struct utmp)) - break; - if (strncmp(ut.ut_line, ent.ut_line, sizeof(ut.ut_line)) == 0) { - tty = lc; -#ifdef WTMP_REQUIRES_USERNAME - if (!username&&(flags&PTY_UTMP_USERNAME_VALID)) - strncpy(userbuf, ut.ut_user, sizeof(ut.ut_user)); -#endif - break; + tty = -1; + fd = open(UTMP_FILE, O_RDONLY); + if (fd == -1) + return errno; + for (lc = 0; ; lc++) { + seekpos = lseek(fd, (off_t)(lc * sizeof(struct utmp)), SEEK_SET); + if (seekpos != (off_t)(lc * sizeof(struct utmp))) + break; + if (read(fd, (char *) &ut, sizeof(struct utmp)) + != sizeof(struct utmp)) + break; + if (strncmp(ut.ut_line, ent.ut_line, sizeof(ut.ut_line)) == 0) { + tty = lc; + break; + } } - } - close(fd); + close(fd); } - - if (tty > 0 && (fd = open(UTMP_FILE, O_WRONLY, 0)) >= 0) { - (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); - (void)write(fd, (char *)&ent, sizeof(struct utmp)); - (void)close(fd); + if (tty > 0) { + fd = open(UTMP_FILE, O_WRONLY); + if (fd == -1) + return 0; + if (fstat(fd, &statb)) { + close(fd); + return 0; + } + seekpos = lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + if (seekpos != (off_t)(tty * sizeof(struct utmp))) { + close(fd); + return 0; + } + ret = write(fd, (char *)&ent, sizeof(struct utmp)); + if (ret != sizeof(struct utmp)) { + ftruncate(fd, statb.st_size); + } + close(fd); } - - -#endif /* HAVE_SETUTENT */ - /* Don't record LOGIN_PROCESS entries. */ if (process_type == PTY_LOGIN_PROCESS) - return 0; + return 0; else - return ptyint_update_wtmp(&ent, host, userbuf); + return ptyint_update_wtmp(&ent); } +#endif diff --git a/src/util/pty/update_wtmp.c b/src/util/pty/update_wtmp.c index 7e8d573..f557d36 100644 --- a/src/util/pty/update_wtmp.c +++ b/src/util/pty/update_wtmp.c @@ -1,8 +1,7 @@ /* - * ptyint_update_utmp: Update or create a utmp entry - * - * Copyright 1995 by the Massachusetts Institute of Technology. + * ptyint_update_wtmp: Update wtmp. * + * Copyright 1995, 2001 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby @@ -28,7 +27,7 @@ #define WTMP_FILE _PATH_WTMP #endif -#if !defined(WTMPX_FILE) && defined(_PATH_WTMPX) && defined(HAVE_UPDWTMPX) +#if !defined(WTMPX_FILE) && defined(_PATH_WTMPX) #define WTMPX_FILE _PATH_WTMPX #endif @@ -37,82 +36,88 @@ #define WTMP_FILE "/usr/adm/wtmp" #endif -#if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) -/* This is ugly, but the lack of standardization in the utmp/utmpx - * space, and what glibc implements and doesn't make available, is - * even worse. - */ -#undef HAVE_UPDWTMPX /* Don't use updwtmpx for glibc 2.1 */ -#endif +#ifdef HAVE_SETUTXENT -long ptyint_update_wtmp (ent , host, user) - struct utmp *ent; - char *host; - char *user; +/* + * Welcome to conditional salad. + * + * This really wants to take a (const struct utmpx *) but updutmpx() + * on Solaris at least doesn't take a const argument. *sigh* + */ +long +ptyint_update_wtmpx(struct utmpx *ent) { +#if !(defined(HAVE_UPDWTMPX) && defined(WTMPX_FILE)) struct utmp ut; - struct stat statb; - int fd; - time_t uttime; -#ifdef HAVE_UPDWTMPX - struct utmpx utx; - - getutmpx(ent, &utx); - if (host) - strncpy(utx.ut_host, host, sizeof(utx.ut_host) ); - else - utx.ut_host[0] = 0; - if (user) - strncpy(utx.ut_user, user, sizeof(utx.ut_user)); - updwtmpx(WTMPX_FILE, &utx); #endif -#ifdef HAVE_UPDWTMP -#ifndef HAVE_UPDWTMPX -/* This is already performed byupdwtmpx if present.*/ - updwtmp(WTMP_FILE, ent); -#endif /* HAVE_UPDWTMPX*/ -#else /* HAVE_UPDWTMP */ +#if defined(HAVE_UPDWTMPX) && defined(WTMPX_FILE) + updwtmpx(WTMPX_FILE, ent); + return 0; +#else - if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) >= 0) { - if (!fstat(fd, &statb)) { - (void)memset((char *)&ut, 0, sizeof(ut)); -#ifdef __hpux - strncpy (ut.ut_id, ent->ut_id, sizeof (ut.ut_id)); +#ifdef HAVE_GETUTMP + getutmp(ent, &ut); +#else /* Emulate getutmp(). Yuck. */ + memset(&ut, 0, sizeof(ut)); + strncpy(ut.ut_name, ent->ut_user, sizeof(ut.ut_name)); + strncpy(ut.ut_line, ent->ut_line, sizeof(ut.ut_line)); + ut.ut_time = ent->ut_tv.tv_sec; +#ifdef HAVE_STRUCT_UTMP_UT_HOST + strncpy(ut.ut_host, ent->ut_host, sizeof(ut.ut_host)); + ut.ut_host[sizeof(ut.ut_host) - 1] = '\0'; +#ifdef HAVE_STRUCT_UTMP_UT_SYSLEN + ut.ut_syslen = strlen(ut.ut_host) + 1; #endif - (void)strncpy(ut.ut_line, ent->ut_line, sizeof(ut.ut_line)); - (void)strncpy(ut.ut_name, ent->ut_name, sizeof(ut.ut_name)); -#ifndef NO_UT_HOST - (void)strncpy(ut.ut_host, ent->ut_host, sizeof(ut.ut_host)); #endif - (void)time(&uttime); - ut.ut_time = uttime; -#if defined(HAVE_GETUTENT) && defined(USER_PROCESS) - if (ent->ut_name) { - if (!ut.ut_pid) - ut.ut_pid = getpid(); -#ifndef __hpux - ut.ut_type = USER_PROCESS; -#else - ut.ut_type = ent->ut_type; +#ifdef HAVE_STRUCT_UTMP_UT_ID + strncpy(ut.ut_id, ent->ut_id, sizeof(ut.ut_id)); #endif - } else { -#ifdef EMPTY - ut.ut_type = EMPTY; -#else - ut.ut_type = DEAD_PROCESS; /* For Linux brokenness*/ -#endif +#ifdef HAVE_STRUCT_UTMP_UT_PID + ut.ut_pid = ent->ut_pid; +#endif +#ifdef HAVE_STRUCT_UTMP_UT_TYPE + ut.ut_type = ent->ut_type; +#endif +#if defined(PTY_UTMP_E_EXIT) && defined(PTY_UTMPX_E_EXIT) + ut.ut_exit.PTY_UTMP_E_EXIT = ent->ut_exit.PTY_UTMPX_E_EXIT; + ut.ut_exit.PTY_UTMP_E_TERMINATION = + ent->ut_exit.PTY_UTMPX_E_TERMINATION; +#endif +#endif /* !HAVE_GETUTMP */ + + return ptyint_update_wtmp(&ut); +#endif /* !(defined(WTMPX_FILE) && defined(HAVE_UPDWTMPX)) */ +} - } +#endif /* HAVE_SETUTXENT */ + +#if !(defined(WTMPX_FILE) && defined(HAVE_UPDWTMPX)) \ + || !defined(HAVE_SETUTXENT) + +long +ptyint_update_wtmp(struct utmp *ent) +{ +#ifndef HAVE_UPDWTMP + int fd; + struct stat statb; #endif - if (write(fd, (char *)&ut, sizeof(struct utmp)) != - sizeof(struct utmp)) - (void)ftruncate(fd, statb.st_size); - } + +#ifdef HAVE_UPDWTMP + updwtmp(WTMP_FILE, ent); +#else + fd = open(WTMP_FILE, O_WRONLY | O_APPEND, 0); + if (fd != -1 && !fstat(fd, &statb)) { + if (write(fd, (char *)ent, sizeof(struct utmp)) + != sizeof(struct utmp)) + (void)ftruncate(fd, statb.st_size); (void)close(fd); } -#endif /* HAVE_UPDWTMP */ - return 0; /* no current failure cases; file not found is not failure!*/ - +#endif + /* + * no current failure cases; file not found is not failure! + */ + return 0; } +#endif diff --git a/src/util/pty/void_assoc.c b/src/util/pty/void_assoc.c index a6ec33d..4b7e26c 100644 --- a/src/util/pty/void_assoc.c +++ b/src/util/pty/void_assoc.c @@ -23,32 +23,27 @@ #include <com_err.h> #include "libpty.h" #include "pty-int.h" -/* - * This routine will be called twice. It's not particularly important - * that the setsid() or TIOCSTTY ioctls succeed (they may not the - * second time), but rather that we have a controlling terminal at the - * end. It is assumed that vhangup doesn't exist and confuse the - * process's notion of controlling terminal on any system without - * TIOCNOTTY. That is, either vhangup() leaves the controlling - * terminal in tact, breaks the association completely, or the system - * provides TIOCNOTTY to get things back into a reasonable state. In - * practice, vhangup() either breaks the association completely or - * doesn't effect controlling terminals, so this condition is met. - */ -long ptyint_void_association() +/* + * This function gets called to set up the current process as a + * session leader (hence, can't be called except from a process that + * isn't already a session leader) and dissociates the controlling + * terminal (if any) from the session. + */ +long +ptyint_void_association(void) { - int con_fd; + int fd; #ifdef HAVE_SETSID (void) setsid(); #endif - - /* Void tty association first */ + /* Void tty association first */ #ifdef TIOCNOTTY - if ((con_fd = open("/dev/tty", O_RDWR)) >= 0) { - ioctl(con_fd, TIOCNOTTY, 0); - close(con_fd); - } + fd = open("/dev/tty", O_RDWR); + if (fd >= 0) { + ioctl(fd, TIOCNOTTY, 0); + close(fd); + } #endif - return 0; + return 0; } diff --git a/src/util/ss/ChangeLog b/src/util/ss/ChangeLog index 8cac4b7..72063d7 100644 --- a/src/util/ss/ChangeLog +++ b/src/util/ss/ChangeLog @@ -1,3 +1,11 @@ +2000-05-01 Nalin Dahyabhai <nalin@redhat.com> + + * help.c (ss_help): Don't overflow buffers "buffer" or "buf". + * list_rqs.c (ss_list_requests): Don't overflow buffer "buffer". + * mk_cmds.c (main): Don't overflow buffer "c_file". + * utils.c (generate_rqte): Update lengths of constant strings in + computing buffer size. + 2000-02-01 Ken Raeburn <raeburn@mit.edu> * listen.c (ss_listen): Local var END should be volatile. diff --git a/src/util/ss/help.c b/src/util/ss/help.c index e09b777..3c9cbec 100644 --- a/src/util/ss/help.c +++ b/src/util/ss/help.c @@ -53,16 +53,18 @@ void ss_help (argc, argv, sci_idx, info_ptr) return; } for (idx = 0; info->info_dirs[idx] != (char *)NULL; idx++) { - (void) strcpy(buffer, info->info_dirs[idx]); - (void) strcat(buffer, "/"); - (void) strcat(buffer, argv[1]); - (void) strcat(buffer, ".info"); + (void) strncpy(buffer, info->info_dirs[idx], sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = '\0'; + (void) strncat(buffer, "/", sizeof(buffer) - 1 - strlen(buffer)); + (void) strncat(buffer, argv[1], sizeof(buffer) - 1 - strlen(buffer)); + (void) strncat(buffer, ".info", sizeof(buffer) - 1 - strlen(buffer)); if ((fd = open(&buffer[0], O_RDONLY)) >= 0) goto got_it; } if ((fd = open(&buffer[0], O_RDONLY)) < 0) { char buf[MAXPATHLEN]; - strcpy(buf, "No info found for "); - strcat(buf, argv[1]); + strncpy(buf, "No info found for ", sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + strncat(buf, argv[1], sizeof(buf) - 1 - strlen(buf)); ss_perror(sci_idx, 0, buf); return; } diff --git a/src/util/ss/list_rqs.c b/src/util/ss/list_rqs.c index cf2c931..045a0c8 100644 --- a/src/util/ss/list_rqs.c +++ b/src/util/ss/list_rqs.c @@ -87,23 +87,24 @@ ss_list_requests(argc, argv, sci_idx, info_ptr) buffer[0] = '\0'; if (entry->flags & SS_OPT_DONT_LIST) continue; + buffer[sizeof(buffer) - 1] = '\0'; for (name = entry->command_names; *name; name++) { register int len = strlen(*name); - strncat(buffer, *name, len); + strncat(buffer, *name, sizeof(buffer) - 1 - strlen(buffer)); spacing += len + 2; if (name[1]) { - strcat(buffer, ", "); + strncat(buffer, ", ", sizeof(buffer) - 1 - strlen(buffer)); } } if (spacing > 23) { - strcat(buffer, NL); + strncat(buffer, NL, sizeof(buffer) - 1 - strlen(buffer)); fputs(buffer, output); spacing = 0; buffer[0] = '\0'; } - strncat(buffer, twentyfive_spaces, 25-spacing); - strcat(buffer, entry->info_string); - strcat(buffer, NL); + strncat(buffer, twentyfive_spaces, sizeof(buffer) - 1 - (25-spacing)); + strncpy(buffer + 25, entry->info_string, sizeof(buffer) - 1 - 25); + strncat(buffer, NL, sizeof(buffer) - 1 - strlen(buffer)); fputs(buffer, output); } } diff --git a/src/util/ss/mk_cmds.c b/src/util/ss/mk_cmds.c index 0bcd770..bba5edd 100644 --- a/src/util/ss/mk_cmds.c +++ b/src/util/ss/mk_cmds.c @@ -62,8 +62,9 @@ int main(argc, argv) p = strrchr(path, '.'); *p = '\0'; q = rindex(path, '/'); - strcpy(c_file, (q) ? q + 1 : path); - strcat(c_file, ".c"); + strncpy(c_file, (q) ? q + 1 : path, sizeof(c_file) - 1); + c_file[sizeof(c_file) - 1] = '\0'; + strncat(c_file, ".c", sizeof(c_file) - 1 - strlen(c_file)); *p = '.'; output_file = fopen(c_file, "w+"); diff --git a/src/util/ss/utils.c b/src/util/ss/utils.c index 9698e70..c578001 100644 --- a/src/util/ss/utils.c +++ b/src/util/ss/utils.c @@ -61,13 +61,12 @@ char * generate_rqte(func_name, info_string, cmds, options) var_name = generate_cmds_string(cmds); generate_function_definition(func_name); size = 6; /* " { " */ - size += strlen(var_name)+7; /* "quux, " */ - size += strlen(func_name)+7; /* "foo, " */ - size += strlen(info_string)+9; /* "\"Info!\", " */ + size += strlen(var_name)+8; /* "quux, " */ + size += strlen(func_name)+8; /* "foo, " */ + size += strlen(info_string)+8; /* "\"Info!\", " */ sprintf(numbuf, "%d", options); - size += strlen(numbuf); - size += 4; /* " }," + NL */ - string = malloc(size * sizeof(char *)); + size += strlen(numbuf)+5; /* " }," + NL + NUL */ + string = malloc(size); strcpy(string, " { "); strcat(string, var_name); strcat(string, ",\n "); |