2 * Copyright 2005-2008,2010,2011,2013 Red Hat Inc., Durham, North Carolina.
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.
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.
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
20 * Steve Grubb <sgrubb@redhat.com>
30 #include <netinet/in.h> // inet6 addrlen
31 #include <netdb.h> // gethostbyname
32 #include <arpa/inet.h> // inet_ntop
34 #include <limits.h> // PATH_MAX
40 #define MAX_USER (UT_NAMESIZE * 2) + 8
42 // NOTE: The kernel fills in pid, uid, and loginuid of sender. Therefore,
43 // these routines do not need to send them.
46 * resolve's the hostname - caller must pass a INET6_ADDRSTRLEN byte buffer
47 * Returns string w/ numerical address, or "?" on failure
49 static void _resolve_addr(char buf[], const char *host)
52 struct addrinfo hints;
57 /* Short circuit this lookup if NULL, or empty */
58 if (host == NULL || *host == 0)
61 memset(&hints, 0, sizeof(hints));
62 hints.ai_flags = AI_ADDRCONFIG;
63 hints.ai_socktype = SOCK_STREAM;
65 e = getaddrinfo(host, NULL, &hints, &ai);
68 "resolve_addr: cannot resolve hostname %s (%s)",
69 host, gai_strerror(e));
72 // What to do if more than 1 addr?
73 inet_ntop(ai->ai_family, ai->ai_family == AF_INET ?
74 (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr :
75 (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
76 buf, INET6_ADDRSTRLEN);
81 * This function checks a string to see if it needs encoding. It
82 * return 1 if needed and 0 if not
84 int audit_value_needs_encoding(const char *str, unsigned int size)
91 for (i=0; i<size; i++) {
92 // we don't test for > 0x7f because str[] is signed.
93 if (str[i] == '"' || str[i] < 0x21 || str[i] == 0x7F)
100 * This function does encoding of "untrusted" names just like the kernel
102 char *audit_encode_value(char *final, const char *buf, unsigned int size)
106 const char *hex = "0123456789ABCDEF";
116 for (i=0; i<size; i++) {
117 *ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */
118 *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */
124 char *audit_encode_nv_string(const char *name, const char *value,
129 if (vlen == 0 && value)
130 vlen = strlen(value);
132 if (value && audit_value_needs_encoding(value, vlen)) {
133 char *tmp = malloc(2*vlen + 1);
135 audit_encode_value(tmp, value, vlen);
136 if (asprintf(&str, "%s=%s", name, tmp) < 0)
142 if (asprintf(&str, "%s=\"%s\"", name, value ? value : "?") < 0)
148 * Get the executable's name
150 static char *_get_exename(char *exename, int size)
153 char tmp[PATH_MAX+1];
155 /* get the name of the current executable */
156 if ((res = readlink("/proc/self/exe", tmp, PATH_MAX)) == -1) {
157 strcpy(exename, "\"?\"");
158 audit_msg(LOG_ERR, "get_exename: cannot determine executable");
161 if (audit_value_needs_encoding(tmp, res))
162 return audit_encode_value(exename, tmp, res);
163 snprintf(exename, size, "\"%s\"", tmp);
169 * Get the command line name
170 * NOTE: at the moment, this only escapes what the user sent
172 static char *_get_commname(const char *comm, char *commname, unsigned int size)
177 strcpy(commname, "\"?\"");
182 if (audit_value_needs_encoding(comm, len))
183 audit_encode_value(commname, comm, len);
185 snprintf(commname, size, "\"%s\"", comm);
190 static int check_ttyname(const char *ttyn)
194 if (lstat(ttyn, &statbuf)
195 || !S_ISCHR(statbuf.st_mode)
196 || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))) {
197 audit_msg(LOG_ERR, "FATAL: bad tty %s", ttyn);
203 static const char *_get_tty(char *tname, int size)
205 int rc, i, found = 0;
207 for (i=0; i<3 && !found; i++) {
208 rc = ttyname_r(i, tname, size);
209 if (rc == 0 && tname[0] != '\0')
216 if (check_ttyname(tname))
219 if (strncmp(tname, "/dev/", 5) == 0)
226 * This function will log a message to the audit system using a predefined
227 * message format. This function should be used by all console apps that do
228 * not manipulate accounts or groups.
230 * audit_fd - The fd returned by audit_open
231 * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN
232 * message - the message being sent
233 * hostname - the hostname if known
234 * addr - The network address of the user
235 * tty - The tty of the user
236 * result - 1 is "success" and 0 is "failed"
238 * It returns the sequence number which is > 0 on success or <= 0 on error.
240 int audit_log_user_message(int audit_fd, int type, const char *message,
241 const char *hostname, const char *addr, const char *tty, int result)
243 char buf[MAX_AUDIT_MESSAGE_LENGTH];
244 char addrbuf[INET6_ADDRSTRLEN];
245 static char exename[PATH_MAX*2]="";
246 char ttyname[TTY_PATH];
258 /* If hostname is empty string, make it NULL ptr */
259 if (hostname && *hostname == 0)
262 if (addr == NULL || strlen(addr) == 0)
263 _resolve_addr(addrbuf, hostname);
265 strncat(addrbuf, addr, sizeof(addrbuf)-1);
268 _get_exename(exename, sizeof(exename));
270 tty = _get_tty(ttyname, TTY_PATH);
274 snprintf(buf, sizeof(buf),
275 "%s exe=%s hostname=%s addr=%s terminal=%s res=%s",
277 hostname ? hostname : "?",
284 ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf );
285 if ((ret < 1) && errno == 0)
291 * This function will log a message to the audit system using a predefined
292 * message format. This function should be used by all console apps that do
293 * not manipulate accounts or groups and are executing a script. An example
294 * would be python or crond wanting to say what they are executing.
296 * audit_fd - The fd returned by audit_open
297 * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN
298 * message - the message being sent
299 * comm - the program command line name
300 * hostname - the hostname if known
301 * addr - The network address of the user
302 * tty - The tty of the user
303 * result - 1 is "success" and 0 is "failed"
305 * It returns the sequence number which is > 0 on success or <= 0 on error.
307 int audit_log_user_comm_message(int audit_fd, int type, const char *message,
308 const char *comm, const char *hostname, const char *addr,
309 const char *tty, int result)
311 char buf[MAX_AUDIT_MESSAGE_LENGTH];
312 char addrbuf[INET6_ADDRSTRLEN];
313 static char exename[PATH_MAX*2]="";
314 char commname[PATH_MAX*2];
315 char ttyname[TTY_PATH];
327 /* If hostname is empty string, make it NULL ptr */
328 if (hostname && *hostname == 0)
331 if (addr == NULL || strlen(addr) == 0)
332 _resolve_addr(addrbuf, hostname);
334 strncat(addrbuf, addr, sizeof(addrbuf)-1);
336 _get_exename(exename, sizeof(exename));
338 tty = _get_tty(ttyname, TTY_PATH);
342 _get_commname(comm, commname, sizeof(commname));
344 snprintf(buf, sizeof(buf),
345 "%s comm=%s exe=%s hostname=%s addr=%s terminal=%s res=%s",
346 message, commname, exename,
347 hostname ? hostname : "?",
354 ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf );
355 if ((ret < 1) && errno == 0)
362 * This function will log a message to the audit system using a predefined
363 * message format. It should be used for all account manipulation operations.
364 * Parameter usage is as follows:
366 * audit_fd - The fd returned by audit_open
367 * type - type of message: AUDIT_USER_CHAUTHTOK for changing any account
369 * pgname - program's name
370 * op - operation. "adding user", "changing finger info", "deleting group"
371 * name - user's account or group name. If not available use NULL.
372 * id - uid or gid that the operation is being performed on. This is used
373 * only when user is NULL.
374 * host - The hostname if known
375 * addr - The network address of the user
376 * tty - The tty of the user
377 * result - 1 is "success" and 0 is "failed"
379 * It returns the sequence number which is > 0 on success or <= 0 on error.
381 int audit_log_acct_message(int audit_fd, int type, const char *pgname,
382 const char *op, const char *name, unsigned int id,
383 const char *host, const char *addr, const char *tty, int result)
386 char buf[MAX_AUDIT_MESSAGE_LENGTH];
387 char addrbuf[INET6_ADDRSTRLEN];
388 static char exename[PATH_MAX*2] = "";
389 char ttyname[TTY_PATH];
400 /* If hostname is empty string, make it NULL ptr */
401 if (host && *host == 0)
404 if (addr == NULL || strlen(addr) == 0)
405 _resolve_addr(addrbuf, host);
407 strncat(addrbuf, addr, sizeof(addrbuf)-1);
409 if (pgname == NULL) {
411 _get_exename(exename, sizeof(exename));
412 } else if (pgname[0] != '"')
413 snprintf(exename, sizeof(exename), "\"%s\"", pgname);
415 snprintf(exename, sizeof(exename), "%s", pgname);
418 tty = _get_tty(ttyname, TTY_PATH);
422 if (name && id == -1) {
428 strncat(user, name, MAX_USER-1);
429 len = strnlen(user, UT_NAMESIZE);
431 if (audit_value_needs_encoding(name, len)) {
432 audit_encode_value(user, name, len);
434 "op=%s acct=%s exe=%s hostname=%s addr=%s terminal=%s res=%s";
437 "op=%s acct=\"%s\" exe=%s hostname=%s addr=%s terminal=%s res=%s";
439 snprintf(buf, sizeof(buf), format,
447 snprintf(buf, sizeof(buf),
448 "op=%s id=%u exe=%s hostname=%s addr=%s terminal=%s res=%s",
457 ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf);
458 if ((ret < 1) && errno == 0)
464 * This function will log a message to the audit system using a predefined
465 * message format. This function should be used by all apps that are SE Linux
468 * audit_fd - The fd returned by audit_open
469 * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN
470 * message - the message being sent
471 * hostname - the hostname if known
472 * addr - The network address of the user
473 * tty - The tty of the user
474 * uid - The auid of the person related to the avc message
476 * It returns the sequence number which is > 0 on success or <= 0 on error.
478 int audit_log_user_avc_message(int audit_fd, int type, const char *message,
479 const char *hostname, const char *addr, const char *tty, uid_t uid)
481 char buf[MAX_AUDIT_MESSAGE_LENGTH];
482 char addrbuf[INET6_ADDRSTRLEN];
483 static char exename[PATH_MAX*2] = "";
484 char ttyname[TTY_PATH];
490 /* If hostname is empty string, make it NULL ptr */
491 if (hostname && *hostname == 0)
494 if (addr == NULL || strlen(addr) == 0)
495 _resolve_addr(addrbuf, hostname);
497 strncat(addrbuf, addr, sizeof(addrbuf)-1);
499 _get_exename(exename, sizeof(exename));
501 tty = _get_tty(ttyname, TTY_PATH);
505 snprintf(buf, sizeof(buf),
506 "%s exe=%s sauid=%d hostname=%s addr=%s terminal=%s",
507 message, exename, uid,
508 hostname ? hostname : "?",
514 retval = audit_send_user_message( audit_fd, type, REAL_ERR, buf );
515 if (retval == -EPERM && getuid() != 0) {
516 syslog(LOG_ERR, "Can't send to audit system: %s %s",
517 audit_msg_type_to_name(type), buf);
520 if ((retval < 1) && errno == 0)
526 * This function will log a message to the audit system using a predefined
527 * message format. It should be used for all SE linux user and role
528 * manipulation operations.
529 * Parameter usage is as follows:
531 * type - type of message: AUDIT_ROLE_ASSIGN/REMOVE for changing any SE Linux
532 * user or role attributes.
533 * pgname - program's name
534 * op - operation. "adding-user", "adding-role", "deleting-user", "deleting-role"
535 * name - user's account. If not available use NULL.
536 * id - uid that the operation is being performed on. This is used
537 * only when name is NULL.
538 * new_seuser - the new seuser that the login user is getting
539 * new_role - the new_role that the login user is getting
540 * new_range - the new mls range that the login user is getting
541 * old_seuser - the old seuser that the login usr had
542 * old_role - the old role that the login user had
543 * old_range - the old mls range that the login usr had
544 * host - The hostname if known
545 * addr - The network address of the user
546 * tty - The tty of the user
547 * result - 1 is "success" and 0 is "failed"
549 * It returns the sequence number which is > 0 on success or <= 0 on error.
551 int audit_log_semanage_message(int audit_fd, int type, const char *pgname,
552 const char *op, const char *name, unsigned int id,
553 const char *new_seuser, const char *new_role, const char *new_range,
554 const char *old_seuser, const char *old_role, const char *old_range,
555 const char *host, const char *addr,
556 const char *tty, int result)
559 char buf[MAX_AUDIT_MESSAGE_LENGTH];
560 char addrbuf[INET6_ADDRSTRLEN];
561 static char exename[PATH_MAX*2] = "";
562 char ttyname[TTY_PATH];
573 /* If hostname is empty string, make it NULL ptr */
574 if (host && *host == 0)
577 if (addr == NULL || strlen(addr) == 0)
578 _resolve_addr(addrbuf, host);
580 strncat(addrbuf, addr, sizeof(addrbuf)-1);
582 if (pgname == NULL || strlen(pgname) == 0) {
584 _get_exename(exename, sizeof(exename));
587 if (tty == NULL || strlen(tty) == 0)
588 tty = _get_tty(ttyname, TTY_PATH);
592 if (name && strlen(name) > 0) {
598 strncat(user, name, MAX_USER-1);
599 len = strnlen(user, UT_NAMESIZE);
601 if (audit_value_needs_encoding(name, len)) {
602 audit_encode_value(user, name, len);
603 format = "op=%s acct=%s old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s";
605 format = "op=%s acct=\"%s\" old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s";
606 snprintf(buf, sizeof(buf), format, op, user,
607 old_seuser && strlen(old_seuser) ? old_seuser : "?",
608 old_role && strlen(old_role) ? old_role : "?",
609 old_range && strlen(old_range) ? old_range : "?",
610 new_seuser && strlen(new_seuser) ? new_seuser : "?",
611 new_role && strlen(new_role) ? new_role : "?",
612 new_range && strlen(new_range) ? new_range : "?",
614 host && strlen(host) ? host : "?",
616 tty && strlen(tty) ? tty : "?",
620 snprintf(buf, sizeof(buf),
621 "op=%s id=%u old-seuser=%s old-role=%s old-range=%s new-seuser=%s new-role=%s new-range=%s exe=%s hostname=%s addr=%s terminal=%s res=%s",
623 old_seuser && strlen(old_seuser) ? old_seuser : "?",
624 old_role && strlen(old_role) ? old_role : "?",
625 old_range && strlen(old_range) ? old_range : "?",
626 new_seuser && strlen(new_seuser) ? new_seuser : "?",
627 new_role && strlen(new_role) ? new_role : "?",
628 new_range && strlen(new_range) ? new_range : "?",
630 host && strlen(host) ? host : "?",
632 tty && strlen(tty) ? tty : "?",
637 ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf);
638 if ((ret < 1) && errno == 0)
644 * This function will log a message to the audit system using a predefined
645 * message format. This function should be used by all console apps that do
646 * not manipulate accounts or groups.
648 * audit_fd - The fd returned by audit_open
649 * type - type of message, ex: AUDIT_USER_CMD
650 * command - the command line being logged
651 * tty - The tty of the user
652 * result - 1 is "success" and 0 is "failed"
654 * It returns the sequence number which is > 0 on success or <= 0 on error.
656 int audit_log_user_command(int audit_fd, int type, const char *command,
657 const char *tty, int result)
660 char buf[MAX_AUDIT_MESSAGE_LENGTH];
661 char commname[PATH_MAX*2];
662 char cwdname[PATH_MAX*2];
663 char ttyname[TTY_PATH];
667 int ret, cwdenc=0, cmdenc=0;
679 tty = _get_tty(ttyname, TTY_PATH);
683 /* Trim leading spaces */
684 while (*command == ' ')
687 cmd = strdup(command);
691 // We borrow the commname buffer
692 if (getcwd(commname, PATH_MAX) == NULL)
693 strcpy(commname, "?");
694 len = strlen(commname);
695 if (audit_value_needs_encoding(commname, len)) {
696 audit_encode_value(cwdname, commname, len);
699 strcpy(cwdname, commname);
702 // Trim the trailing carriage return and spaces
703 while (len && (cmd[len-1] == 0x0A || cmd[len-1] == ' ')) {
708 if (len >= PATH_MAX) {
712 if (audit_value_needs_encoding(cmd, len)) {
713 audit_encode_value(commname, cmd, len);
717 strcpy(commname, cmd);
720 // Make the format string
722 p=stpcpy(format, "cwd=%s ");
724 p=stpcpy(format, "cwd=\"%s\" ");
727 p = stpcpy(p, "cmd=%s ");
729 p = stpcpy(p, "cmd=\"%s\" ");
731 strcpy(p, "terminal=%s res=%s");
733 // now use the format string to make the event
734 snprintf(buf, sizeof(buf), format,
741 ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf );
742 if ((ret < 1) && errno == 0)