Modularize password changing mechanisms, as proposed in

<20000130122641.A8134@xanadu.kublai.com>:
Subject: PROPOSAL: making passwd pluggable (sort of)
Date: Sun, 30 Jan 2000 12:26:41 -0500
This commit is contained in:
aidan 2000-02-14 04:36:20 +00:00
parent 492312b9ed
commit 919f6272de
6 changed files with 384 additions and 1177 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.6 2000/01/26 01:18:48 aidan Exp $ */
/* $NetBSD: extern.h,v 1.7 2000/02/14 04:36:20 aidan Exp $ */
/*
* Copyright (c) 1994
@ -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 *));
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 *));
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 *));
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 *));
int local_arg __P((char, const char *));
int local_arg_end __P((void));
void local_end __P((void));
int local_chpw __P((const char *));

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: local_passwd.c,v 1.18 2000/01/12 05:13:32 mjl Exp $ */
/* $NetBSD: local_passwd.c,v 1.19 2000/02/14 04:36:21 aidan Exp $ */
/*-
* Copyright (c) 1990, 1993, 1994
@ -38,7 +38,7 @@
#if 0
static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94";
#else
__RCSID("$NetBSD: local_passwd.c,v 1.18 2000/01/12 05:13:32 mjl Exp $");
__RCSID("$NetBSD: local_passwd.c,v 1.19 2000/02/14 04:36:21 aidan Exp $");
#endif
#endif /* not lint */
@ -62,6 +62,7 @@ __RCSID("$NetBSD: local_passwd.c,v 1.18 2000/01/12 05:13:32 mjl Exp $");
static char *getnewpasswd __P((struct passwd *, int));
static uid_t uid;
static int force_local;
char *tempname;
@ -139,8 +140,43 @@ getnewpasswd(pw, min_pw_len)
}
int
local_passwd(uname)
char *uname;
local_init(progname)
const char *progname;
{
force_local = 0;
return (0);
}
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;

View File

@ -1,4 +1,4 @@
.\" $NetBSD: passwd.1,v 1.12 1999/03/22 18:16:41 garbled Exp $
.\" $NetBSD: passwd.1,v 1.13 2000/02/14 04:36:21 aidan Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@ -38,19 +38,46 @@
.Os
.Sh NAME
.Nm passwd ,
.Nm yppasswd
.Nm yppasswd ,
.Nm kpasswd
.Nd modify a user's password
.Sh SYNOPSIS
.Nm
.Nm passwd
.Op Fl l
.Op Ar user
.Nm passwd
.Op Fl y
.Op Ar user
.Nm passwd
.Op Fl 4
.Op Fl k
.Op Fl i Ar instance
.Op Fl r Ar realm
.Op Fl u Ar fullname
.Op Ar user
.Nm passwd
.Op Fl 5
.Op Fl k
.Op Fl u Ar fullname
.Op Ar user
.Nm kpasswd
.Op Fl 4
.Op Fl k
.Op Fl i Ar instance
.Op Fl r Ar realm
.Op Fl u Ar fullname
.Op Ar user
.Nm kpasswd
.Op Fl 5
.Op Fl k
.Op Fl u Ar fullname
.Op Ar user
.Nm yppasswd
.Op Ar user
.Sh DESCRIPTION
.Nm
changes the user's local or YP password. First, the user is prompted
for their current password.
changes the user's local, YP, or kerberos password. First, the user is
prompted for their current password.
If the current password is correctly typed, a new password is
requested.
The new password must be entered twice to avoid typing errors.
@ -62,6 +89,8 @@ Its total length must be less than
(currently 128 characters).
Numbers, upper case letters and meta characters
are encouraged.
.Pp
All options may not be available on all systems.
.Bl -tag -width flag
.It Fl l
This option causes the password to be updated only in the local
@ -79,11 +108,45 @@ is the equivalent of
with the
.Fl y
flag.
.It Fl 4
This option causes passwd to change the user's kerberos password,
using the kerberos 4 admin protocol.
.It Fl 5
This option causes passwd to change the user's kerberos password,
using the kerberos 5 admin protocol.
.It Fl k
This option causes passwd to change the user's kerberos password,
using either the kerberos 4 or kerberos 5 admin protocol.
If both kerberos 4 and kerberos 5 libraries and config files are
installed on the host, kerberos 5 will be used to change the password.
.Nm kpasswd
is the equivalent of
.Nm
with the
.Fl k
flag.
.It Fl i Ar instance
This option selects a non-default Kerberos 4 instance for the
Kerberos password to be changed.
.It Fl r Ar realm
This option selects a non-default Kerberos 4 realm for the Kerberos
password to be changed.
.It Fl u Ar fullname
This option specifies the entire principal.instance@realm (for Kerberos
4) or principal/instance@realm (for Kerberos 5) for the Kerberos
password to be changed.
.El
.Pp
This is the behavior if no flags are specified:
If Kerberos is active then
.Nm
will talk to the Kerberos server, attempting to use Kerberos 5, then
Kerberos 4 protocols to change the password (even if the user has an
entry in the local database.)
If Kerberos is unavailable, an attempt is made to use the YP database.
If the password is not in the YP database, then
an attempt is made to use the local password database.
.Pp
The super-user is not required to provide a user's current password
if only the local password is modified.
.Sh FILES

View File

