summaryrefslogtreecommitdiffstats
path: root/libpam/pam_audit.c
blob: 97a9a92959288f386f7963f4492779419f1ab2cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* pam_audit.c -- Instrumentation code for Linux Auditing System  */

/* (C) 2005-2006 Red Hat, Inc. -- Licensing details are in the COPYING
   file accompanying the Linux-PAM source distribution.

   Authors:
   Steve Grubb <sgrubb@redhat.com> */

#include "pam_private.h"
#include "pam_modutil_private.h"

#ifdef HAVE_LIBAUDIT
#include <stdio.h>
#include <syslog.h>
#include <libaudit.h>
#include <pwd.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

#define PAMAUDIT_LOGGED 1

static int
_pam_audit_writelog(pam_handle_t *pamh, int audit_fd, int type,
	const char *message, const char *grantors, int retval)
{
  static int old_errno = -1;
  int rc = -ENOMEM;
  char *buf;
  const char *grantors_field = " grantors=";

  if (grantors == NULL) {
      grantors = "";
      grantors_field = "";
  }

  if (asprintf(&buf, "PAM:%s%s%s", message, grantors_field, grantors) >= 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
     internal error code. Which makes errno useless :-((. Try the
     best to fix it. */
  errno = -rc;

  pamh->audit_state |= PAMAUDIT_LOGGED;

  if (rc < 0) {
      if (rc == -EPERM)
          return 0;
      if (errno != old_errno) {
          old_errno = errno;
          pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m");
      }
  }
  return rc;
}

static int
_pam_audit_open(pam_handle_t *pamh)
{
  int audit_fd;
  audit_fd = audit_open();
  if (audit_fd < 0) {
    /* You get these error codes only when the kernel doesn't have
     * audit compiled in. */
    if (errno == EINVAL || errno == EPROTONOSUPPORT ||
        errno == EAFNOSUPPORT)
        return -2;

    /* this should only fail in case of extreme resource shortage,
     * need to prevent login in that case for CAPP compliance.
     */
    pam_syslog(pamh, LOG_CRIT, "audit_open() failed: %m");
    return -1;
  }

  return audit_fd;
}

static int
_pam_list_grantors(struct handler *hlist, int retval, char **list)
{
  *list = NULL;

  if (retval == PAM_SUCCESS) {
    struct handler *h;
    char *p = NULL;
    size_t len = 0;

    for (h = hlist; h != NULL; h = h->next) {
      if (h->grantor) {
        len += strlen(h->mod_name) + 1;
      }
    }

    if (len == 0) {
      return 0;
    }

    *list = malloc(len);
    if (*list == NULL) {
      return -1;
    }

    for (h = hlist; h != NULL; h = h->next) {
      if (h->grantor) {
        if (p == NULL) {
          p = *list;
        } else {
          p = stpcpy(p, ",");
        }

        p = stpcpy(p, h->mod_name);
      }
    }
  }

  return 0;
}

int
_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h)
{
  const char *message;
  int type;
  int audit_fd;
  char *grantors;

  if ((audit_fd=_pam_audit_open(pamh)) == -1) {
    return PAM_SYSTEM_ERR;
  } else if (audit_fd == -2) {
    return retval;
  }

  switch (action) {
  case PAM_AUTHENTICATE:
    message = "authentication";
    type = AUDIT_USER_AUTH;
    break;
  case PAM_OPEN_SESSION:
    message = "session_open";
    type = AUDIT_USER_START;
    break;
  case PAM_CLOSE_SESSION:
    message = "session_close";
    type = AUDIT_USER_END;
    break;
  case PAM_ACCOUNT:
    message = "accounting";
    type = AUDIT_USER_ACCT;
    break;
  case PAM_CHAUTHTOK:
    message = "chauthtok";
    type = AUDIT_USER_CHAUTHTOK;
    break;
  case PAM_SETCRED:
    message = "setcred";
    if (flags & PAM_ESTABLISH_CRED)
	type = AUDIT_CRED_ACQ;
    else if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED))
	type = AUDIT_CRED_REFR;
    else if (flags & PAM_DELETE_CRED)
	type = AUDIT_CRED_DISP;
    else
        type = AUDIT_USER_ERR;
    break;
  case _PAM_ACTION_DONE:
    message = "bad_ident";
    type = AUDIT_USER_ERR;
    break;
  default:
    message = "UNKNOWN";
    type = AUDIT_USER_ERR;
    pam_syslog(pamh, LOG_CRIT, "_pam_auditlog() should never get here");
    retval = PAM_SYSTEM_ERR;
  }

  if (_pam_list_grantors(h, retval, &grantors) < 0) {
    /* allocation failure */
    pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m");
    retval = PAM_SYSTEM_ERR;
  }

  if (_pam_audit_writelog(pamh, audit_fd, type, message,
      grantors ? grantors : "?", retval) < 0)
    retval = PAM_SYSTEM_ERR;

  free(grantors);

  audit_close(audit_fd);
  return retval;
}

int
_pam_audit_end(pam_handle_t *pamh, int status UNUSED)
{
  if (! (pamh->audit_state & PAMAUDIT_LOGGED)) {
    /* PAM library is being shut down without any of the auditted
     * 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, NULL);
  }

  return 0;
}

int
pam_modutil_audit_write(pam_handle_t *pamh, int type,
    const char *message, int retval)
{
  int audit_fd;
  int rc;

  if ((audit_fd=_pam_audit_open(pamh)) == -1) {
    return PAM_SYSTEM_ERR;
  } else if (audit_fd == -2) {
    return retval;
  }

  rc = _pam_audit_writelog(pamh, audit_fd, type, message, NULL, retval);

  audit_close(audit_fd);

  return rc < 0 ? PAM_SYSTEM_ERR : PAM_SUCCESS;
}

#else
int pam_modutil_audit_write(pam_handle_t *pamh UNUSED, int type UNUSED,
    const char *message UNUSED, int retval UNUSED)
{
  return PAM_SUCCESS;
}
#endif /* HAVE_LIBAUDIT */