SUMMARY: Preventing real name changes

From: Kevin Lentin <kev_at_marble.cc.monash.edu.au>
Date: Wed, 31 Jan 1996 23:24:09 +1100 (EST)

My (somewhat misunderstood) initial question was how to prevent real name
changes. I was not after disbling chfn totally since I wanted office &
phone numbers to be able to be changed.

My thanks to all who responded and especially Spider Boardman who had many
interesting ideas.

Options ranged from allowing chfn to run and then comparing the resulting
passwd entry to a saved version, etc. Another was to replace the whole
thing with a bsd style system but that wouldn't handle the C2 security.
Another solution was to ask the user for the info and then pipe through
chfn.

In the end, a little man page and include file reading and some large
assumptions (which turned out to be good ones) paid off. Seems that the
sia_chg_passwd, sia_chg_shell and sia_chg_finger routines do almost all the
work of passwd,chsh and chfn. It even has a hook to allow the user to
provide their own data collection routine. So the solution is to write a
program that mimics the passwd/chfn/chsh argument processing and then calls
one of those functions. With chfn, I just ask it to use my own collection
routine and that routine passes almost all calls through to the default
routine except for the 'Name' prompt which it ignores.

Works like a charm. Most of the hassle is replicating the passwd/chfn/chsh
argument processing which is weird to say the least. I was feeling a little
silly so I made them all behave identically as far as I can tell. Makes for
an interesting bit of argument processing. The actual code for changing the
passwd entry is minimal.

This solution also does not require the the original DU binaries to exist
and allows root to change real names.

The best way to show how this all works is to post the code. Most of it is
comments, a huge chunk is the arg processing, and about 8 lines of actual
useful code. I'd like to say that I am extremely impressed with the sia_*
library functions and quite chuffed that this program could solve our
problem so cleanly.

Here is the code. Enjoy, use, ignore as you will...

/*
 * Wrapper for passwd/chsh/chfn to prevent real name changes in chfn
 *
 * Written by Kevin Lentin for the General Access Unix Group
 * (c) 1996 Kevin Lentin
 *
 * This code may be freely used as long as this copyright message remains
 * intact and part of the code.
 *
 * NB This program is merely a wrapper for the sia_chg_* functions. I
 * presume, although I do not know, that the passwd/chfn/chsh programs do
 * something very similar to this program except for the my_collect()
 * function. The existence of the sia_chg_* functions means that this program
 * does not have to do any forking and execing, etc of the real programs
 * while still containing all the security features that DU 3.2C offers.
 *
 * The main() routing goes to great lengths to mimic the behaviour of the
 * real DU 3.2C versions of these programs. In particular (and noted in more
 * detail below), the DU versions ignore everything after the username,
 * passwd allows multiple -f's or -s's but not both, chfn allows -f, gives a
 * usage report for -s. All programs give an error message for unknown
 * options appearing before the username. NB this means that although chfn -s
 * and chfn -p are equally invalid, one gives more diagnostics than the
 * other. I can only imagine what the insides of the DU program look like to
 * cause this behaviour but I've replicated the behaviour here anyway.
 *
 * The program works by using its own data collection routine for the finger
 * change operation. It merely calls the default sia_collect_trm function
 * except for the 'Name [' prompt which it just displays and ignores. The
 * my_collect() function could be easily modified to implement various other
 * policies. So too could the switch near the bottom which chooses which
 * collect function to use.
 *
 * I do not know if the return values of this program are the same as the
 * DU versions since the man pages don't tell you what the return values are.
 */

#include <siad.h>

/* An enum to describe who we are and what we want to do */
typedef enum oper_enum { doPasswd, doFinger, doShell } operation;

/*
 * The local collect routine. Almost always just passes things on.
 */
int my_collect(int timeout, int rendition, unsigned char* title, int num_prompts, prompt_t *prompts)
{
        /* If we are not root and this is a single line question asking for a
         * real name, then just display the name and continue.
         * This allows root to change real names.
         */
        if (getuid() && rendition == SIAONELINER && !strncmp(prompts[0].prompt, "\012Name [", 7))
        {
                printf("%s NO CHANGE ALLOWED\n", prompts[0].prompt);
                return SIACOLSUCCESS;
        } else {
                return sia_collect_trm(timeout, rendition, title, num_prompts, prompts);
        }
}

