Imported Upstream version 2.4.3
[platform/upstream/audit.git] / lib / netlink.c
1 /* netlink.c --
2  * Copyright 2004, 2005, 2009, 2013 Red Hat Inc., Durham, North Carolina.
3  * All Rights Reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Authors:
20  *      Steve Grubb <sgrubb@redhat.com>
21  *      Rickard E. (Rik) Faith <faith@redhat.com>
22  */
23
24 #include "config.h"
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <time.h>
30 #include <sys/poll.h>
31 #include "libaudit.h"
32 #include "private.h"
33
34 #ifndef NETLINK_AUDIT
35 #define NETLINK_AUDIT 9
36 #endif
37
38 static int adjust_reply(struct audit_reply *rep, int len);
39 static int check_ack(int fd, int seq);
40
41 /*
42  * This function opens a connection to the kernel's audit
43  * subsystem. You must be root for the call to succeed. On error,
44  * a negative value is returned. On success, the file descriptor is
45  * returned - which can be 0 or higher.
46  */
47 int audit_open(void)
48 {
49         int saved_errno;
50         int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
51
52         if (fd < 0) {
53                 saved_errno = errno;
54                 if (errno == EINVAL || errno == EPROTONOSUPPORT ||
55                                 errno == EAFNOSUPPORT)
56                         audit_msg(LOG_ERR,
57                                 "Error - audit support not in kernel"); 
58                 else
59                         audit_msg(LOG_ERR,
60                                 "Error opening audit netlink socket (%s)", 
61                                 strerror(errno));
62                 errno = saved_errno;
63                 return fd;
64         }
65         if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
66                 saved_errno = errno;
67                 close(fd);
68                 audit_msg(LOG_ERR, 
69                         "Error setting audit netlink socket CLOEXEC flag (%s)", 
70                         strerror(errno));
71                 errno = saved_errno;
72                 return -1;
73         }
74         return fd;
75 }
76
77
78 void audit_close(int fd)
79 {
80         if (fd >= 0)
81                 close(fd);
82 }
83
84
85 /*
86  * This function returns -1 on error, 0 if error response received,
87  * and > 0 if packet OK.
88  */
89 int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek)
90 {
91         int len;
92         struct sockaddr_nl nladdr;
93         socklen_t nladdrlen = sizeof(nladdr);
94
95         if (fd < 0)
96                 return -EBADF;
97
98         if (block == GET_REPLY_NONBLOCKING)
99                 block = MSG_DONTWAIT;
100
101 retry:
102         len = recvfrom(fd, &rep->msg, sizeof(rep->msg), block|peek,
103                 (struct sockaddr*)&nladdr, &nladdrlen);
104
105         if (len < 0) {
106                 if (errno == EINTR)
107                         goto retry;
108                 if (errno != EAGAIN) {
109                         int saved_errno = errno;
110                         audit_msg(LOG_ERR, 
111                                 "Error receiving audit netlink packet (%s)", 
112                                 strerror(errno));
113                         errno = saved_errno;
114                 }
115                 return -errno;
116         }
117         if (nladdrlen != sizeof(nladdr)) {
118                 audit_msg(LOG_ERR, 
119                         "Bad address size reading audit netlink socket");
120                 return -EPROTO;
121         }
122         if (nladdr.nl_pid) {
123                 audit_msg(LOG_ERR, 
124                         "Spoofed packet received on audit netlink socket");
125                 return -EINVAL;
126         }
127
128         len = adjust_reply(rep, len);
129         if (len == 0)
130                 len = -errno;
131         return len; 
132 }
133 hidden_def(audit_get_reply)
134
135
136 /* 
137  * This function returns 0 on error and len on success.
138  */
139 static int adjust_reply(struct audit_reply *rep, int len)
140 {
141         rep->type     = rep->msg.nlh.nlmsg_type;
142         rep->len      = rep->msg.nlh.nlmsg_len;
143         rep->nlh      = &rep->msg.nlh;
144         rep->status   = NULL;
145         rep->ruledata = NULL;
146         rep->login    = NULL;
147         rep->message  = NULL;
148         rep->error    = NULL;
149         rep->signal_info = NULL;
150         rep->conf     = NULL;
151 #if HAVE_DECL_AUDIT_FEATURE_VERSION
152         rep->features = NULL;
153 #endif
154         if (!NLMSG_OK(rep->nlh, (unsigned int)len)) {
155                 if (len == sizeof(rep->msg)) {
156                         audit_msg(LOG_ERR, 
157                                 "Netlink event from kernel is too big");
158                         errno = EFBIG;
159                 } else {
160                         audit_msg(LOG_ERR, 
161                         "Netlink message from kernel was not OK");
162                         errno = EBADE;
163                 }
164                 return 0;
165         }
166
167         /* Next we'll set the data structure to point to msg.data. This is
168          * to avoid having to use casts later. */
169         switch (rep->type) {
170                 case NLMSG_ERROR: 
171                         rep->error   = NLMSG_DATA(rep->nlh); 
172                         break;
173                 case AUDIT_GET:   
174                         rep->status  = NLMSG_DATA(rep->nlh); 
175                         break;
176 #if HAVE_DECL_AUDIT_FEATURE_VERSION
177                 case AUDIT_GET_FEATURE:
178                         rep->features =  NLMSG_DATA(rep->nlh);
179                         break;
180 #endif
181                 case AUDIT_LIST_RULES:  
182                         rep->ruledata = NLMSG_DATA(rep->nlh); 
183                         break;
184                 case AUDIT_USER:
185                 case AUDIT_LOGIN:
186                 case AUDIT_KERNEL:
187                 case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
188                 case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2:
189                 case AUDIT_FIRST_EVENT...AUDIT_INTEGRITY_LAST_MSG:
190                         rep->message = NLMSG_DATA(rep->nlh);
191                         break;
192                 case AUDIT_SIGNAL_INFO:
193                         rep->signal_info = NLMSG_DATA(rep->nlh);
194                         break;
195         }
196         return len;
197 }
198
199
200 /*
201  *  Return values:   success: positive non-zero sequence number
202  *                   error:   -errno
203  *                   short:   0
204  */
205 int audit_send(int fd, int type, const void *data, unsigned int size)
206 {
207         static int sequence = 0;
208         struct audit_message req;
209         int retval;
210         struct sockaddr_nl addr;
211
212         /* Due to user space library callbacks, there's a chance that
213            a -1 for the fd could be passed. Just check for and handle it. */
214         if (fd < 0) {
215                 errno = EBADF;
216                 return -errno;
217         }
218
219         if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) {
220                 errno = EINVAL;
221                 return -errno;
222         }
223
224         if (++sequence < 0) 
225                 sequence = 1;
226
227         memset(&req, 0, sizeof(req));
228         req.nlh.nlmsg_len = NLMSG_SPACE(size);
229         req.nlh.nlmsg_type = type;
230         req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
231         req.nlh.nlmsg_seq = sequence;
232         if (size && data)
233                 memcpy(NLMSG_DATA(&req.nlh), data, size);
234         memset(&addr, 0, sizeof(addr));
235         addr.nl_family = AF_NETLINK;
236         addr.nl_pid = 0;
237         addr.nl_groups = 0;
238
239         do {
240                 retval = sendto(fd, &req, req.nlh.nlmsg_len, 0,
241                         (struct sockaddr*)&addr, sizeof(addr));
242         } while (retval < 0 && errno == EINTR);
243         if (retval == (int)req.nlh.nlmsg_len) {
244                 if ((retval = check_ack(fd, sequence)) == 0)
245                         return sequence;
246                 else
247                         return retval; 
248         }
249         if (retval < 0) 
250                 return -errno;
251
252         return 0;
253 }
254 hidden_def(audit_send)
255
256 /*
257  * This function will take a peek into the next packet and see if there's
258  * an error. If so, the error is returned and its non-zero. Otherwise a 
259  * zero is returned indicating that we don't know of any problems.
260  */
261 static int check_ack(int fd, int seq)
262 {
263         int rc, retries = 80;
264         struct audit_reply rep;
265         struct pollfd pfd[1];
266
267 retry:
268         pfd[0].fd = fd;
269         pfd[0].events = POLLIN;
270         do {
271                 rc = poll(pfd, 1, 500); /* .5 second */
272         } while (rc < 0 && errno == EINTR);
273
274         /* We don't look at rc from above as it doesn't matter. We are 
275          * going to try to read nonblocking just in case packet shows up. */
276         
277         /* NOTE: whatever is returned is treated as the errno */
278         rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, MSG_PEEK);
279         if (rc == -EAGAIN && retries) {
280                 retries--;
281                 goto retry;
282         } else if (rc < 0)
283                 return rc;
284         else if (rc == 0)
285                 return -EINVAL; /* This can't happen anymore */
286         else if (rc > 0 && rep.type == NLMSG_ERROR) {
287                 int error = rep.error->error;
288                 /* Eat the message */
289                 (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
290
291                 /* NLMSG_ERROR can indicate success, only report nonzero */ 
292                 if (error) {
293                         errno = -error;
294                         return error;
295                 }
296         }
297         return 0;
298 }
299