Comment 13 for bug 927828

Revision history for this message
TJ (tj) wrote :

The crux of the problem is that sudo in Precise (v1.8.3p1) is a massively re-factored code-base compared to v1.7.4p6 in Oneiric.

From what I've been able to deduce sudo has been massively re-written to use a 'plugin' architecture to easily support many different authorisation mechanisms. This seems to have divorced the sudo timestamp checking from the PAM authentication logic such that if a valid sudo timestamp exists for the user then a PAM session (via pam_open_session() ) is never created.

The sudo session closing logic however doesn't know this and blindly goes on to try and close the PAM session (via pam_close_session() ) which discovers that the PAM Config hasn't been initialised during authentication and hits this assert.

Next I'm going to look at the current upstream sudo in detail to see if this issue has been resolved and whether it is possible to backport any change.

I've tracked the call paths in Precise's v1.8.3p1 and provide it here.

plugins/sudoers/sudoers.c::sudoers_policy_main()
    /* Require a password if sudoers says so. */
    rval = check_user(validated, sudo_mode);
    if (rval != TRUE)
 goto done;

plugins/sudoers/check.c::check_user()
    status = timestamp_status(timestampdir, timestampfile, user_name,
 TS_MAKE_DIRS);

////**** TJ: if the timestamp is valid status == TS_CURRENT
////**** this code never calls verify_user() so the PAM session is not created.

    if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
    ...
     rval = verify_user(auth_pw, prompt);

plugins/sudoers/auth/sudo_auth.c::verify_user()

    while (--counter) {
    ...
 /* Call authentication functions. */
 for (auth = auth_switch; auth->name; auth++) {
     if (IS_DISABLED(auth))
  continue;

     if (NEEDS_USER(auth))
  set_perms(PERM_USER);

     success = auth->status = (auth->verify)(pw, p, auth);

plugins/sudoers/auth/sudo_auth.h
typedef struct sudo_auth {
    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, struct sudo_auth *auth);
    int (*setup)(struct passwd *pw, char **prompt, struct sudo_auth *auth);
    int (*verify)(struct passwd *pw, char *p, struct sudo_auth *auth);
    int (*cleanup)(struct passwd *pw, struct sudo_auth *auth);
    int (*begin_session)(struct passwd *pw, struct sudo_auth *auth);
    int (*end_session)(struct passwd *pw, struct sudo_auth *auth);
} sudo_auth;

plugins/sudoers/auth/sudo_auth.c
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

plugins/sudoers/auth/pam.c::pam_verify()
    *pam_status = pam_authenticate(pamh, PAM_SILENT);

...

src/sudo.c::exec_setup()
    if (policy_init_session(&policy_plugin, pw) != TRUE)
 goto done;

src/sudo.c::policy_init_session()
{
    if (plugin->u.policy->init_session)
 return plugin->u.policy->init_session(pwd);

plugin/sudoers/sudoers.c::sudoers_policy_init_session()
    return sudo_auth_begin_session(pwd);

plugins/sudoers/auth/sudo_auth.c::sudo_auth_begin_session()
    for (auth = auth_switch; auth->name; auth++) {
 if (auth->begin_session && !IS_DISABLED(auth)) {
     status = (auth->begin_session)(pw, auth);