Clean up the sudoers auth API a bit and update the docs.

This commit is contained in:
Todd C. Miller
2010-05-27 14:53:11 -04:00
parent 7e6d1d1f7d
commit b2ed46652b
3 changed files with 110 additions and 119 deletions

View File

@@ -7,15 +7,17 @@ Purpose: to provide a simple API for authentication methods that
The sudo_auth struct looks like this:
typedef struct sudo_auth {
short flags; /* various flags, see below */
short status; /* status from verify routine */
int flags; /* various flags, see below */
int status; /* status from verify routine */
char *name; /* name of the method in string form */
void *data; /* method-specific data pointer */
int (*init) __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int (*setup) __P((struct passwd *pw, char **prompt, sudo_auth *auth));
int (*verify) __P((struct passwd *pw, char *p, sudo_auth *auth));
int (*cleanup) __P((struct passwd *pw, sudo_auth *auth));
int (*init)(struct passwd *pw, char **prompt, sudo_auth *auth);
int (*setup)(struct passwd *pw, char **prompt, sudo_auth *auth);
int (*verify)(struct passwd *pw, char *p, sudo_auth *auth);
int (*cleanup)(struct passwd *pw, sudo_auth *auth);
int (*begin_session)(struct passwd *pw, sudo_auth *auth);
int (*end_session)(sudo_auth *auth);
} sudo_auth;
The variables in the struct are as follows:
@@ -34,10 +36,11 @@ Possible values of sudo_auth.flags:
FLAG_USER Whether or not the auth functions should run with
the euid of the invoking user instead of 0.
FLAG_CONFIGURED If set then the auth method is assumed to have been
configured successfully. All auth methods start out
with this set. If an "init" or "setup" function
fails, this bit is cleared.
FLAG_DISABLED Set if an "init" or "setup" function fails.
FLAG_STANDALONE If set, this indicates that the method must
be the only auth method configured, and that
it will prompt for the password itself.
FLAG_ONEANDONLY If set, this indicates that the method is the
only one in use. Can be used by auth functions
@@ -97,32 +100,23 @@ Adding a new authentication method:
Each method should live in its own file. Add prototypes for the functions
in sudo_auth.h.
If this is a standalone method, add it to the standalone #if cascade
in sudo_auth.h. For instance, for a method, ``fooauth'', add:
Add the method to the ``auth_switch'' in sudo_auth.c. Note that
standalone methods must go first. If ``fooauth'' is a normal auth
method, its entry would look like:
#elif defined(HAVE_FOOAUTH)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "foo", \
foo_init, foo_setup, foo_verify, foo_cleanup)
#ifdef HAVE_FOOAUTH
AUTH_ENTRY("foo", 0, foo_init, foo_setup, foo_verify,
foo_cleanup, foo_begin_session, foo_end_session)
#endif
If the method needs to run as the user, not root, replace the first
parameter to AUTH_ENTRY (0) with FLAG_USER. If you don't have a
init/setup/cleanup routine, just use a NULL for that field.
If this is a standalone method, it would be:
For a normal authentication method, add it to the ``auth_switch'' in
sudo_auth.c. If ``fooauth'' is a normal auth method, its entry
would look like:
#ifdef HAVE_FOOAUTH
AUTH_ENTRY("foo", FLAG_STANDALONE, foo_init, foo_setup, foo_verify,
foo_cleanup, foo_begin_session, foo_end_session)
#endif
# ifdef HAVE_FOOAUTH
AUTH_ENTRY(0, "foo", foo_init, foo_setup, foo_verify, foo_cleanup)
# endif
Again, if the method doesn't need to run as root, replace the 0 with
FLAG_USER. Likewise, if you don't have a init/setup/cleanup routine,
just use a NULL for that field.
NOTE: You should not make a method both ``standalone'' and
``normal''. Just use the --without-passwd configure argument
to disable passwd/shadow file checking and then have your
auth routines check the FLAG_ONEANDONLY flag to see if
they are running standalone and act accordingly.
If the method needs to run as the user, not root, add FLAG_USER to
the second argument in the AUTH_ENTRY line. If you don't have an
init/setup/cleanup/begin/end routine, just use a NULL for that
field.

