On Thu, Jul 24, 2014 at 05:29:50PM +0200, Tomas Mraz wrote:
Hi,
this patch modifies the audit records of libpam to add a grantor field.
This field records in case of the successful authentication (or any
other successful pam module call) that the module granted the access.
The PAM_SUCCESS return is recorded only for cases where the module
really contributed to the access being granted. That means that if the
action for success is different from ok and done, the module is not
recorded.
Please review, thanks,
Sorry for the long delay, see my comments below.
diff --git a/libpam/pam_account.c b/libpam/pam_account.c
index 572acc4..3a4fb1f 100644
--- a/libpam/pam_account.c
+++ b/libpam/pam_account.c
@@ -19,9 +19,5 @@ int pam_acct_mgmt(pam_handle_t *pamh, int flags)
retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT);
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_ACCOUNT, retval, flags);
-#endif
-
return retval;
}
diff --git a/libpam/pam_audit.c b/libpam/pam_audit.c
index 531746a..63a4ea5 100644
--- a/libpam/pam_audit.c
+++ b/libpam/pam_audit.c
First of all, libpam/pam_audit.c must include "pam_private.h" prior to all
other header files, otherwise some vital system header files are included
before config.h
@@ -28,14 +28,15 @@ _pam_audit_writelog(pam_handle_t *pamh, int
audit_fd, int type,
const char *message, int retval)
{
static int old_errno = -1;
- int rc;
- char buf[32];
-
- snprintf(buf, sizeof(buf), "PAM:%s", message);
-
- rc = audit_log_acct_message (audit_fd, type, NULL, buf,
- (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user :
"?",
- -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS );
+ int rc = -ENOMEM;
+ char *buf = NULL;
minor: no need to initialize buf before passing it to asprintf:
buf can be relied upon iff asprintf succeeded.
+
+ if (asprintf(&buf, "PAM:%s", message) >= 0) {
+ rc = audit_log_acct_message(audit_fd, type, NULL, buf,
+ (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?",
+ -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS);
+ free(buf);
+ }
/* libaudit sets errno to his own negative error code. This can be
an official errno number, but must not. It can also be a audit
@@ -78,12 +79,65 @@ _pam_audit_open(pam_handle_t *pamh)
return audit_fd;
}
+static char *
+_pam_list_grantors(struct handler *hlist, const char *message, int retval)
+{
+ char *buf;
+ char *list = NULL;
+
+ if (retval == PAM_SUCCESS) {
+ struct handler *h;
+ char *p = NULL;
+ size_t len = 0;
+
+ h = hlist;
+
+ while (h != NULL) {
+ if (h->grantor) {
+ len += strlen(h->mod_name) + 1;
+ }
+
+ h = h->next;
+ }
minor: this is exactly the case where a "for" expression is more suitable.
major: please check for len == 0, otherwise you'll sooner or later end up
with malloc(0) with subsequent garbage passed to asprintf. All you need
to reproduce this is an almost empty auth stack like this
auth [success=1 default=ignore] pam_permit.so
auth required pam_permit.so
and an application that calls pam_setcred without pam_authenticate
(as sshd often does).
+
+ list = malloc(len);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ h = hlist;
+
+ while (h != NULL) {
+ if (h->grantor) {
+ if (p == NULL) {
+ p = list;
+ } else {
+ p = stpcpy(p, ",");
+ }
+
+ p = stpcpy(p, h->mod_name);
+ }
+
+ h = h->next;
+ }
minor: this is another case where a "for" expression would be appropriate.
+ }
+
+ if (asprintf(&buf, "%s grantor=%s", message, list ? list :
"?") < 0) {
+ free(list);
+ return NULL;
+ }
+
+ free(list);
+ return buf;
+}
+
int
-_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags)
+_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h)
{
const char *message;
int type;
int audit_fd;
+ char *buf = NULL;
if ((audit_fd=_pam_audit_open(pamh)) == -1) {
return PAM_SYSTEM_ERR;
@@ -134,9 +188,18 @@ _pam_auditlog(pam_handle_t *pamh, int action, int retval, int
flags)
retval = PAM_SYSTEM_ERR;
}
- if (_pam_audit_writelog(pamh, audit_fd, type, message, retval) < 0)
+ buf = _pam_list_grantors(h, message, retval);
+ if (buf == NULL) {
+ /* allocation failure */
+ pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m");
+ retval = PAM_SYSTEM_ERR;
+ }
Since there are no grantors when retval != PAM_SUCCESS, we can make
_pam_list_grantors simpler and save on extra asprintf/free by calling
_pam_list_grantors only if retval == PAM_SUCCESS.
No need to print "grantor=?" when there are no grantors.
+
+ if (_pam_audit_writelog(pamh, audit_fd, type, buf ? buf : message, retval) < 0)
retval = PAM_SYSTEM_ERR;
+ free(buf);
+
audit_close(audit_fd);
return retval;
}
@@ -149,7 +212,7 @@ _pam_audit_end(pam_handle_t *pamh, int status UNUSED)
* stacks having been run. Assume that this is sshd faking
* things for an unknown user.
*/
- _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0);
+ _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0, NULL);
}
return 0;
diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c
index 5984fa5..1e7bc6e 100644
--- a/libpam/pam_auth.c
+++ b/libpam/pam_auth.c
@@ -45,10 +45,6 @@ int pam_authenticate(pam_handle_t *pamh, int flags)
prelude_send_alert(pamh, retval);
#endif
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_AUTHENTICATE, retval, flags);
-#endif
-
return retval;
}
@@ -71,10 +67,6 @@ int pam_setcred(pam_handle_t *pamh, int flags)
retval = _pam_dispatch(pamh, flags, PAM_SETCRED);
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_SETCRED, retval, flags);
-#endif
-
D(("pam_setcred exit"));
return retval;
diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c
index eb52c82..ccfc372 100644
--- a/libpam/pam_dispatch.c
+++ b/libpam/pam_dispatch.c
@@ -217,8 +217,14 @@ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct
handler *h,
status = retval;
}
}
- if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
- goto decision_made;
+ if ( impression == _PAM_POSITIVE ) {
+ if ( retval == PAM_SUCCESS ) {
+ h->grantor = 1;
+ }
Technically, this is the only place where grantor has to be set,
but in practice there are many configs like
auth [success=1 default=ignore] pam_unix.so ...
auth required pam_something_else.so
In such cases, actual grantors like pam_unix will not be registered
because technically they are not grantors.
A workaround is to rewrite configs this way:
auth [success=ok default=1] pam_unix.so ...
auth [success=1] pam_permit.so
auth required pam_something_else.so
+
+ if ( action == _PAM_ACTION_DONE ) {
+ goto decision_made;
+ }
}
break;
@@ -308,6 +314,14 @@ decision_made: /* by getting here we have made a decision */
return status;
}
+static void _pam_clear_grantors(struct handler *h)
+{
+ while (h != NULL) {
+ h->grantor = 0;
+ h = h->next;
+ }
minor: this is another case where a "for" expression would be appropriate.
+}
+
/*
* This function translates the module dispatch request into a pointer
* to the stack of modules that will actually be run. the
@@ -318,21 +332,21 @@ decision_made: /* by getting here we have made a decision */
int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
{
struct handler *h = NULL;
- int retval, use_cached_chain;
+ int retval = PAM_SYSTEM_ERR, use_cached_chain;
_pam_boolean resumed;
IF_NO_PAMH("_pam_dispatch", pamh, PAM_SYSTEM_ERR);
if (__PAM_FROM_MODULE(pamh)) {
D(("called from a module!?"));
- return PAM_SYSTEM_ERR;
+ goto end;
}
/* Load all modules, resolve all symbols */
if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) {
pam_syslog(pamh, LOG_ERR, "unable to dispatch function");
- return retval;
+ goto end;
}
use_cached_chain = _PAM_PLEASE_FREEZE;
@@ -360,7 +374,8 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
break;
default:
pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice);
- return PAM_ABORT;
+ retval = PAM_ABORT;
+ goto end;
}
if (h == NULL) { /* there was no handlers.conf... entry; will use
@@ -393,11 +408,13 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
pam_syslog(pamh, LOG_ERR,
"application failed to re-exec stack [%d:%d]",
pamh->former.choice, choice);
- return PAM_ABORT;
+ retval = PAM_ABORT;
+ goto end;
}
resumed = PAM_TRUE;
} else {
resumed = PAM_FALSE;
+ _pam_clear_grantors(h);
}
__PAM_TO_MODULE(pamh);
@@ -417,5 +434,13 @@ int _pam_dispatch(pam_handle_t *pamh, int flags, int choice)
pamh->former.choice = PAM_NOT_STACKED;
}
+end:
+
+#ifdef HAVE_LIBAUDIT
+ if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval !=
PAM_SUCCESS) {
+ retval = _pam_auditlog(pamh, choice, retval, flags, h);
+ }
+#endif
+
return retval;
}
diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c
index 02714f7..e3f8ff6 100644
--- a/libpam/pam_handlers.c
+++ b/libpam/pam_handlers.c
@@ -889,6 +889,7 @@ int _pam_add_handler(pam_handle_t *pamh
(*handler_p)->argc = argc;
(*handler_p)->argv = argv; /* not a copy */
(*handler_p)->mod_name = extract_modulename(mod_path);
+ (*handler_p)->grantor = 0;
(*handler_p)->next = NULL;
/* some of the modules have a second calling function */
@@ -921,6 +922,7 @@ int _pam_add_handler(pam_handle_t *pamh
(*handler_p2)->argv = NULL; /* no arguments */
}
(*handler_p2)->mod_name = extract_modulename(mod_path);
+ (*handler_p2)->grantor = 0;
(*handler_p2)->next = NULL;
}
diff --git a/libpam/pam_password.c b/libpam/pam_password.c
index 75db5e5..592e01f 100644
--- a/libpam/pam_password.c
+++ b/libpam/pam_password.c
@@ -57,9 +57,5 @@ int pam_chauthtok(pam_handle_t *pamh, int flags)
D(("will resume when ready", retval));
}
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_CHAUTHTOK, retval, flags);
-#endif
-
return retval;
}
diff --git a/libpam/pam_private.h b/libpam/pam_private.h
index 134dc72..d93283c 100644
--- a/libpam/pam_private.h
+++ b/libpam/pam_private.h
@@ -55,6 +55,7 @@ struct handler {
struct handler *next;
char *mod_name;
int stack_level;
+ int grantor;
};
#define PAM_HT_MODULE 0
@@ -316,7 +317,7 @@ if ((pamh) == NULL) { \
do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0)
#ifdef HAVE_LIBAUDIT
-extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags);
+extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct
handler *h);
extern int _pam_audit_end(pam_handle_t *pamh, int pam_status);
#endif
diff --git a/libpam/pam_session.c b/libpam/pam_session.c
index 512153f..cb393c1 100644
--- a/libpam/pam_session.c
+++ b/libpam/pam_session.c
@@ -22,9 +22,6 @@ int pam_open_session(pam_handle_t *pamh, int flags)
}
retval = _pam_dispatch(pamh, flags, PAM_OPEN_SESSION);
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_OPEN_SESSION, retval, flags);
-#endif
return retval;
}
@@ -43,10 +40,6 @@ int pam_close_session(pam_handle_t *pamh, int flags)
retval = _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION);
-#ifdef HAVE_LIBAUDIT
- retval = _pam_auditlog(pamh, PAM_CLOSE_SESSION, retval, flags);
-#endif
-
return retval;
}
--
ldv