1 /* pam_audit.c -- Instrumentation code for Linux Auditing System */
2
3 /* (C) 2005-2006 Red Hat, Inc. -- Licensing details are in the COPYING
4 file accompanying the Linux-PAM source distribution.
5
6 Authors:
7 Steve Grubb <sgrubb@redhat.com> */
8
9 #include "pam_private.h"
10 #include "pam_modutil_private.h"
11
12 #ifdef HAVE_LIBAUDIT
13 #include <stdio.h>
14 #include <syslog.h>
15 #include <libaudit.h>
16 #include <pwd.h>
17 #include <netdb.h>
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <arpa/inet.h>
22 #include <errno.h>
23
24 #define PAMAUDIT_LOGGED 1
25
26 static int
27 _pam_audit_writelog(pam_handle_t *pamh, int audit_fd, int type,
28 const char *message, const char *grantors, int retval)
29 {
30 static int old_errno = -1;
31 int rc = -ENOMEM;
32 char *buf;
33 const char *grantors_field = " grantors=";
34
35 if (grantors == NULL) {
36 grantors = "";
37 grantors_field = "";
38 }
39
40 if (asprintf(&buf, "PAM:%s%s%s", message, grantors_field, grantors) >= 0) {
41 rc = audit_log_acct_message(audit_fd, type, NULL, buf,
42 (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?",
43 -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS);
44 free(buf);
45 }
46
47 /* libaudit sets errno to his own negative error code. This can be
48 an official errno number, but must not. It can also be a audit
49 internal error code. Which makes errno useless :-((. Try the
50 best to fix it. */
51 errno = -rc;
52
53 pamh->audit_state |= PAMAUDIT_LOGGED;
54
55 if (rc < 0) {
56 if (rc == -EPERM)
57 return 0;
58 if (errno != old_errno) {
59 old_errno = errno;
60 pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m");
61 }
62 }
63 return rc;
64 }
65
66 static int
67 _pam_audit_open(pam_handle_t *pamh)
68 {
69 int audit_fd;
70 audit_fd = audit_open();
71 if (audit_fd < 0) {
72 /* You get these error codes only when the kernel doesn't have
73 * audit compiled in. */
74 if (errno == EINVAL || errno == EPROTONOSUPPORT ||
75 errno == EAFNOSUPPORT)
76 return -2;
77
78 /* this should only fail in case of extreme resource shortage,
79 * need to prevent login in that case for CAPP compliance.
80 */
81 pam_syslog(pamh, LOG_CRIT, "audit_open() failed: %m");
82 return -1;
83 }
84
85 return audit_fd;
86 }
87
88 static int
89 _pam_list_grantors(struct handler *hlist, int retval, char **list)
90 {
91 *list = NULL;
92
93 if (retval == PAM_SUCCESS) {
94 struct handler *h;
95 char *p = NULL;
96 size_t len = 0;
97
98 for (h = hlist; h != NULL; h = h->next) {
99 if (h->grantor) {
100 len += strlen(h->mod_name) + 1;
101 }
102 }
103
104 if (len == 0) {
105 return 0;
106 }
107
108 *list = malloc(len);
109 if (*list == NULL) {
110 return -1;
111 }
112
113 for (h = hlist; h != NULL; h = h->next) {
114 if (h->grantor) {
115 if (p == NULL) {
116 p = *list;
117 } else {
118 p = stpcpy(p, ",");
119 }
120
121 p = stpcpy(p, h->mod_name);
122 }
123 }
124 }
125
126 return 0;
127 }
128
129 int
130 _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h)
131 {
132 const char *message;
133 int type;
134 int audit_fd;
135 char *grantors;
136
137 if ((audit_fd=_pam_audit_open(pamh)) == -1) {
138 return PAM_SYSTEM_ERR;
139 } else if (audit_fd == -2) {
140 return retval;
141 }
142
143 switch (action) {
144 case PAM_AUTHENTICATE:
145 message = "authentication";
146 type = AUDIT_USER_AUTH;
147 break;
148 case PAM_OPEN_SESSION:
149 message = "session_open";
150 type = AUDIT_USER_START;
151 break;
152 case PAM_CLOSE_SESSION:
153 message = "session_close";
154 type = AUDIT_USER_END;
155 break;
156 case PAM_ACCOUNT:
157 message = "accounting";
158 type = AUDIT_USER_ACCT;
159 break;
160 case PAM_CHAUTHTOK:
161 message = "chauthtok";
162 type = AUDIT_USER_CHAUTHTOK;
163 break;
164 case PAM_SETCRED:
165 message = "setcred";
166 if (flags & PAM_ESTABLISH_CRED)
167 type = AUDIT_CRED_ACQ;
168 else if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED))
169 type = AUDIT_CRED_REFR;
170 else if (flags & PAM_DELETE_CRED)
171 type = AUDIT_CRED_DISP;
172 else
173 type = AUDIT_USER_ERR;
174 break;
175 case _PAM_ACTION_DONE:
176 message = "bad_ident";
177 type = AUDIT_USER_ERR;
178 break;
179 default:
180 message = "UNKNOWN";
181 type = AUDIT_USER_ERR;
182 pam_syslog(pamh, LOG_CRIT, "_pam_auditlog() should never get here");
183 retval = PAM_SYSTEM_ERR;
184 }
185
186 if (_pam_list_grantors(h, retval, &grantors) < 0) {
187 /* allocation failure */
188 pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m");
189 retval = PAM_SYSTEM_ERR;
190 }
191
192 if (_pam_audit_writelog(pamh, audit_fd, type, message,
193 grantors ? grantors : "?", retval) < 0)
194 retval = PAM_SYSTEM_ERR;
195
196 free(grantors);
197
198 audit_close(audit_fd);
199 return retval;
200 }
201
202 int
203 _pam_audit_end(pam_handle_t *pamh, int status UNUSED)
204 {
205 if (! (pamh->audit_state & PAMAUDIT_LOGGED)) {
206 /* PAM library is being shut down without any of the auditted
207 * stacks having been run. Assume that this is sshd faking
208 * things for an unknown user.
209 */
210 _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0, NULL);
211 }
212
213 return 0;
214 }
215
216 int
217 pam_modutil_audit_write(pam_handle_t *pamh, int type,
218 const char *message, int retval)
219 {
220 int audit_fd;
221 int rc;
222
223 if ((audit_fd=_pam_audit_open(pamh)) == -1) {
224 return PAM_SYSTEM_ERR;
225 } else if (audit_fd == -2) {
226 return retval;
227 }
228
229 rc = _pam_audit_writelog(pamh, audit_fd, type, message, NULL, retval);
230
231 audit_close(audit_fd);
232
233 return rc < 0 ? PAM_SYSTEM_ERR : PAM_SUCCESS;
234 }
235
236 #else
237 int pam_modutil_audit_write(pam_handle_t *pamh UNUSED, int type UNUSED,
238 const char *message UNUSED, int retval UNUSED)
239 {
240 return PAM_SUCCESS;
241 }
242 #endif /* HAVE_LIBAUDIT */