View File

@@ -52,36 +52,53 @@
#include "sudo_auth.h"
#include "insults.h"
sudo_auth auth_switch[] = {
#ifdef AUTH_STANDALONE
AUTH_STANDALONE
#else
# ifndef WITHOUT_PASSWD
AUTH_ENTRY(0, "passwd", passwd_init, NULL, passwd_verify, NULL, NULL, NULL)
# endif
# if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
AUTH_ENTRY(0, "secureware", secureware_init, NULL, secureware_verify, NULL, NULL, NULL)
# endif
# ifdef HAVE_AFS
AUTH_ENTRY(0, "afs", NULL, NULL, afs_verify, NULL, NULL, NULL)
# endif
# ifdef HAVE_DCE
AUTH_ENTRY(0, "dce", NULL, NULL, dce_verify, NULL, NULL, NULL)
# endif
# ifdef HAVE_KERB4
AUTH_ENTRY(0, "kerb4", kerb4_init, NULL, kerb4_verify, NULL, NULL, NULL)
# endif
# ifdef HAVE_KERB5
AUTH_ENTRY(0, "kerb5", kerb5_init, NULL, kerb5_verify, kerb5_cleanup, NULL, NULL)
# endif
# ifdef HAVE_SKEY
AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL, NULL, NULL)
# endif
# ifdef HAVE_OPIE
AUTH_ENTRY(0, "OPIE", NULL, rfc1938_setup, rfc1938_verify, NULL, NULL, NULL)
# endif
#endif /* AUTH_STANDALONE */
AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
static sudo_auth auth_switch[] = {
/* Standalone entries first */
#ifdef HAVE_PAM
AUTH_ENTRY("pam", FLAG_STANDALONE, pam_init, NULL, pam_verify, pam_cleanup, pam_begin_session, pam_end_session)
#endif
#ifdef HAVE_SECURID
AUTH_ENTRY("SecurId", FLAG_STANDALONE, securid_init, securid_setup, securid_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_SIA_SES_INIT
AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sia_setup, sia_verify, sia_cleanup, NULL, NULL)
#endif
#ifdef HAVE_AIXAUTH
AUTH_ENTRY("aixauth", FLAG_STANDALONE, NULL, NULL, aixauth_verify, aixauth_cleanup, NULL, NULL)
#endif
#ifdef HAVE_FWTK
AUTH_ENTRY("fwtk", FLAG_STANDALONE, fwtk_init, NULL, fwtk_verify, fwtk_cleanup, NULL, NULL)
#endif
#ifdef HAVE_BSD_AUTH_H
AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_cleanup, NULL, NULL)
#endif
/* Non-standalone entries */
#ifndef WITHOUT_PASSWD
AUTH_ENTRY("passwd", 0, passwd_init, NULL, passwd_verify, NULL, NULL, NULL)
#endif
#if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD)
AUTH_ENTRY("secureware", 0, secureware_init, NULL, secureware_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_AFS
AUTH_ENTRY("afs", 0, NULL, NULL, afs_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_DCE
AUTH_ENTRY("dce", 0, NULL, NULL, dce_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_KERB4
AUTH_ENTRY("kerb4", 0, kerb4_init, NULL, kerb4_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_KERB5
AUTH_ENTRY("kerb5", 0, kerb5_init, NULL, kerb5_verify, kerb5_cleanup, NULL, NULL)
#endif
#ifdef HAVE_SKEY
AUTH_ENTRY("S/Key", 0, NULL, rfc1938_setup, rfc1938_verify, NULL, NULL, NULL)
#endif
#ifdef HAVE_OPIE
AUTH_ENTRY("OPIE", 0, NULL, rfc1938_setup, rfc1938_verify, NULL, NULL, NULL)
#endif
AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL)
};
extern char **NewArgv; /* XXX - for auditing */
@@ -93,7 +110,7 @@ verify_user(struct passwd *pw, char *prompt)
{
int counter = def_passwd_tries + 1;
int success = AUTH_FAILURE;
int flags, status, rval;
int flags, status, standalone, rval;
char *p;
sudo_auth *auth;
sigaction_t sa, osa;
@@ -114,19 +131,28 @@ verify_user(struct passwd *pw, char *prompt)
return -1;
}
/* Make sure we haven't mixed standalone and shared auth methods. */
standalone = IS_STANDALONE(&auth_switch[0]);
if (standalone && auth_switch[1].name != NULL) {
audit_failure(NewArgv, "invalid authentication methods");
log_error(0, "Invalid authentication methods compiled into sudo! "
"You cannot mix standalone and non-standalone authentication.");
return -1;
}
/* Set FLAG_ONEANDONLY if there is only one auth method. */
if (auth_switch[1].name == NULL)
SET(auth_switch[0].flags, FLAG_ONEANDONLY);
/* Initialize auth methods and unconfigure the method if necessary. */
for (auth = auth_switch; auth->name; auth++) {
if (auth->init && IS_CONFIGURED(auth)) {
if (auth->init && !IS_DISABLED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
status = (auth->init)(pw, &prompt, auth);
if (status == AUTH_FAILURE)
CLR(auth->flags, FLAG_CONFIGURED);
SET(auth->flags, FLAG_DISABLED);
else if (status == AUTH_FATAL) { /* XXX log */
audit_failure(NewArgv, "authentication failure");
return -1; /* assume error msg already printed */
@@ -140,13 +166,13 @@ verify_user(struct passwd *pw, char *prompt)
while (--counter) {
/* Do any per-method setup and unconfigure the method if needed */
for (auth = auth_switch; auth->name; auth++) {
if (auth->setup && IS_CONFIGURED(auth)) {
if (auth->setup && !IS_DISABLED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
status = (auth->setup)(pw, &prompt, auth);
if (status == AUTH_FAILURE)
CLR(auth->flags, FLAG_CONFIGURED);
SET(auth->flags, FLAG_DISABLED);
else if (status == AUTH_FATAL) {/* XXX log */
audit_failure(NewArgv, "authentication failure");
return -1; /* assume error msg already printed */
@@ -158,21 +184,20 @@ verify_user(struct passwd *pw, char *prompt)
}
/* Get the password unless the auth function will do it for us */
#ifdef AUTH_STANDALONE
p = prompt;
#else
p = auth_getpass(prompt, def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF);
#endif /* AUTH_STANDALONE */
if (standalone)
p = prompt;
else
p = auth_getpass(prompt, def_passwd_timeout * 60, SUDO_CONV_PROMPT_ECHO_OFF);
/* Call authentication functions. */
for (auth = auth_switch; p && auth->name; auth++) {
if (!IS_CONFIGURED(auth))
if (IS_DISABLED(auth))
continue;
if (NEEDS_USER(auth))
set_perms(PERM_USER);
success = auth->status = (auth->verify)(pw, (char *)p, auth);
success = auth->status = (auth->verify)(pw, p, auth);
if (NEEDS_USER(auth))
restore_perms();
@@ -180,17 +205,15 @@ verify_user(struct passwd *pw, char *prompt)
if (auth->status != AUTH_FAILURE)
goto cleanup;
}
#ifndef AUTH_STANDALONE
if (p)
if (!standalone && p != NULL)
zero_bytes(p, strlen(p));
#endif
pass_warn();
}
cleanup:
/* Call cleanup routines. */
for (auth = auth_switch; auth->name; auth++) {
if (auth->cleanup && IS_CONFIGURED(auth)) {
if (auth->cleanup && !IS_DISABLED(auth)) {
if (NEEDS_USER(auth))
set_perms(PERM_USER);
@@ -240,7 +263,7 @@ int auth_begin_session(struct passwd *pw)
int status;
for (auth = auth_switch; auth->name; auth++) {
if (auth->begin_session && IS_CONFIGURED(auth)) {
if (auth->begin_session && !IS_DISABLED(auth)) {
status = (auth->begin_session)(pw, auth);
if (status == AUTH_FATAL) { /* XXX log */
audit_failure(NewArgv, "authentication failure");
@@ -257,7 +280,7 @@ int auth_end_session(void)
int status;
for (auth = auth_switch; auth->name; auth++) {
if (auth->end_session && IS_CONFIGURED(auth)) {
if (auth->end_session && !IS_DISABLED(auth)) {
status = (auth->end_session)(auth);
if (status == AUTH_FATAL) { /* XXX log */
return -1; /* assume error msg already printed */

View File

@@ -24,8 +24,8 @@
#define AUTH_FATAL 3
typedef struct sudo_auth {
short flags; /* various flags, see below */
short status; /* status from verify routine */
int flags; /* various flags, see below */
int status; /* status from verify routine */
char *name; /* name of the method as a string */
void *data; /* method-specific data pointer */
int (*init)(struct passwd *pw, char **prompt, struct sudo_auth *auth);
@@ -37,14 +37,15 @@ typedef struct sudo_auth {
} sudo_auth;
/* Values for sudo_auth.flags. */
/* XXX - these names are too long for my liking */
#define FLAG_USER 0x01 /* functions must run as the user, not root */
#define FLAG_CONFIGURED 0x02 /* method configured ok */
#define FLAG_ONEANDONLY 0x04 /* one and only auth method */
#define FLAG_DISABLED 0x02 /* method disabled */
#define FLAG_STANDALONE 0x04 /* standalone auth method */
#define FLAG_ONEANDONLY 0x08 /* one and only auth method */
/* Shortcuts for using the flags above. */
#define NEEDS_USER(x) ((x)->flags & FLAG_USER)
#define IS_CONFIGURED(x) ((x)->flags & FLAG_CONFIGURED)
#define IS_DISABLED(x) ((x)->flags & FLAG_DISABLED)
#define IS_STANDALONE(x) ((x)->flags & FLAG_STANDALONE)
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
/* Like tgetpass() but uses conversation function */
@@ -89,35 +90,8 @@ int securid_init(struct passwd *pw, char **prompt, sudo_auth *auth);
int securid_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
int securid_verify(struct passwd *pw, char *pass, sudo_auth *auth);
/* Fields: need_root, name, init, setup, verify, cleanup */
#define AUTH_ENTRY(r, n, i, s, v, c, b, e) \
{ (r|FLAG_CONFIGURED), AUTH_FAILURE, n, NULL, i, s, v, c , b, e },
/* Some methods cannot (or should not) interoperate with any others */
#if defined(HAVE_PAM)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "pam", \
pam_init, NULL, pam_verify, pam_cleanup, pam_begin_session, pam_end_session)
#elif defined(HAVE_SECURID)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "SecurId", \
securid_init, securid_setup, securid_verify, NULL, NULL, NULL)
#elif defined(HAVE_SIA_SES_INIT)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "sia", \
NULL, sia_setup, sia_verify, sia_cleanup, NULL, NULL)
#elif defined(HAVE_AIXAUTH)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "aixauth", \
NULL, NULL, aixauth_verify, aixauth_cleanup, NULL, NULL)
#elif defined(HAVE_FWTK)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "fwtk", \
fwtk_init, NULL, fwtk_verify, fwtk_cleanup, NULL, NULL)
#elif defined(HAVE_BSD_AUTH_H)
# define AUTH_STANDALONE \
AUTH_ENTRY(0, "bsdauth", \
bsdauth_init, NULL, bsdauth_verify, bsdauth_cleanup, NULL, NULL)
#endif
/* Fields: name, flags, init, setup, verify, cleanup, begin_sess, end_sess */
#define AUTH_ENTRY(n, f, i, s, v, c, b, e) \
{ (f), AUTH_FAILURE, (n), NULL, (i), (s), (v), (c) , (b), (e) },
#endif /* SUDO_AUTH_H */