Logo Search packages:      
Sourcecode: audit version File versions

audit_logging.c

/* audit_logging.c -- 
 * Copyright 2005-2008 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 <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 "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)
{
      int i;

      for (i=0; i<size; i++) {
            // we don't test for > 0x7f because str[] is signed
            // and str[i] < 0x21 covers it.
            if (str[i] == '"' || str[i] < 0x21)
                  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";

      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)
{
      char *str;

      if (vlen == 0)
            vlen = strlen(value);

      if (audit_value_needs_encoding(value, vlen)) {
            char *tmp = malloc(2*vlen + 1);
            audit_encode_value(tmp, value, vlen);
            asprintf(&str, "%s=%s", name, tmp);
            free(tmp);
      } else
            asprintf(&str, "%s=\"%s\"", name, value);
      return str;
}

/*
 * Get the executable's name 
 */
static char *_get_exename(char *exename, int size)
{
      int res;
      char tmp[PATH_MAX];

      /* get the name of the current executable */
      if ((res = readlink("/proc/self/exe", tmp, PATH_MAX - 1)) == -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];
      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";

      addrbuf[0] = 0;
      if (addr == NULL || strlen(addr) == 0)
            _resolve_addr(addrbuf, hostname);
      else
            strncat(addrbuf, addr, sizeof(addrbuf)-1);

      _get_exename(exename, sizeof(exename));
      if (tty == NULL) 
            tty = _get_tty(ttyname, TTY_PATH);

      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];
      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";

      addrbuf[0] = 0;
      if (addr == NULL || strlen(addr) == 0)
            _resolve_addr(addrbuf, hostname);
      else
            strncat(addrbuf, addr, sizeof(addrbuf)-1);
      _get_exename(exename, sizeof(exename));
      if (tty == NULL) 
            tty = _get_tty(ttyname, TTY_PATH);

      _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];
      char exename[PATH_MAX*2];
      char ttyname[TTY_PATH];
      int ret;

      if (audit_fd < 0)
            return 0;

      if (result)
            success = "success";
      else
            success = "failed";

      addrbuf[0] = 0;
      if (addr == NULL || strlen(addr) == 0)
            _resolve_addr(addrbuf, host);
      else
            strncat(addrbuf, addr, sizeof(addrbuf)-1);

      if (pgname == NULL) {
            _get_exename(exename, sizeof(exename));
            pgname = exename;
      }
      if (tty == NULL) 
            tty = _get_tty(ttyname, TTY_PATH);

      if (name) {
            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, pgname,
                  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, pgname,
                  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];
      char exename[PATH_MAX*2];
      char ttyname[TTY_PATH];
      int retval;

      if (audit_fd < 0)
            return 0;

      addrbuf[0] = 0;
      if (addr == NULL || strlen(addr) == 0)
            _resolve_addr(addrbuf, hostname);
      else
            strncat(addrbuf, addr, sizeof(addrbuf)-1);
      _get_exename(exename, sizeof(exename));
      if (tty == NULL) 
            tty = _get_tty(ttyname, TTY_PATH);

      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_USER_ROLE_CHANGE for changing any SE Linux
 *        user or role 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.
 * 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];
      char exename[PATH_MAX*2];
      char ttyname[TTY_PATH];
      int ret;

      if (audit_fd < 0)
            return 0;

      if (result)
            success = "success";
      else
            success = "failed";

      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) {
            _get_exename(exename, sizeof(exename));
            pgname = exename;
      }
      if (tty == NULL || strlen(tty) == 0) 
            tty = _get_tty(ttyname, TTY_PATH);

      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);

      /* 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;
}


Generated by  Doxygen 1.6.0   Back to index