From 594c2d4efd5c5492ef39125456bb30e2a5c8a250 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Tue, 16 Jan 2018 10:27:58 -0700 Subject: [PATCH] Add an approval function to the sudo auth API which is run after the user's password has been verified. The approval function is run even if no password is required. This is currently only used for PAM (use pam_acct_mgmt) and BSD auth (auth_approval). --- plugins/sudoers/auth/API | 26 ++++++++-- plugins/sudoers/auth/bsdauth.c | 16 ++++++ plugins/sudoers/auth/pam.c | 87 +++++++++++++++++++------------ plugins/sudoers/auth/sudo_auth.c | 88 +++++++++++++++++++++----------- plugins/sudoers/auth/sudo_auth.h | 11 ++-- plugins/sudoers/check.c | 4 ++ plugins/sudoers/sudoers.h | 4 +- 7 files changed, 163 insertions(+), 73 deletions(-) diff --git a/plugins/sudoers/auth/API b/plugins/sudoers/auth/API index 55d2dfc0c..901cc8851 100644 --- a/plugins/sudoers/auth/API +++ b/plugins/sudoers/auth/API @@ -14,10 +14,11 @@ typedef struct sudo_auth { int (*init)(struct passwd *pw, sudo_auth *auth); int (*setup)(struct passwd *pw, char **prompt, sudo_auth *auth); - int (*verify)(struct passwd *pw, char *p, sudo_auth *auth); + int (*verify)(struct passwd *pw, char *p, sudo_auth *auth, struct sudo_conv_callback *callback); + int (*approval)(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); + int (*begin_session)(struct passwd *pw, char **user_env[], struct sudo_auth *auth); + int (*end_session)(struct passwd *pw, struct sudo_auth *auth); } sudo_auth; The variables in the struct are as follows: @@ -71,13 +72,22 @@ The functions in the struct are as follows: pointer to the prompt string may be used to add method-specific info to the prompt. - int verify(struct passwd *pw, char *p, sudo_auth *auth) + int verify(struct passwd *pw, char *p, sudo_auth *auth, struct sudo_conv_callback *callback) Function to do user verification for this auth method. For standalone auth methods ``p'' is the prompt string. For normal auth methods, ``p'' is the password the user entered. + The callback should be passed to auth_getpass() to allow sudoers + to unlock the ticket file when sudo is suspended. Note that standalone auth methods are responsible for rerading the password themselves. + int approval(struct passwd *pw, struct sudo_auth *auth) + Function to perform account management and approval *after* + the user has authenticated successfully. This function may + check for expired accounts, perform time of day restrictions, etc. + For PAM, this calls pam_acct_mgmt(). For BSD auth, it calls + auth_approval(). + int cleanup(struct passwd *pw, sudo_auth *auth) Function to do per-auth method cleanup. This is only run at the end of the authentication process, after the user @@ -85,6 +95,14 @@ The functions in the struct are as follows: The ``auth->status'' variable contains the result of the last authentication attempt which may be interesting. + int begin_session(struct passwd *pw, char **user_env[], struct sudo_auth *auth) + Function to begin a user session. This is used for session handling + in PAM and SIA. + + int end_session(struct passwd *pw, struct sudo_auth *auth) + Function to end a user session. This is used for session handling + in PAM and SIA. + A note about standalone methods. Some authentication methods can't coexist with any others. This may be because they encapsulate other methods (pam, sia) or because they have a special way of interacting diff --git a/plugins/sudoers/auth/bsdauth.c b/plugins/sudoers/auth/bsdauth.c index 8bf775dcf..e34b68676 100644 --- a/plugins/sudoers/auth/bsdauth.c +++ b/plugins/sudoers/auth/bsdauth.c @@ -168,6 +168,22 @@ bsdauth_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_con debug_return_int(AUTH_FAILURE); } +int +bsdauth_approval(struct passwd *pw, sudo_auth *auth) +{ + struct bsdauth_state *state = auth->data; + debug_decl(bsdauth_approval, SUDOERS_DEBUG_AUTH) + + if (auth_approval(state->as, state->lc, pw->pw_name, "auth-sudo") == 0) { + if (auth_getstate(state->as) & AUTH_EXPIRED) + log_warningx(0, "%s", N_("your account has expired")); + else + log_warningx(0, "%s", N_("approval failed")); + debug_return_int(AUTH_FAILURE); + } + debug_return_int(AUTH_SUCCESS); +} + int bsdauth_cleanup(struct passwd *pw, sudo_auth *auth) { diff --git a/plugins/sudoers/auth/pam.c b/plugins/sudoers/auth/pam.c index 898cc99ff..dcd7f2a22 100644 --- a/plugins/sudoers/auth/pam.c +++ b/plugins/sudoers/auth/pam.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005, 2007-2015 Todd C. Miller + * Copyright (c) 1999-2005, 2007-2018 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -186,43 +186,13 @@ sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_co } switch (*pam_status) { case PAM_SUCCESS: - *pam_status = pam_acct_mgmt(pamh, PAM_SILENT); - switch (*pam_status) { - case PAM_SUCCESS: - debug_return_int(AUTH_SUCCESS); - case PAM_AUTH_ERR: - log_warningx(0, N_("account validation failure, " - "is your account locked?")); - debug_return_int(AUTH_FATAL); - case PAM_NEW_AUTHTOK_REQD: - log_warningx(0, N_("Account or password is " - "expired, reset your password and try again")); - *pam_status = pam_chauthtok(pamh, - PAM_CHANGE_EXPIRED_AUTHTOK); - if (*pam_status == PAM_SUCCESS) - debug_return_int(AUTH_SUCCESS); - if ((s = pam_strerror(pamh, *pam_status)) != NULL) { - log_warningx(0, - N_("unable to change expired password: %s"), s); - } - debug_return_int(AUTH_FAILURE); - case PAM_AUTHTOK_EXPIRED: - log_warningx(0, - N_("Password expired, contact your system administrator")); - debug_return_int(AUTH_FATAL); - case PAM_ACCT_EXPIRED: - log_warningx(0, - N_("Account expired or PAM config lacks an \"account\" " - "section for sudo, contact your system administrator")); - debug_return_int(AUTH_FATAL); - } - /* FALLTHROUGH */ + debug_return_int(AUTH_SUCCESS); case PAM_AUTH_ERR: case PAM_AUTHINFO_UNAVAIL: case PAM_MAXTRIES: case PAM_PERM_DENIED: sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO, - "pam_acct_mgmt: %d", *pam_status); + "pam_authenticate: %d", *pam_status); debug_return_int(AUTH_FAILURE); default: if ((s = pam_strerror(pamh, *pam_status)) != NULL) @@ -231,6 +201,57 @@ sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_co } } +int +sudo_pam_approval(struct passwd *pw, sudo_auth *auth) +{ + const char *s; + int *pam_status = (int *) auth->data; + debug_decl(sudo_pam_approval, SUDOERS_DEBUG_AUTH) + + *pam_status = pam_acct_mgmt(pamh, PAM_SILENT); + switch (*pam_status) { + case PAM_SUCCESS: + debug_return_int(AUTH_SUCCESS); + case PAM_AUTH_ERR: + log_warningx(0, N_("account validation failure, " + "is your account locked?")); + debug_return_int(AUTH_FATAL); + case PAM_NEW_AUTHTOK_REQD: + log_warningx(0, N_("Account or password is " + "expired, reset your password and try again")); + *pam_status = pam_chauthtok(pamh, + PAM_CHANGE_EXPIRED_AUTHTOK); + if (*pam_status == PAM_SUCCESS) + debug_return_int(AUTH_SUCCESS); + if ((s = pam_strerror(pamh, *pam_status)) == NULL) + s = "unknown error"; + log_warningx(0, + N_("unable to change expired password: %s"), s); + debug_return_int(AUTH_FAILURE); + case PAM_AUTHTOK_EXPIRED: + log_warningx(0, + N_("Password expired, contact your system administrator")); + debug_return_int(AUTH_FATAL); + case PAM_ACCT_EXPIRED: + log_warningx(0, + N_("Account expired or PAM config lacks an \"account\" " + "section for sudo, contact your system administrator")); + debug_return_int(AUTH_FATAL); + case PAM_AUTHINFO_UNAVAIL: + case PAM_MAXTRIES: + case PAM_PERM_DENIED: + s = pam_strerror(pamh, *pam_status); + log_warningx(0, N_("PAM account management error: %s"), + s ? s : "unknown error"); + debug_return_int(AUTH_FAILURE); + default: + s = pam_strerror(pamh, *pam_status); + log_warningx(0, N_("PAM account management error: %s"), + s ? s : "unknown error"); + debug_return_int(AUTH_FATAL); + } +} + int sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth) { diff --git a/plugins/sudoers/auth/sudo_auth.c b/plugins/sudoers/auth/sudo_auth.c index dc2bd5e3c..76569ee91 100644 --- a/plugins/sudoers/auth/sudo_auth.c +++ b/plugins/sudoers/auth/sudo_auth.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005, 2008-2016 Todd C. Miller + * Copyright (c) 1999-2005, 2008-2018 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,47 +41,47 @@ static sudo_auth auth_switch[] = { /* Standalone entries first */ #ifdef HAVE_AIXAUTH - AUTH_ENTRY("aixauth", FLAG_STANDALONE, sudo_aix_init, NULL, sudo_aix_verify, sudo_aix_cleanup, NULL, NULL) + AUTH_ENTRY("aixauth", FLAG_STANDALONE, sudo_aix_init, NULL, sudo_aix_verify, NULL, sudo_aix_cleanup, NULL, NULL) #endif #ifdef HAVE_PAM - AUTH_ENTRY("pam", FLAG_STANDALONE, sudo_pam_init, NULL, sudo_pam_verify, sudo_pam_cleanup, sudo_pam_begin_session, sudo_pam_end_session) + AUTH_ENTRY("pam", FLAG_STANDALONE, sudo_pam_init, NULL, sudo_pam_verify, sudo_pam_approval, sudo_pam_cleanup, sudo_pam_begin_session, sudo_pam_end_session) #endif #ifdef HAVE_SECURID - AUTH_ENTRY("SecurId", FLAG_STANDALONE, sudo_securid_init, sudo_securid_setup, sudo_securid_verify, NULL, NULL, NULL) + AUTH_ENTRY("SecurId", FLAG_STANDALONE, sudo_securid_init, sudo_securid_setup, sudo_securid_verify, NULL, NULL, NULL, NULL) #endif #ifdef HAVE_SIA_SES_INIT - AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sudo_sia_setup, sudo_sia_verify, sudo_sia_cleanup, sudo_sia_begin_session, NULL) + AUTH_ENTRY("sia", FLAG_STANDALONE, NULL, sudo_sia_setup, sudo_sia_verify, NULL, sudo_sia_cleanup, sudo_sia_begin_session, NULL) #endif #ifdef HAVE_FWTK - AUTH_ENTRY("fwtk", FLAG_STANDALONE, sudo_fwtk_init, NULL, sudo_fwtk_verify, sudo_fwtk_cleanup, NULL, NULL) + AUTH_ENTRY("fwtk", FLAG_STANDALONE, sudo_fwtk_init, NULL, sudo_fwtk_verify, NULL, sudo_fwtk_cleanup, NULL, NULL) #endif #ifdef HAVE_BSD_AUTH_H - AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_cleanup, NULL, NULL) + AUTH_ENTRY("bsdauth", FLAG_STANDALONE, bsdauth_init, NULL, bsdauth_verify, bsdauth_approval, bsdauth_cleanup, NULL, NULL) #endif /* Non-standalone entries */ #ifndef WITHOUT_PASSWD - AUTH_ENTRY("passwd", 0, sudo_passwd_init, NULL, sudo_passwd_verify, sudo_passwd_cleanup, NULL, NULL) + AUTH_ENTRY("passwd", 0, sudo_passwd_init, NULL, sudo_passwd_verify, NULL, sudo_passwd_cleanup, NULL, NULL) #endif #if defined(HAVE_GETPRPWNAM) && !defined(WITHOUT_PASSWD) - AUTH_ENTRY("secureware", 0, sudo_secureware_init, NULL, sudo_secureware_verify, sudo_secureware_cleanup, NULL, NULL) + AUTH_ENTRY("secureware", 0, sudo_secureware_init, NULL, sudo_secureware_verify, NULL, sudo_secureware_cleanup, NULL, NULL) #endif #ifdef HAVE_AFS - AUTH_ENTRY("afs", 0, NULL, NULL, sudo_afs_verify, NULL, NULL, NULL) + AUTH_ENTRY("afs", 0, NULL, NULL, sudo_afs_verify, NULL, NULL, NULL, NULL) #endif #ifdef HAVE_DCE - AUTH_ENTRY("dce", 0, NULL, NULL, sudo_dce_verify, NULL, NULL, NULL) + AUTH_ENTRY("dce", 0, NULL, NULL, sudo_dce_verify, NULL, NULL, NULL, NULL) #endif #ifdef HAVE_KERB5 - AUTH_ENTRY("kerb5", 0, sudo_krb5_init, sudo_krb5_setup, sudo_krb5_verify, sudo_krb5_cleanup, NULL, NULL) + AUTH_ENTRY("kerb5", 0, sudo_krb5_init, sudo_krb5_setup, sudo_krb5_verify, NULL, sudo_krb5_cleanup, NULL, NULL) #endif #ifdef HAVE_SKEY - AUTH_ENTRY("S/Key", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL) + AUTH_ENTRY("S/Key", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL) #endif #ifdef HAVE_OPIE - AUTH_ENTRY("OPIE", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL) + AUTH_ENTRY("OPIE", 0, NULL, sudo_rfc1938_setup, sudo_rfc1938_verify, NULL, NULL, NULL, NULL) #endif - AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL) + AUTH_ENTRY(NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL) }; static bool standalone; @@ -158,6 +158,30 @@ sudo_auth_init(struct passwd *pw) debug_return_int(status == AUTH_FATAL ? -1 : 0); } +/* + * Cleanup all authentication approval methods. + * Returns true on success, false on failure and -1 on error. + */ +int +sudo_auth_approval(struct passwd *pw, int validated) +{ + sudo_auth *auth; + debug_decl(sudo_auth_approval, SUDOERS_DEBUG_AUTH) + + /* Call approval routines. */ + for (auth = auth_switch; auth->name; auth++) { + if (auth->approval && !IS_DISABLED(auth)) { + int status = (auth->approval)(pw, auth); + if (status != AUTH_SUCCESS) { + /* Assume error msg already printed. */ + log_auth_failure(validated, 0); + debug_return_int(status == AUTH_FAILURE ? false : -1); + } + } + } + debug_return_int(true); +} + /* * Cleanup all authentication methods. * Returns 0 on success and -1 on error. @@ -166,18 +190,19 @@ int sudo_auth_cleanup(struct passwd *pw) { sudo_auth *auth; - int status = AUTH_SUCCESS; debug_decl(sudo_auth_cleanup, SUDOERS_DEBUG_AUTH) /* Call cleanup routines. */ for (auth = auth_switch; auth->name; auth++) { if (auth->cleanup && !IS_DISABLED(auth)) { - status = (auth->cleanup)(pw, auth); - if (status == AUTH_FATAL) - break; /* assume error msg already printed */ + int status = (auth->cleanup)(pw, auth); + if (status == AUTH_FATAL) { + /* Assume error msg already printed. */ + debug_return_int(-1); + } } } - debug_return_int(status == AUTH_FATAL ? -1 : 0); + debug_return_int(0); } static void @@ -322,7 +347,7 @@ done: break; case AUTH_FATAL: default: - log_auth_failure(validated | FLAG_AUTH_ERROR, 0); + log_auth_failure(validated, 0); ret = -1; break; } @@ -338,17 +363,18 @@ int sudo_auth_begin_session(struct passwd *pw, char **user_env[]) { sudo_auth *auth; - int status = AUTH_SUCCESS; debug_decl(sudo_auth_begin_session, SUDOERS_DEBUG_AUTH) for (auth = auth_switch; auth->name; auth++) { if (auth->begin_session && !IS_DISABLED(auth)) { - status = (auth->begin_session)(pw, user_env, auth); - if (status != AUTH_SUCCESS) - break; /* assume error msg already printed */ + int status = (auth->begin_session)(pw, user_env, auth); + if (status != AUTH_SUCCESS) { + /* Assume error msg already printed. */ + debug_return_int(-1); + } } } - debug_return_int(status == AUTH_SUCCESS ? 1 : -1); + debug_return_int(1); } bool @@ -375,17 +401,19 @@ int sudo_auth_end_session(struct passwd *pw) { sudo_auth *auth; - int status = AUTH_SUCCESS; + int status; debug_decl(sudo_auth_end_session, SUDOERS_DEBUG_AUTH) for (auth = auth_switch; auth->name; auth++) { if (auth->end_session && !IS_DISABLED(auth)) { status = (auth->end_session)(pw, auth); - if (status == AUTH_FATAL) - break; /* assume error msg already printed */ + if (status == AUTH_FATAL) { + /* Assume error msg already printed. */ + debug_return_int(-1); + } } } - debug_return_int(status == AUTH_FATAL ? -1 : 1); + debug_return_int(1); } /* diff --git a/plugins/sudoers/auth/sudo_auth.h b/plugins/sudoers/auth/sudo_auth.h index abbaf614b..6e3128071 100644 --- a/plugins/sudoers/auth/sudo_auth.h +++ b/plugins/sudoers/auth/sudo_auth.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005, 2007-2015 Todd C. Miller + * Copyright (c) 1999-2005, 2007-2016, 2018 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -31,6 +31,7 @@ typedef struct sudo_auth { 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, struct sudo_conv_callback *callback); + int (*approval)(struct passwd *pw, struct sudo_auth *auth); int (*cleanup)(struct passwd *pw, struct sudo_auth *auth); int (*begin_session)(struct passwd *pw, char **user_env[], struct sudo_auth *auth); int (*end_session)(struct passwd *pw, struct sudo_auth *auth); @@ -56,6 +57,7 @@ extern sudo_conv_t sudo_conv; /* Prototypes for standalone methods */ int bsdauth_init(struct passwd *pw, sudo_auth *auth); int bsdauth_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback); +int bsdauth_approval(struct passwd *pw, sudo_auth *auth); int bsdauth_cleanup(struct passwd *pw, sudo_auth *auth); int sudo_aix_init(struct passwd *pw, sudo_auth *auth); int sudo_aix_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback); @@ -66,6 +68,7 @@ int sudo_fwtk_cleanup(struct passwd *pw, sudo_auth *auth); int sudo_pam_init(struct passwd *pw, sudo_auth *auth); int sudo_pam_init_quiet(struct passwd *pw, sudo_auth *auth); int sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth, struct sudo_conv_callback *callback); +int sudo_pam_approval(struct passwd *pw, sudo_auth *auth); int sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth); int sudo_pam_begin_session(struct passwd *pw, char **user_env[], sudo_auth *auth); int sudo_pam_end_session(struct passwd *pw, sudo_auth *auth); @@ -93,8 +96,8 @@ int sudo_secureware_init(struct passwd *pw, sudo_auth *auth); int sudo_secureware_verify(struct passwd *pw, char *pass, sudo_auth *auth, struct sudo_conv_callback *callback); int sudo_secureware_cleanup(struct passwd *pw, sudo_auth *auth); -/* 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) }, +/* Fields: name, flags, init, setup, verify, approval, cleanup, begin_sess, end_sess */ +#define AUTH_ENTRY(n, f, i, s, v, a, c, b, e) \ + { (f), AUTH_FAILURE, (n), NULL, (i), (s), (v), (a), (c) , (b), (e) }, #endif /* SUDO_AUTH_H */ diff --git a/plugins/sudoers/check.c b/plugins/sudoers/check.c index fa5998d45..8a68d4d04 100644 --- a/plugins/sudoers/check.c +++ b/plugins/sudoers/check.c @@ -219,6 +219,10 @@ check_user(int validated, int mode) ret = check_user_interactive(validated, mode, auth_pw); done: + if (ret == true) { + /* The approval function may disallow a user post-authentication. */ + ret = sudo_auth_approval(auth_pw, validated); + } sudo_auth_cleanup(auth_pw); sudo_pw_delref(auth_pw); diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 945d29b95..0825181f8 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -136,8 +136,7 @@ struct sudo_user { #define FLAG_NO_CHECK 0x080 #define FLAG_NON_INTERACTIVE 0x100 #define FLAG_BAD_PASSWORD 0x200 -#define FLAG_AUTH_ERROR 0x400 -#define FLAG_NOPASSWD 0x800 +#define FLAG_NOPASSWD 0x400 /* * find_path()/set_cmnd() return values @@ -265,6 +264,7 @@ int verify_user(struct passwd *pw, char *prompt, int validated, struct sudo_conv int sudo_auth_begin_session(struct passwd *pw, char **user_env[]); int sudo_auth_end_session(struct passwd *pw); int sudo_auth_init(struct passwd *pw); +int sudo_auth_approval(struct passwd *pw, int validated); int sudo_auth_cleanup(struct passwd *pw); /* set_perms.c */ -- 2.40.0