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: The sudo_auth struct looks like this:
typedef struct sudo_auth { typedef struct sudo_auth {
short flags; /* various flags, see below */ int flags; /* various flags, see below */
short status; /* status from verify routine */ int status; /* status from verify routine */
char *name; /* name of the method in string form */ char *name; /* name of the method in string form */
void *data; /* method-specific data pointer */ void *data; /* method-specific data pointer */
int (*init) __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int (*init)(struct passwd *pw, char **prompt, sudo_auth *auth);
int (*setup) __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int (*setup)(struct passwd *pw, char **prompt, sudo_auth *auth);
int (*verify) __P((struct passwd *pw, char *p, sudo_auth *auth)); int (*verify)(struct passwd *pw, char *p, sudo_auth *auth);
int (*cleanup) __P((struct passwd *pw, 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; } sudo_auth;
The variables in the struct are as follows: 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 FLAG_USER Whether or not the auth functions should run with
the euid of the invoking user instead of 0. the euid of the invoking user instead of 0.
FLAG_CONFIGURED If set then the auth method is assumed to have been FLAG_DISABLED Set if an "init" or "setup" function fails.
configured successfully. All auth methods start out
with this set. If an "init" or "setup" function FLAG_STANDALONE If set, this indicates that the method must
fails, this bit is cleared. 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 FLAG_ONEANDONLY If set, this indicates that the method is the
only one in use. Can be used by auth functions 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 Each method should live in its own file. Add prototypes for the functions
in sudo_auth.h. in sudo_auth.h.
If this is a standalone method, add it to the standalone #if cascade Add the method to the ``auth_switch'' in sudo_auth.c. Note that
in sudo_auth.h. For instance, for a method, ``fooauth'', add: standalone methods must go first. If ``fooauth'' is a normal auth
method, its entry would look like:
#elif defined(HAVE_FOOAUTH) #ifdef HAVE_FOOAUTH
# define AUTH_STANDALONE \ AUTH_ENTRY("foo", 0, foo_init, foo_setup, foo_verify,
AUTH_ENTRY(0, "foo", \ foo_cleanup, foo_begin_session, foo_end_session)
foo_init, foo_setup, foo_verify, foo_cleanup) #endif
If the method needs to run as the user, not root, replace the first If this is a standalone method, it would be:
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.
For a normal authentication method, add it to the ``auth_switch'' in #ifdef HAVE_FOOAUTH
sudo_auth.c. If ``fooauth'' is a normal auth method, its entry AUTH_ENTRY("foo", FLAG_STANDALONE, foo_init, foo_setup, foo_verify,
would look like: foo_cleanup, foo_begin_session, foo_end_session)
#endif
# ifdef HAVE_FOOAUTH If the method needs to run as the user, not root, add FLAG_USER to
AUTH_ENTRY(0, "foo", foo_init, foo_setup, foo_verify, foo_cleanup) the second argument in the AUTH_ENTRY line. If you don't have an
# endif init/setup/cleanup/begin/end routine, just use a NULL for that
field.
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.

View File

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

View File

@@ -24,8 +24,8 @@
#define AUTH_FATAL 3 #define AUTH_FATAL 3
typedef struct sudo_auth { typedef struct sudo_auth {
short flags; /* various flags, see below */ int flags; /* various flags, see below */
short status; /* status from verify routine */ int status; /* status from verify routine */
char *name; /* name of the method as a string */ char *name; /* name of the method as a string */
void *data; /* method-specific data pointer */ void *data; /* method-specific data pointer */
int (*init)(struct passwd *pw, char **prompt, struct sudo_auth *auth); int (*init)(struct passwd *pw, char **prompt, struct sudo_auth *auth);
@@ -37,14 +37,15 @@ typedef struct sudo_auth {
} sudo_auth; } sudo_auth;
/* Values for sudo_auth.flags. */ /* 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_USER 0x01 /* functions must run as the user, not root */
#define FLAG_CONFIGURED 0x02 /* method configured ok */ #define FLAG_DISABLED 0x02 /* method disabled */
#define FLAG_ONEANDONLY 0x04 /* one and only auth method */ #define FLAG_STANDALONE 0x04 /* standalone auth method */
#define FLAG_ONEANDONLY 0x08 /* one and only auth method */
/* Shortcuts for using the flags above. */ /* Shortcuts for using the flags above. */
#define NEEDS_USER(x) ((x)->flags & FLAG_USER) #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) #define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
/* Like tgetpass() but uses conversation function */ /* 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_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
int securid_verify(struct passwd *pw, char *pass, sudo_auth *auth); int securid_verify(struct passwd *pw, char *pass, sudo_auth *auth);
/* Fields: need_root, name, init, setup, verify, cleanup */ /* Fields: name, flags, init, setup, verify, cleanup, begin_sess, end_sess */
#define AUTH_ENTRY(r, n, i, s, v, c, b, e) \ #define AUTH_ENTRY(n, f, i, s, v, c, b, e) \
{ (r|FLAG_CONFIGURED), AUTH_FAILURE, n, NULL, i, s, v, c , b, e }, { (f), 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
#endif /* SUDO_AUTH_H */ #endif /* SUDO_AUTH_H */