From df5afa4fcd9725380f94ca6476248d4cc24f889a Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Sun, 29 Nov 2015 08:22:13 -0800 Subject: v2.4.4 audit sources Change-Id: I9315a7408817db51edf084fb4d27fbb492785084 Signed-off-by: Ashlee Young --- framework/src/audit/lib/audit_logging.c | 746 ++++++++++++++++++++++++++++++++ 1 file changed, 746 insertions(+) create mode 100644 framework/src/audit/lib/audit_logging.c (limited to 'framework/src/audit/lib/audit_logging.c') diff --git a/framework/src/audit/lib/audit_logging.c b/framework/src/audit/lib/audit_logging.c new file mode 100644 index 00000000..c9461061 --- /dev/null +++ b/framework/src/audit/lib/audit_logging.c @@ -0,0 +1,746 @@ +/* audit_logging.c -- + * Copyright 2005-2008,2010,2011,2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Steve Grubb + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include // inet6 addrlen +#include // gethostbyname +#include // inet_ntop +#include +#include // PATH_MAX + +#include "libaudit.h" +#include "private.h" + +#define TTY_PATH 32 +#define MAX_USER (UT_NAMESIZE * 2) + 8 + +// NOTE: The kernel fills in pid, uid, and loginuid of sender. Therefore, +// these routines do not need to send them. + +/* + * resolve's the hostname - caller must pass a INET6_ADDRSTRLEN byte buffer + * Returns string w/ numerical address, or "?" on failure + */ +static void _resolve_addr(char buf[], const char *host) +{ + struct addrinfo *ai; + struct addrinfo hints; + int e; + + buf[0] = '?'; + buf[1] = 0; + /* Short circuit this lookup if NULL, or empty */ + if (host == NULL || *host == 0) + return; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + hints.ai_socktype = SOCK_STREAM; + + e = getaddrinfo(host, NULL, &hints, &ai); + if (e != 0) { + audit_msg(LOG_ERR, + "resolve_addr: cannot resolve hostname %s (%s)", + host, gai_strerror(e)); + return; + } + // What to do if more than 1 addr? + inet_ntop(ai->ai_family, ai->ai_family == AF_INET ? + (void *) &((struct sockaddr_in *)ai->ai_addr)->sin_addr : + (void *) &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr, + buf, INET6_ADDRSTRLEN); + freeaddrinfo(ai); +} + +/* + * This function checks a string to see if it needs encoding. It + * return 1 if needed and 0 if not + */ +int audit_value_needs_encoding(const char *str, unsigned int size) +{ + unsigned int i; + + if (str == NULL) + return 0; + + for (i=0; i 0x7f because str[] is signed. + if (str[i] == '"' || str[i] < 0x21 || str[i] == 0x7F) + return 1; + } + return 0; +} + +/* + * This function does encoding of "untrusted" names just like the kernel + */ +char *audit_encode_value(char *final, const char *buf, unsigned int size) +{ + unsigned int i; + char *ptr = final; + const char *hex = "0123456789ABCDEF"; + + if (final == NULL) + return NULL; + + if (buf == NULL) { + *final = 0; + return final; + } + + for (i=0; i>4]; /* Upper nibble */ + *ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */ + } + *ptr = 0; + return final; +} + +char *audit_encode_nv_string(const char *name, const char *value, + unsigned int vlen) +{ + char *str; + + if (vlen == 0 && value) + vlen = strlen(value); + + if (value && audit_value_needs_encoding(value, vlen)) { + char *tmp = malloc(2*vlen + 1); + if (tmp) { + audit_encode_value(tmp, value, vlen); + if (asprintf(&str, "%s=%s", name, tmp) < 0) + str = NULL; + free(tmp); + } else + str = NULL; + } else + if (asprintf(&str, "%s=\"%s\"", name, value ? value : "?") < 0) + str = NULL; + return str; +} + +/* + * Get the executable's name + */ +static char *_get_exename(char *exename, int size) +{ + int res; + char tmp[PATH_MAX+1]; + + /* get the name of the current executable */ + if ((res = readlink("/proc/self/exe", tmp, PATH_MAX)) == -1) { + strcpy(exename, "\"?\""); + audit_msg(LOG_ERR, "get_exename: cannot determine executable"); + } else { + tmp[res] = '\0'; + if (audit_value_needs_encoding(tmp, res)) + return audit_encode_value(exename, tmp, res); + snprintf(exename, size, "\"%s\"", tmp); + } + return exename; +} + +/* + * Get the command line name + * NOTE: at the moment, this only escapes what the user sent + */ +static char *_get_commname(const char *comm, char *commname, unsigned int size) +{ + unsigned int len; + + if (comm == NULL) { + strcpy(commname, "\"?\""); + return commname; + } + + len = strlen(comm); + if (audit_value_needs_encoding(comm, len)) + audit_encode_value(commname, comm, len); + else + snprintf(commname, size, "\"%s\"", comm); + + return commname; +} + +static int check_ttyname(const char *ttyn) +{ + struct stat statbuf; + + if (lstat(ttyn, &statbuf) + || !S_ISCHR(statbuf.st_mode) + || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5))) { + audit_msg(LOG_ERR, "FATAL: bad tty %s", ttyn); + return 1; + } + return 0; +} + +static const char *_get_tty(char *tname, int size) +{ + int rc, i, found = 0; + + for (i=0; i<3 && !found; i++) { + rc = ttyname_r(i, tname, size); + if (rc == 0 && tname[0] != '\0') + found = 1; + } + + if (!found) + return NULL; + + if (check_ttyname(tname)) + return NULL; + + if (strncmp(tname, "/dev/", 5) == 0) + return &tname[5]; + + return tname; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_message(int audit_fd, int type, const char *message, + const char *hostname, const char *addr, const char *tty, int result) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2]=""; + char ttyname[TTY_PATH]; + const char *success; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + snprintf(buf, sizeof(buf), + "%s exe=%s hostname=%s addr=%s terminal=%s res=%s", + message, exename, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups and are executing a script. An example + * would be python or crond wanting to say what they are executing. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * comm - the program command line name + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_comm_message(int audit_fd, int type, const char *message, + const char *comm, const char *hostname, const char *addr, + const char *tty, int result) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2]=""; + char commname[PATH_MAX*2]; + char ttyname[TTY_PATH]; + const char *success; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + _get_commname(comm, commname, sizeof(commname)); + + snprintf(buf, sizeof(buf), + "%s comm=%s exe=%s hostname=%s addr=%s terminal=%s res=%s", + message, commname, exename, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + + +/* + * This function will log a message to the audit system using a predefined + * message format. It should be used for all account manipulation operations. + * Parameter usage is as follows: + * + * audit_fd - The fd returned by audit_open + * type - type of message: AUDIT_USER_CHAUTHTOK for changing any account + * attributes. + * pgname - program's name + * op - operation. "adding user", "changing finger info", "deleting group" + * name - user's account or group name. If not available use NULL. + * id - uid or gid that the operation is being performed on. This is used + * only when user is NULL. + * host - The hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_acct_message(int audit_fd, int type, const char *pgname, + const char *op, const char *name, unsigned int id, + const char *host, const char *addr, const char *tty, int result) +{ + const char *success; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (host && *host == 0) + host = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, host); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (pgname == NULL) { + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + } else if (pgname[0] != '"') + snprintf(exename, sizeof(exename), "\"%s\"", pgname); + else + snprintf(exename, sizeof(exename), "%s", pgname); + + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + if (name && id == -1) { + char user[MAX_USER]; + const char *format; + size_t len; + + user[0] = 0; + strncat(user, name, MAX_USER-1); + len = strnlen(user, UT_NAMESIZE); + user[len] = 0; + if (audit_value_needs_encoding(name, len)) { + audit_encode_value(user, name, len); + format = + "op=%s acct=%s exe=%s hostname=%s addr=%s terminal=%s res=%s"; + } else + format = + "op=%s acct=\"%s\" exe=%s hostname=%s addr=%s terminal=%s res=%s"; + + snprintf(buf, sizeof(buf), format, + op, user, exename, + host ? host : "?", + addrbuf, + tty ? tty : "?", + success + ); + } else + snprintf(buf, sizeof(buf), + "op=%s id=%u exe=%s hostname=%s addr=%s terminal=%s res=%s", + op, id, exename, + host ? host : "?", + addrbuf, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all apps that are SE Linux + * object managers. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER, AUDIT_USYS_CONFIG, AUDIT_USER_LOGIN + * message - the message being sent + * hostname - the hostname if known + * addr - The network address of the user + * tty - The tty of the user + * uid - The auid of the person related to the avc message + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_avc_message(int audit_fd, int type, const char *message, + const char *hostname, const char *addr, const char *tty, uid_t uid) +{ + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int retval; + + if (audit_fd < 0) + return 0; + + /* If hostname is empty string, make it NULL ptr */ + if (hostname && *hostname == 0) + hostname = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, hostname); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + snprintf(buf, sizeof(buf), + "%s exe=%s sauid=%d hostname=%s addr=%s terminal=%s", + message, exename, uid, + hostname ? hostname : "?", + addrbuf, + tty ? tty : "?" + ); + + errno = 0; + retval = audit_send_user_message( audit_fd, type, REAL_ERR, buf ); + if (retval == -EPERM && getuid() != 0) { + syslog(LOG_ERR, "Can't send to audit system: %s %s", + audit_msg_type_to_name(type), buf); + return 0; + } + if ((retval < 1) && errno == 0) + errno = retval; + return retval; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. It should be used for all SE linux user and role + * manipulation operations. + * Parameter usage is as follows: + * + * type - type of message: AUDIT_ROLE_ASSIGN/REMOVE for changing any SE Linux + * user or role attributes. + * pgname - program's name + * op - operation. "adding-user", "adding-role", "deleting-user", "deleting-role" + * name - user's account. If not available use NULL. + * id - uid that the operation is being performed on. This is used + * only when name is NULL. + * new_seuser - the new seuser that the login user is getting + * new_role - the new_role that the login user is getting + * new_range - the new mls range that the login user is getting + * old_seuser - the old seuser that the login usr had + * old_role - the old role that the login user had + * old_range - the old mls range that the login usr had + * host - The hostname if known + * addr - The network address of the user + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_semanage_message(int audit_fd, int type, const char *pgname, + const char *op, const char *name, unsigned int id, + const char *new_seuser, const char *new_role, const char *new_range, + const char *old_seuser, const char *old_role, const char *old_range, + const char *host, const char *addr, + const char *tty, int result) +{ + const char *success; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char addrbuf[INET6_ADDRSTRLEN]; + static char exename[PATH_MAX*2] = ""; + char ttyname[TTY_PATH]; + int ret; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + /* If hostname is empty string, make it NULL ptr */ + if (host && *host == 0) + host = NULL; + addrbuf[0] = 0; + if (addr == NULL || strlen(addr) == 0) + _resolve_addr(addrbuf, host); + else + strncat(addrbuf, addr, sizeof(addrbuf)-1); + + if (pgname == NULL || strlen(pgname) == 0) { + if (exename[0] == 0) + _get_exename(exename, sizeof(exename)); + pgname = exename; + } + if (tty == NULL || strlen(tty) == 0) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + if (name && strlen(name) > 0) { + size_t len; + const char *format; + char user[MAX_USER]; + + user[0] = 0; + strncat(user, name, MAX_USER-1); + len = strnlen(user, UT_NAMESIZE); + user[len] = 0; + if (audit_value_needs_encoding(name, len)) { + audit_encode_value(user, name, len); + 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"; + } else + 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"; + snprintf(buf, sizeof(buf), format, op, user, + old_seuser && strlen(old_seuser) ? old_seuser : "?", + old_role && strlen(old_role) ? old_role : "?", + old_range && strlen(old_range) ? old_range : "?", + new_seuser && strlen(new_seuser) ? new_seuser : "?", + new_role && strlen(new_role) ? new_role : "?", + new_range && strlen(new_range) ? new_range : "?", + pgname, + host && strlen(host) ? host : "?", + addrbuf, + tty && strlen(tty) ? tty : "?", + success + ); + } else + snprintf(buf, sizeof(buf), + "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", + op, id, + old_seuser && strlen(old_seuser) ? old_seuser : "?", + old_role && strlen(old_role) ? old_role : "?", + old_range && strlen(old_range) ? old_range : "?", + new_seuser && strlen(new_seuser) ? new_seuser : "?", + new_role && strlen(new_role) ? new_role : "?", + new_range && strlen(new_range) ? new_range : "?", + pgname, + host && strlen(host) ? host : "?", + addrbuf, + tty && strlen(tty) ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message(audit_fd, type, REAL_ERR, buf); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + +/* + * This function will log a message to the audit system using a predefined + * message format. This function should be used by all console apps that do + * not manipulate accounts or groups. + * + * audit_fd - The fd returned by audit_open + * type - type of message, ex: AUDIT_USER_CMD + * command - the command line being logged + * tty - The tty of the user + * result - 1 is "success" and 0 is "failed" + * + * It returns the sequence number which is > 0 on success or <= 0 on error. + */ +int audit_log_user_command(int audit_fd, int type, const char *command, + const char *tty, int result) +{ + char *p; + char buf[MAX_AUDIT_MESSAGE_LENGTH]; + char commname[PATH_MAX*2]; + char cwdname[PATH_MAX*2]; + char ttyname[TTY_PATH]; + char format[64]; + const char *success; + char *cmd; + int ret, cwdenc=0, cmdenc=0; + unsigned int len; + + if (audit_fd < 0) + return 0; + + if (result) + success = "success"; + else + success = "failed"; + + if (tty == NULL) + tty = _get_tty(ttyname, TTY_PATH); + else if (*tty == 0) + tty = NULL; + + /* Trim leading spaces */ + while (*command == ' ') + command++; + + cmd = strdup(command); + if (cmd == NULL) + return -1; + + // We borrow the commname buffer + if (getcwd(commname, PATH_MAX) == NULL) + strcpy(commname, "?"); + len = strlen(commname); + if (audit_value_needs_encoding(commname, len)) { + audit_encode_value(cwdname, commname, len); + cwdenc = 1; + } else + strcpy(cwdname, commname); + + len = strlen(cmd); + // Trim the trailing carriage return and spaces + while (len && (cmd[len-1] == 0x0A || cmd[len-1] == ' ')) { + cmd[len-1] = 0; + len--; + } + + if (len >= PATH_MAX) { + cmd[PATH_MAX] = 0; + len = PATH_MAX-1; + } + if (audit_value_needs_encoding(cmd, len)) { + audit_encode_value(commname, cmd, len); + cmdenc = 1; + } + if (cmdenc == 0) + strcpy(commname, cmd); + free(cmd); + + // Make the format string + if (cwdenc) + p=stpcpy(format, "cwd=%s "); + else + p=stpcpy(format, "cwd=\"%s\" "); + + if (cmdenc) + p = stpcpy(p, "cmd=%s "); + else + p = stpcpy(p, "cmd=\"%s\" "); + + strcpy(p, "terminal=%s res=%s"); + + // now use the format string to make the event + snprintf(buf, sizeof(buf), format, + cwdname, commname, + tty ? tty : "?", + success + ); + + errno = 0; + ret = audit_send_user_message( audit_fd, type, HIDE_IT, buf ); + if ((ret < 1) && errno == 0) + errno = ret; + return ret; +} + -- cgit 1.2.3-korg