@ -1,4 +1,4 @@
/* $NetBSD: passwd.c,v 1.15 2000/01/26 01:18:48 aidan Exp $ */
/* $NetBSD: passwd.c,v 1.16 2000/02/14 04:36:21 aidan Exp $ */
/*
* Copyright (c) 1988, 1993, 1994
@ -43,7 +43,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\n\
#if 0
static char sccsid[] = "from: @(#)passwd.c 8.3 (Berkeley) 4/2/94";
#else
__RCSID("$NetBSD: passwd.c,v 1.15 2000/01/26 01:18:48 aidan Exp $");
__RCSID("$NetBSD: passwd.c,v 1.16 2000/02/14 04:36:21 aidan Exp $");
#endif
#endif /* not lint */
@ -53,28 +53,54 @@ __RCSID("$NetBSD: passwd.c,v 1.15 2000/01/26 01:18:48 aidan Exp $");
#include <unistd.h>
#include "extern.h"
static struct pw_module_s {
const char *argv0;
const char *args;
const char *usage;
int (*pw_init) __P((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
{ NULL, "5ku:", "[-5] [-k] [-u principal]",
krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 },
{ "kpasswd", "5ku:", "[-5] [-k] [-u principal]",
krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 },
#endif
#ifdef KERBEROS
{ NULL, "4ku:i:r:", "[-4] [-k] [-u user] [-i instance] [-r realm]",
krb4_init, krb4_arg, krb4_arg_end, krb4_end, krb4_chpw, 0, 0 },
{ "kpasswd", "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
{ NULL, "y", "[-y]",
yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 },
{ "yppasswd", "", "[-y]",
yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 },
#endif
/* local */
{ NULL, "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,87 +109,123 @@ main(argc, argv)
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
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;
#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
/* allow passwd modules to do argv[0] specific processing */
use_always = 0;
valid = 0;
for (i = 0; pw_modules[i].pw_init != NULL; i++) {
pw_modules[i].invalid = 0;
if (pw_modules[i].argv0) {
/*
* If we have a module that matches this progname, be
* sure that no modules but those that match this
* progname can be used. If we have a module that
* matches against a particular progname, but does NOT
* match this one, don't use that module.
*/
if ((strcmp(__progname, pw_modules[i].argv0) == 0) &&
use_always == 0) {
for (j = 0; j < i; j++) {
pw_modules[j].invalid |= INIT_INVALID;
(*pw_modules[j].pw_end)();
}
use_always = 1;
} else if (use_always == 0)
pw_modules[i].invalid |= INIT_INVALID;
} else if (use_always)
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
if (pw_modules[i].invalid)
continue;
pw_modules[i].invalid |= (*pw_modules[i].pw_init)(__progname) ?
/* zero on success, non-zero on error */
INIT_INVALID : 0;
if (! pw_modules[i].invalid)
valid = 1;
}
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:
usage();
if (valid == 0)
errx(1, "Can't change password.");
/* 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].pw_init != NULL; 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, optstring)) != -1)
{
valid = 0;
for (i = 0; pw_modules[i].pw_init != NULL; 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].pw_init != NULL; 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 +238,6 @@ main(argc, argv)
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 +245,31 @@ main(argc, argv)
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].pw_init != NULL; i++) {
if (pw_modules[i].invalid)
continue;
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].pw_init != NULL; i++)
if (! (pw_modules[i].invalid & INIT_INVALID))
fprintf(stderr, "\t%s %s [user]\n", __progname,
pw_modules[i].usage);
exit(1);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: yp_passwd.c,v 1.21 1999/12/23 01:02:52 mjl Exp $ */
/* $NetBSD: yp_passwd.c,v 1.22 2000/02/14 04:36:21 aidan Exp $ */
/*
* Copyright (c) 1988, 1990, 1993, 1994
@ -38,7 +38,7 @@
#if 0
static char sccsid[] = "from: @(#)local_passwd.c 8.3 (Berkeley) 4/2/94";
#else
__RCSID("$NetBSD: yp_passwd.c,v 1.21 1999/12/23 01:02:52 mjl Exp $");
__RCSID("$NetBSD: yp_passwd.c,v 1.22 2000/02/14 04:36:21 aidan Exp $");
#endif
#endif /* not lint */
@ -71,12 +71,11 @@ __RCSID("$NetBSD: yp_passwd.c,v 1.21 1999/12/23 01:02:52 mjl Exp $");
extern char *__progname; /* from crt0.o */
extern int yflag, yppwd;
static int yflag;
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 +91,58 @@ pw_error(name, err, eval)
errx(eval, "YP passwd database unchanged");
}
static void
test_local(username)
char *username;
int
yp_init(progname)
const char *progname;
{
int yppwd;
/*
* 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);
if (strcmp(progname, "yppasswd") == 0) {
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(-1);
}
return (0);
}
int
yp_passwd(username)
char *username;
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)
return (PW_USE_FORCE);
return (PW_USE);
}
void
yp_end()
{
/* NOOP */
}
int
yp_chpw(username)
const char *username;
{
char *master;
int r, rpcport, status;
@ -124,7 +156,7 @@ yp_passwd(username)
/*
* 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 +165,10 @@ yp_passwd(username)
* 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 +176,10 @@ yp_passwd(username)
*/
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 +192,9 @@ yp_passwd(username)
/* 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 +231,7 @@ yp_passwd(username)
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 +298,7 @@ getnewpasswd(pw, old_pass)
static int
ypgetpwnam(nam)
char *nam;
const char *nam;
{
char *val;
int reason, vallen;