volatile int
usage(operation prog)
{
        switch (prog) {
                case doPasswd:
                        fprintf(stderr, "usage: passwd [-fs] [username]\n");
                        break;
                case doShell:
                        fprintf(stderr, "usage: chsh [username]\n");
                        break;
                case doFinger:
                        fprintf(stderr, "usage: chfn [-fs] [username]\n");
                        break;
        }
        exit (-1);
}

int
main(int argc, char** argv)
{
        /* We keep separate track of what program is running and what operation
         * is to be performed. This is because the process behaves differently
         * depending on what program was called. eg passwd -f does not behave
         * _exactly_ like chfn.
         */

        operation op = doPasswd;
        operation prog = doPasswd;

        /* If no user name is specified, then the sia routines change the
         * current user's info.
         */
        char* user = 0;
        char* progname;
        int i,res;

        /* strip off leading path to work out who we are */
        progname = strrchr(argv[0], '/');
        if (!progname)
                progname = argv[0];
        else
                progname++;

        /* Where are we starting... */
        if (!strcmp(progname, "chfn"))
                op = prog = doFinger;
        else if (!strcmp(progname, "chsh"))
                op = prog = doShell;

        /*
         * Process arguments. NB OSF's passwd/chfn/chsh behave like this:
         * If multiple users are given, all but the first are ignored. no error.
         * In fact, everything after the first username is ignored.
         * only passwd accepts arguments. ie passwd -f behaves like chfn but
         * chfn -s does not behave like chsh.
         * passwd will accept duplicate flags but will not allow -f after -s
         * and vice versa.
         * ie: LEGAL:
         * passwd user1 user2 -f -l -s -q user3 changes passwd of user1
         * chfn user1 -s -f -q --- user2 changes finger of user1
         * chsh user1 changes shell of user1
         * passwd -f -f -f user1 changes finger of user1
         * passwd -s -s user1 -f -q -v -- user3 changes shell of user1
         * ILLEGAL:
         * passwd -f -s user1
         * passwd -q user1
         * chfn -a user1
         * chsh -f user1
         */
        for (i=1; i<argc; i++)
        {
                if (argv[i][0] != '-') /* a user name */
                {
                        /* NB the OSF binaries ignore everything after the username */
                        user = strdup(argv[i]);
                        break;
                }

                /* Must be an option */

                if (argv[i][1] == 's')
                        if (prog == doShell) /* chsh ignores -s */
                                continue;
                        else if (op == doFinger) /* this must be a passwd -f or chfn */
                                usage(prog);
                        else
                                op = doShell;
                else if (argv[i][1] == 'f')
                        if (prog == doFinger) /* chfn ignores -f */
                                continue;
                        else if (op == doShell) /* this must be a passwd -s or chsh */
                                usage(prog);
                        else
                                op = doFinger;
                else {
                        /* Note that illegal -s and -f options are ignored */
                        fprintf(stderr, "%s: illegal option -- %c\n", argv[0], argv[i][1]);
                        usage(prog); /* this is an invalid option _before_ username */
                }
        }
        /* Not sure if I have to call this. Won't hurt */
        sia_chk_invoker();

        /* Call correct sia_chg_* routine as appropriate. Note difference
         * with the collect function for sia_chg_finger.
         */
        switch (op) {
                case doPasswd:
                        res = sia_chg_password(sia_collect_trm, user, argc, argv);
                        break;
                case doShell:
                        res = sia_chg_shell(sia_collect_trm, user, argc, argv);
                        break;
                case doFinger:
                        res = sia_chg_finger(my_collect, user, argc, argv);
                        break;
                default:
                        fprintf(stderr, "%s: Major bad karma in switch\n", argv[0]);
        }
        return res;
}

--
[=======================================================================]
[ Kevin Lentin                 |finger kevinl_at_fangorn.cs.monash.edu.au| ]
[ K.Lentin_at_cs.monash.edu.au    |for PGP public key block. Fingerprint | ]
[ Macintrash: 'Just say NO!'   |6024308DE1F84314  811B511DBA6FD596    | ]
[=======================================================================]
Received on Wed Jan 31 1996 - 14:01:10 NZDT

This archive was generated by hypermail 2.4.0 : Wed Nov 08 2023 - 11:53:46 NZDT