/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
lib/audit_logging.c
857 строк
22 KB
Steve Grubb
libaudit: bound plain comm formatting
24 июл 2026, 23:26
24 июл 2026, 23:26
3c46ca8
Код
Авторство
О чём код?
/* audit_logging.c -- * Copyright 2005-2008,2010,2011,2013,2017 Red Hat Inc. * 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 <sgrubb@redhat.com> */ #include "config.h" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <errno.h> #include <netinet/in.h> // inet6 addrlen #include <netdb.h> // gethostbyname #include <arpa/inet.h> // inet_ntop #include <utmp.h> #include <limits.h> // PATH_MAX #include <fcntl.h> #include "libaudit.h" #include "private.h" #define TTY_PATH 32 #define MAX_USER ((UT_NAMESIZE * 2) + 8) #define EXENAME_SIZE ((PATH_MAX * 2) + 1) /* Kernel TASK_COMM_LEN includes the terminating NUL. */ #define AUDIT_COMM_LEN 16 #define AUDIT_COMM_ENCODED_LEN (((AUDIT_COMM_LEN - 1) * 2) + 1) // 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; unsigned char ch; if (str == NULL) return 0; for (i=0; i<size; i++) { // Encoding should not depend on whether plain char is signed. ch = (unsigned char)str[i]; if (ch == '"' || ch < 0x21 || ch > 0x7E) 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<size; i++) { *ptr++ = hex[(buf[i] & 0xF0)>>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) { size_t nlen, len; char *str, *ptr; int encode = 0; if (value == NULL) { value = "?"; vlen = 1; } if (vlen == 0) vlen = strlen(value); nlen = strlen(name); if (audit_value_needs_encoding(value, vlen)) { encode = 1; // name + '=' + 2*value len + termination len = nlen + 1 + (2 * vlen) + 1; } else // 2 double quotes + name + '=' + value + termination len = 2 + nlen + 1 + vlen + 1; str = malloc(len); if (str == NULL) return NULL; ptr = stpcpy(str, name); *ptr++ = '='; if (encode) audit_encode_value(ptr, value, vlen); else { *ptr++ = '"'; memcpy(ptr, value, vlen); ptr += vlen; *ptr++ = '"'; *ptr = '\0'; } 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)) { if (((res * 2) + 1) > size) { strcpy(exename, "\"?\""); audit_msg(LOG_ERR, "get_exename: encoded executable name too long"); return exename; } return audit_encode_value(exename, tmp, res); } snprintf(exename, size, "\"%s\"", tmp); } return exename; } /* * Get the command line name which the kernel's limit is 15 chars + NUL. */ static char *_get_commname(const char *comm, char *commname, unsigned int size) __attr_access ((__read_only__, 1)) __attr_access ((__write_only__, 2, 3)); static char *_get_commname(const char *comm, char *commname, unsigned int size) { unsigned int len; char tmp_comm[AUDIT_COMM_LEN]; if (comm == NULL) { ssize_t ret; int fd = open("/proc/self/comm", O_RDONLY|O_CLOEXEC); if (fd < 0) { strcpy(commname, "\"?\""); return commname; } ret = read(fd, tmp_comm, sizeof(tmp_comm)); close(fd); if (ret > 0) tmp_comm[ret-1] = 0; else { strcpy(commname, "\"?\""); return commname; } comm = tmp_comm; } len = strnlen(comm, AUDIT_COMM_LEN); if (len == AUDIT_COMM_LEN) { audit_msg(LOG_WARNING, "comm value truncated to %d bytes", AUDIT_COMM_LEN - 1); len = AUDIT_COMM_LEN - 1; } if (audit_value_needs_encoding(comm, len)) audit_encode_value(commname, comm, len); else snprintf(commname, size, "\"%.*s\"", (int)len, 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; } #define HOSTLEN 64 static char _host[HOSTLEN] = ""; static const char *_get_hostname(const char *ttyn) { if (ttyn && ((strncmp(ttyn, "pts", 3) == 0) || (strncmp(ttyn, "tty", 3) == 0) || (strncmp(ttyn, "/dev/tty", 8) == 0) || (strncmp(ttyn, "/dev/pts", 8) == 0) )) { if (_host[0] == 0) { gethostname(_host, HOSTLEN); _host[HOSTLEN - 1] = 0; } return _host; } return NULL; } /* * 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[EXENAME_SIZE]=""; 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; /* See if we can deduce addr */ addrbuf[0] = 0; if (addr == NULL || strlen(addr) == 0) _resolve_addr(addrbuf, hostname); else strncat(addrbuf, addr, sizeof(addrbuf)-1); /* Fill in exec name if needed */ if (exename[0] == 0) _get_exename(exename, sizeof(exename)); /* Fill in tty if needed */ if (tty == NULL) tty = _get_tty(ttyname, TTY_PATH); else if (*tty == 0) tty = NULL; /* Get the local name if we have a real tty */ if (hostname == NULL && tty) hostname = _get_hostname(tty); 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 task name from /proc/self/comm, 15 bytes maximum * 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[EXENAME_SIZE]=""; char commname[AUDIT_COMM_ENCODED_LEN]; 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; /* See if we can deduce addr */ addrbuf[0] = 0; if (addr == NULL || strlen(addr) == 0) _resolve_addr(addrbuf, hostname); else strncat(addrbuf, addr, sizeof(addrbuf)-1); /* Fill in exec name if needed */ if (exename[0] == 0) _get_exename(exename, sizeof(exename)); /* Fill in tty if needed */ if (tty == NULL) tty = _get_tty(ttyname, TTY_PATH); else if (*tty == 0) tty = NULL; if (_get_commname(comm, commname, sizeof(commname)) == NULL) return -1; /* Get the local name if we have a real tty */ if (hostname == NULL && tty) hostname = _get_hostname(tty); 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[EXENAME_SIZE] = ""; 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; /* See if we can deduce addr */ addrbuf[0] = 0; if (addr == NULL || strlen(addr) == 0) _resolve_addr(addrbuf, host); else strncat(addrbuf, addr, sizeof(addrbuf)-1); /* Fill in exec name if needed */ 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); /* Fill in tty if needed */ if (tty == NULL) tty = _get_tty(ttyname, TTY_PATH); else if (*tty == 0) tty = NULL; /* Get the local name if we have a real tty */ if (host == NULL && tty) host = _get_hostname(tty); if (name && id == (unsigned int)-1) { char user[MAX_USER]; int encoded = 0; 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); encoded = 1; } snprintf(buf, sizeof(buf), encoded ? "op=%s acct=%s exe=%s hostname=%s addr=%s terminal=%s res=%s" : "op=%s acct=\"%s\" exe=%s hostname=%s addr=%s terminal=%s res=%s", 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 * auid - 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 auid) { char buf[MAX_AUDIT_MESSAGE_LENGTH]; char addrbuf[INET6_ADDRSTRLEN]; static char exename[EXENAME_SIZE] = ""; 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, auid, hostname ? hostname : "?", addrbuf, tty ? tty : "?" ); errno = 0; retval = audit_send_user_message( audit_fd, type, REAL_ERR, buf ); if (retval == -EPERM && !audit_can_write()) { 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[EXENAME_SIZE] = ""; 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; int encoded = 0; 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); encoded = 1; } snprintf(buf, sizeof(buf), encoded ? "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" : "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", 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]; static char exename[EXENAME_SIZE] = ""; 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; if (exename[0] == 0) _get_exename(exename, sizeof(exename)); /* 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, "exe=%s terminal=%s res=%s"); // now use the format string to make the event snprintf(buf, sizeof(buf), format, cwdname, commname, exename, 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; }