Subject: Re: PROPOSAL: making passwd pluggable (sort of)
To: None <tech-userlevel@netbsd.org>
From: Aidan Cully <aidan@kublai.com>
List: tech-userlevel
Date: 01/30/2000 21:56:14
--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
On Sun, Jan 30, 2000 at 12:26:41PM -0500, Aidan Cully wrote:
> I'll try to have something next weekend, if no one objects.
Next weekend was overly unambitious. Note that I've barely tested this,
and I don't plan on committing it until there's been time for discussion,
but it implements everything I said it would in my proposal (except the
utility function which is getpass()), and a bag of crisps.
Notes:
* I don't include patches to domestic source, for obvious reasons,
and because I haven't done them yet. :-)
* I can't test YP, but since it and local are the only pw_methods
I've modified in this patch, that means that some features of the
patch didn't get any kind of a shake at all. The continuation
logic, in particular (which is supposed to make up for the fact
that yp_passwd can no longer call local_passwd, and that -k means
both krb4 and krb5) is an important feature that hasn't been
tested.
* It's now possible (unlike in the proposal) for password modules
to modify their acceptable argc/argv arguments and usage strings.
(yppasswd apparently can't take a -y argument.)
* I check against two modules using the same command line argument,
one taking an optarg and one not. It should never happen though,
so I flag a violent warning and exit immediately. Maybe I'm being
too rash?
I also grew the source files by a fair amount, but I'd like to think that
for all that, the control flow is much more logical, now, and that the
bloat comes in places that we will (hopefully) not need to modify, later.
Comments?
--aidan
--MGYHOYXEY6WxJCY8
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="passwd.patch"
--- ../passwd.orig/extern.h Tue Jan 25 20:18:48 2000
+++ extern.h Sun Jan 30 18:44:37 2000
@@ -35,10 +35,39 @@
* @(#)extern.h 8.1 (Berkeley) 4/2/94
*/
-int kadm_passwd __P((char *, char *, char *, char *));
-int kadm5_passwd __P((char *));
-int krb_check __P((void));
-int krb_passwd __P((void));
-int local_passwd __P((char *));
-void to64 __P((char *, long, int));
-int yp_passwd __P((char *));
+/* return values from pw_init() and pw_arg_end() */
+enum {
+ PW_USE_FORCE,
+ PW_USE,
+ PW_DONT_USE
+};
+
+void to64(char *, long, int);
+
+#ifdef KERBEROS5
+int krb5_init __P((const char *, const char **, const char **));
+int krb5_arg __P((char, const char *));
+int krb5_arg_end __P((void));
+void krb5_end __P((void));
+int krb5_chpw __P((const char *));
+#endif
+#ifdef KERBEROS
+int krb4_init __P((const char *, const char **, const char **));
+int krb4_arg __P((char, const char *));
+int krb4_arg_end __P((void));
+void krb4_end __P((void));
+int krb4_chpw __P((const char *));
+#endif
+#ifdef YP
+int yp_init __P((const char *, const char **, const char **));
+int yp_arg __P((char, const char *));
+int yp_arg_end __P((void));
+void yp_end __P((void));
+int yp_chpw __P((const char *));
+#endif
+/* local */
+int local_init __P((const char *, const char **, const char **));
+int local_arg __P((char, const char *));
+int local_arg_end __P((void));
+void local_end __P((void));
+int local_chpw __P((const char *));
--- ../passwd.orig/local_passwd.c Sat Jan 22 14:46:58 2000
+++ local_passwd.c Sun Jan 30 18:44:59 2000
@@ -62,6 +62,7 @@
static char *getnewpasswd __P((struct passwd *, int));
static uid_t uid;
+static int force_local;
char *tempname;
@@ -139,8 +140,44 @@
}
int
-local_passwd(uname)
- char *uname;
+local_init(progname, argptr, usageptr)
+ const char *progname;
+ const char **argptr, **usageptr;
+{
+ force_local = 0;
+ return (PW_USE);
+}
+
+int
+local_arg(char arg, const char *optarg)
+{
+ switch (arg) {
+ case 'l':
+ force_local = 1;
+ break;
+ default:
+ return(0);
+ }
+ return(1);
+}
+
+int
+local_arg_end()
+{
+ if (force_local)
+ return(PW_USE_FORCE);
+ return(PW_USE);
+}
+
+void
+local_end()
+{
+ /* NOOP */
+}
+
+int
+local_chpw(uname)
+ const char *uname;
{
struct passwd *pw;
struct passwd old_pw;
--- ../passwd.orig/passwd.c Tue Jan 25 20:18:48 2000
+++ passwd.c Sun Jan 30 20:58:44 2000
@@ -53,28 +53,47 @@
#include <unistd.h>
#include "extern.h"
+
+static struct pw_module_s {
+ const char *name;
+ const char *args;
+ const char *usage;
+ int (*pw_init) __P((const char *, const char **, const char **));
+ int (*pw_arg) __P((char, const char *));
+ int (*pw_arg_end) __P((void));
+ void (*pw_end) __P((void));
+
+ int (*pw_chpw) __P((const char*));
+ int invalid;
+#define INIT_INVALID 1
+#define ARG_INVALID 2
+ int use_class;
+} pw_modules[] = {
+#ifdef KERBEROS5
+ { "Kerberos5", "5ku:", "[-5] [-k] [-u principal]",
+ krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 },
+#endif
+#ifdef KERBEROS
+ { "Kerberos4", "4ku:i:r:", "[-4] [-k] [-u user] [-i instance] [-r realm]",
+ krb4_init, krb4_arg, krb4_arg_end, krb4_end, krb4_chpw, 0, 0 },
+#endif
+#ifdef YP
+ { "YP", "y", "[-y]",
+ yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 },
+#endif
+ /* local */
+ { "local", "l", "[-l]",
+ local_init, local_arg, local_arg_end, local_end, local_chpw, 0, 0 },
+ /* terminator */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
void usage __P((void));
-/*
- * Note on configuration:
- * Generally one would not use both Kerberos and YP
- * to maintain passwords.
- */
-
-int use_kerberos;
-int use_yp;
-int yppwd;
-int yflag;
-
extern char *__progname; /* from crt0.o */
int main __P((int, char **));
-#ifdef YP
-extern int _yp_check __P((char **)); /* buried deep inside libc */
-#endif
-
int
main(argc, argv)
int argc;
@@ -83,88 +102,103 @@
extern int optind;
int ch;
char *username;
-#if defined(KERBEROS)
- char *iflag = 0, *rflag = 0;
-#endif
-#if defined(KERBEROS) || defined(KERBEROS5)
- char *uflag = 0;
-#endif
-
-#if defined(KERBEROS) || defined(KERBEROS5)
- if (strcmp(__progname, "kpasswd") == 0)
- use_kerberos = 1;
- else
- use_kerberos = krb_check();
-#endif
-#ifdef YP
- use_yp = _yp_check(NULL);
-#endif
+ char optstring[64]; /* if we ever get more than 64 args, shoot me. */
+ const char *curopt, *optopt;
+ int i, j;
+ int valid;
+ int use_always;
+
+ /* allow passwd modules to do argv[0] specific processing */
+ use_always = -1;
+ for (i = 0; pw_modules[i].name != (char *) 0; i++) {
+ pw_modules[i].invalid = 1;
+ valid = (*pw_modules[i].pw_init)(__progname,
+ &pw_modules[i].args, &pw_modules[i].usage);
+
+ if (valid == PW_USE_FORCE && use_always == -1) {
+ /* only PW_USE_FORCE modules can run. */
+ use_always = i;
+ for (j = 0; j < i; j++)
+ pw_modules[j].invalid |= INIT_INVALID;
+ } else if (valid == PW_USE_FORCE)
+ pw_modules[i].invalid &= ~INIT_INVALID;
+ else if (valid == PW_USE && use_always == -1)
+ pw_modules[i].invalid &= ~INIT_INVALID;
+ }
- if (strcmp(__progname, "yppasswd") == 0) {
-#ifdef YP
- if (!use_yp)
- errx(1, "YP not in use.");
- use_kerberos = 0;
- yppwd = 1;
-#else
- errx(1, "YP support not compiled in.");
-#endif
+ /* Build the option string from the individual modules' option
+ * strings. Note that two modules can share a single option
+ * letter. */
+ optstring[0] = '\0';
+ j = 0;
+ for (i = 0; pw_modules[i].name != (char *) 0; i++) {
+ if (pw_modules[i].invalid)
+ continue;
+
+ curopt = pw_modules[i].args;
+ while (*curopt != '\0') {
+ if ((optopt = strchr(optstring, *curopt)) == (char *) 0) {
+ optstring[j++] = *curopt;
+ if (curopt[1] == ':') {
+ curopt++;
+ optstring[j++] = *curopt;
+ }
+ optstring[j] = '\0';
+ } else if ((optopt[1] == ':' && curopt[1] != ':') ||
+ (optopt[1] != ':' && curopt[1] == ':')) {
+ errx(1, "NetBSD ERROR! Different password "
+ "modules have two different ideas about "
+ "%c argument format.", curopt[0]);
+ }
+ curopt++;
+ }
}
- while ((ch = getopt(argc, argv, "lkyi:r:u:")) != -1)
- switch (ch) {
- case 'l': /* change local password file */
- if (yppwd)
- usage();
- use_kerberos = 0;
- use_yp = 0;
- break;
-#ifdef KERBEROS
- case 'i':
- iflag = optarg;
- break;
- case 'r':
- rflag = optarg;
- break;
-#endif
-#if defined(KERBEROS) || defined(KERBEROS5)
- case 'u':
- uflag = optarg;
- break;
-#endif
- case 'k': /* change Kerberos password */
-#if defined(KERBEROS) || defined(KERBEROS5)
- if (yppwd)
- usage();
- use_kerberos = 1;
- use_yp = 0;
- break;
-#endif
-#ifndef KERBEROS
- case 'i':
- case 'r':
- errx(1, "Kerberos4 support not compiled in.");
-#endif
-#if !defined(KERBEROS) && !defined(KERBEROS5)
- case 'u':
- errx(1, "Kerberos support not compiled in.");
-#endif
- case 'y': /* change YP password */
-#ifdef YP
- if (yppwd)
- usage();
- if (!use_yp)
- errx(1, "YP not in use.");
- use_kerberos = 0;
- yflag = 1;
- break;
-#else
- errx(1, "YP support not compiled in.");
-#endif
- default:
+ while ((ch = getopt(argc, argv, optstring)) != -1)
+ {
+ valid = 0;
+ for (i = 0; pw_modules[i].name != (char *) 0; i++) {
+ if (pw_modules[i].invalid)
+ continue;
+ if ((optopt = strchr(pw_modules[i].args, ch)) != (char *) 0) {
+ j = (optopt[1] == ':') ?
+ ! (*pw_modules[i].pw_arg)(ch, optarg) :
+ ! (*pw_modules[i].pw_arg)(ch, (char *) 0);
+ if (j != 0)
+ pw_modules[i].invalid |= ARG_INVALID;
+ if (pw_modules[i].invalid)
+ (*pw_modules[i].pw_end)();
+ } else {
+ /* arg doesn't match this module */
+ pw_modules[i].invalid |= ARG_INVALID;
+ (*pw_modules[i].pw_end)();
+ }
+ if (! pw_modules[i].invalid)
+ valid = 1;
+ }
+ if (! valid) {
usage();
+ exit(1);
+ }
+ }
+
+ /* select which module to use to actually change the password. */
+ use_always = 0;
+ valid = 0;
+ for (i = 0; pw_modules[i].name != (char *) 0; i++)
+ if (! pw_modules[i].invalid) {
+ pw_modules[i].use_class = (*pw_modules[i].pw_arg_end)();
+ if (pw_modules[i].use_class != PW_DONT_USE)
+ valid = 1;
+ if (pw_modules[i].use_class == PW_USE_FORCE)
+ use_always = 1;
}
+
+ if (! valid)
+ /* hang the DJ */
+ errx(1, "No valid password module specified.");
+
argc -= optind;
argv += optind;
@@ -176,15 +210,6 @@
case 0:
break;
case 1:
-#ifdef KERBEROS5
- if (use_kerberos && strcmp(argv[0], username)) {
- errx(1, "%s\n\t%s\n%s\n",
- "to change another user's Kerberos password, do",
- "\"kinit <user>; passwd; kdestroy\";",
- "to change a user's local passwd, use\
- \"passwd -l <user>\"");
- }
-#endif
username = argv[0];
break;
default:
@@ -192,30 +217,29 @@
exit(1);
}
-#if defined(KERBEROS5)
- if (use_kerberos)
- exit(kadm5_passwd(username));
-#elif defined(KERBEROS)
- if (uflag && (iflag || rflag))
- errx(1, "-u cannot be used with -r or -i");
-
- if (use_kerberos)
- exit(kadm_passwd(username, iflag, rflag, uflag));
-#endif
-#ifdef YP
- if (use_yp)
- exit(yp_passwd(username));
-#endif
- exit(local_passwd(username));
+ /* allow for fallback to other chpw() methods. */
+ for (i = 0; pw_modules[i].name != (char *) 0; i++) {
+ if ((use_always && pw_modules[i].use_class == PW_USE_FORCE) ||
+ (!use_always && pw_modules[i].use_class == PW_USE)) {
+ valid = (*pw_modules[i].pw_chpw)(username);
+ (*pw_modules[i].pw_end)();
+ if (valid >= 0)
+ exit(valid);
+ /* return value < 0 indicates continuation. */
+ }
+ }
+ exit(1);
}
void
usage()
{
+ int i;
- if (yppwd)
- fprintf(stderr, "usage: %s user\n", __progname);
- else
- fprintf(stderr, "usage: %s [-l] [-k] [-y] [-i instance] [-r realm] [-u fullname] user\n", __progname);
+ fprintf(stderr, "usage:\n");
+ for (i = 0; pw_modules[i].name != (char *) 0; i++)
+ if (! (pw_modules[i].invalid & INIT_INVALID))
+ fprintf(stderr, "\t%s %s [user]\n", __progname,
+ pw_modules[i].usage);
exit(1);
}
--- ../passwd.orig/yp_passwd.c Sun Dec 26 15:27:02 1999
+++ yp_passwd.c Sun Jan 30 21:48:24 2000
@@ -71,12 +71,13 @@
extern char *__progname; /* from crt0.o */
-extern int yflag, yppwd;
+static int yflag, yppwd;
+static char yppwdargs[] = "";
+static char yppwdusage[] = "";
static char *getnewpasswd __P((struct passwd *, char **));
-static int ypgetpwnam __P((char *));
+static int ypgetpwnam __P((const char *));
static void pw_error __P((char *, int, int));
-static void test_local __P((char *));
static uid_t uid;
char *domain;
@@ -92,25 +93,59 @@
errx(eval, "YP passwd database unchanged");
}
-static void
-test_local(username)
- char *username;
+int
+yp_init(progname, optstr, usage)
+ const char *progname;
+ const char **optstr, **usage;
{
+ if (strcmp(progname, "yppasswd") == 0) {
+ *optstr = yppwdargs;
+ *usage = yppwdusage;
+ yppwd = 1;
+ } else
+ yppwd = 0;
+ yflag = 0;
+ if (_yp_check(NULL) == 0) {
+ /* can't use YP. */
+ if (yppwd)
+ errx(1, "YP not in use.");
+ return(PW_DONT_USE);
+ }
+ return (yppwd ? PW_USE_FORCE : PW_USE);
+}
- /*
- * Something failed recoverably stating that the YP system couldn't
- * find this user. Look for a local passwd entry, and change that
- * if and only if we weren't run as yppasswd or with the -y option.
- * This function does not return if a local entry is found.
- */
- if (yppwd == 0 && yflag == 0)
- if ((getpwnam(username) != NULL) && !local_passwd(username))
- exit(0);
+int
+yp_arg(ch, arg)
+ char ch;
+ const char *arg;
+{
+ switch (ch) {
+ case 'y':
+ yflag = 1;
+ break;
+ default:
+ return(0);
+ }
+ return(1);
+}
+
+int
+yp_arg_end()
+{
+ if (yflag || yppwd)
+ return (PW_USE_FORCE);
+ return (PW_USE);
+}
+
+void
+yp_end()
+{
+ /* NOOP */
}
int
-yp_passwd(username)
- char *username;
+yp_chpw(username)
+ const char *username;
{
char *master;
int r, rpcport, status;
@@ -124,7 +159,7 @@
/*
* Get local domain
*/
- if ((r = yp_get_default_domain(&domain)) != NULL)
+ if ((r = yp_get_default_domain(&domain)) != 0)
errx(1, "can't get local YP domain. Reason: %s",
yperr_string(r));
@@ -133,9 +168,10 @@
* the daemon.
*/
if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
- test_local(username);
- errx(1, "can't find the master YP server. Reason: %s",
+ warnx("can't find the master YP server. Reason: %s",
yperr_string(r));
+ /* continuation */
+ return(-1);
}
/*
@@ -143,9 +179,10 @@
*/
if ((rpcport = getrpcport(master, YPPASSWDPROG,
YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
- test_local(username);
- errx(1, "master YP server not running yppasswd daemon.\n\t%s\n",
- "Can't change password.");
+ warnx("master YP server not running yppasswd daemon.\n\t%s\n",
+ "Can't change YP password.");
+ /* continuation */
+ return(-1);
}
/*
@@ -158,8 +195,9 @@
/* then get user's login identity */
if (!ypgetpwnam(username) ||
!(pw = getpwnam(username))) {
- test_local(username);
- errx(1, "unknown user %s", username);
+ warnx("YP unknown user %s", username);
+ /* continuation */
+ return(-1);
}
if (uid && uid != pw->pw_uid)
@@ -196,7 +234,7 @@
else
printf("The YP password has been changed on %s, %s\n",
master, "the master YP passwd server.");
- exit(0);
+ return(0);
}
static char *
@@ -263,7 +301,7 @@
static int
ypgetpwnam(nam)
- char *nam;
+ const char *nam;
{
char *val;
int reason, vallen;
--MGYHOYXEY6WxJCY8--