Subject: PROPOSAL: making passwd pluggable (sort of)
To: None <tech-userlevel@netbsd.org>
From: Aidan Cully <aidan@kublai.com>
List: tech-security
Date: 01/30/2000 12:26:41
  by redmail.netbsd.org with SMTP; 30 Jan 2000 17:26:44 -0000
	id 6D54B2019; Sun, 30 Jan 2000 12:26:41 -0500 (EST)
Date: Sun, 30 Jan 2000 12:26:41 -0500
From: Aidan Cully <aidan@kublai.com>
To: tech-userlevel@netbsd.org
Cc: current-users@netbsd.org, tech-security@netbsd.org
Subject: PROPOSAL: making passwd pluggable (sort of)
Message-ID: <20000130122641.A8134@xanadu.kublai.com>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
User-Agent: Mutt/1.1.2i

Those of you on src-changes may have noticed that /usr/bin/passwd can
now change your krb5 password when KERBEROS5 is defined in /etc/mk.conf.
I'm not too happy with the way I did it, though, since (among other
reasons) one of my goals (avoid changing the NetBSD tree as much as
possible) conflicted directly with a clean integration of krb5
functionality.  The passwd code is such an #ifdef zoo, though, that I
think maybe I should ignore that goal for this program...

Instead, I'd like to attempt to make passwd more pluggable, by defining
an array of passwd-module structures.  A first hack at the structure
definition:
struct passwd_module_s {
	char *args;
	int (*module_init)(char*);	/* __progname */
	    /* returns if or not the module is available for use. */
	int (*arg_callback)(char, char*);	/* arg, optarg */
	int (*arg_end)(void);
	    /* Returns if this module should be used by default, definately,
	     * or not at all.  Can errx if there's an argument problem. */
	void (*module_end)(void);

	void (*chpw)(char*);	/* username passed as last argument. */
	int invalid;	/* used to determine argument conflicts. */
} passwd_modules[] = {
#ifdef KERBEROS5
	{ "5ku:", krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0 },
#endif
#ifdef KERBEROS
	{ "4ku:i:r:", krb4_init, krb4_arg, krb4_arg_end, krb4_end, krb4_chpw, 0 },
#endif
#ifdef YP
	{ "y", yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0 },
#endif
	/* local */
	{ "lu:", local_init, local_arg, local_arg_end, local_end, local_chpw, 0 },
	/* terminator */
	{ 0, 0, 0, 0, 0, 0, 0 }
};

There's a couple of things, here...  First, the arg string to getopt
will be built at run-time, rather than at compile time, and that
building the arg string is a bit more complicated than a bunch of
chained strcat()s.  Arguments with the same letter, used by different
modules MUST have the same interpreted meanings for each module.  We
can figure out argument conflicts (e.g., passwd -kl) by setting a bit
for each element of the modules_array when that module doesn't
understand the arg.  When all modules have that bit set, there's an
argument conflict.  (I'll have to think a bit more on how to handle
error reporting, but I doubt it'll be too complicated.)

The order of elements in this structure is important, and should go
from most to least preferred methods of changing passwords.


For its part, passwd will provide at least this utility function for
the modules:

int read_string_noecho(char *prompt, char *pwbuf, int buflen);
    /* displays prompt, turns echo off and reads the password */

The password modules are, of course, linked in statically.

I think it should be fairly clear how I see everything fitting
together right now...  Does anyone see any problems, or have any
objections to my going ahead with this?  Need clarification on
anything?  I'll try to have something next weekend, if no one
objects.

--aidan